View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.filter.ssl;
21  
22  import java.net.InetSocketAddress;
23  import java.nio.ByteBuffer;
24  import java.util.Queue;
25  import java.util.concurrent.ConcurrentLinkedQueue;
26  import java.util.concurrent.locks.Lock;
27  import java.util.concurrent.locks.ReentrantLock;
28  
29  import javax.net.ssl.SSLEngine;
30  import javax.net.ssl.SSLEngineResult;
31  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
32  import javax.net.ssl.SSLEngineResult.Status;
33  import javax.net.ssl.SSLException;
34  import javax.net.ssl.SSLHandshakeException;
35  
36  import org.apache.mina.core.buffer.IoBuffer;
37  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
38  import org.apache.mina.core.filterchain.IoFilterEvent;
39  import org.apache.mina.core.future.DefaultWriteFuture;
40  import org.apache.mina.core.future.WriteFuture;
41  import org.apache.mina.core.session.IoEventType;
42  import org.apache.mina.core.session.IoSession;
43  import org.apache.mina.core.write.DefaultWriteRequest;
44  import org.apache.mina.core.write.WriteRequest;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  /**
49   * A helper class using the SSLEngine API to decrypt/encrypt data.
50   * <p/>
51   * Each connection has a SSLEngine that is used through the lifetime of the connection.
52   * We allocate buffers for use as the outbound and inbound network buffers.
53   * These buffers handle all of the intermediary data for the SSL connection. To make things easy,
54   * we'll require outNetBuffer be completely flushed before trying to wrap any more data.
55   * <p/>
56   * This class is not to be used by any client, it's closely associated with the SSL Filter.
57   * None of its methods are public as they should not be used by any other class but from
58   * the SslFilter class, in the same package
59   *
60   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
61   */
62  /** No qualifier*/
63  class SslHandler {
64      /** A logger for this class */
65      private final static Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
66  
67      /** The SSL Filter which has created this handler */
68      private final SslFilter sslFilter;
69  
70      /** The current session */
71      private final IoSession session;
72  
73      private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
74  
75      private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
76  
77      /** A queue used to stack all the incoming data until the SSL session is established */
78      private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
79  
80      private SSLEngine sslEngine;
81  
82      /**
83       * Encrypted data from the net
84       */
85      private IoBuffer inNetBuffer;
86  
87      /**
88       * Encrypted data to be written to the net
89       */
90      private IoBuffer outNetBuffer;
91  
92      /**
93       * Application cleartext data to be read by application
94       */
95      private IoBuffer appBuffer;
96  
97      /**
98       * Empty buffer used during initial handshake and close operations
99       */
100     private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
101 
102     private SSLEngineResult.HandshakeStatus handshakeStatus;
103 
104     /**
105      * A flag set to true when the first SSL handshake has been completed
106      * This is used to avoid sending a notification to the application handler
107      * when we switch to a SECURE or UNSECURE session.
108      */
109     private boolean firstSSLNegociation;
110 
111     /** A flag set to true when a SSL Handshake has been completed */
112     private boolean handshakeComplete;
113 
114     /** A flag used to indicate to the SslFilter that the buffer
115      * it will write is already encrypted (this will be the case
116      * for data being produced during the handshake). */
117     private boolean writingEncryptedData;
118 
119     private Lock sslLock = new ReentrantLock();
120 
121     /**
122      * Create a new SSL Handler, and initialize it.
123      *
124      * @param sslContext
125      * @throws SSLException
126      */
127     /* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) throws SSLException {
128         this.sslFilter = sslFilter;
129         this.session = session;
130     }
131 
132     /**
133      * Initialize the SSL handshake.
134      *
135      * @throws SSLException If the underlying SSLEngine handshake initialization failed
136      */
137     /* no qualifier */void init() throws SSLException {
138         if (sslEngine != null) {
139             // We already have a SSL engine created, no need to create a new one
140             return;
141         }
142 
143         LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
144 
145         InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
146 
147         // Create the SSL engine here
148         if (peer == null) {
149             sslEngine = sslFilter.sslContext.createSSLEngine();
150         } else {
151             sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
152         }
153 
154         // Initialize the engine in client mode if necessary
155         sslEngine.setUseClientMode(sslFilter.isUseClientMode());
156 
157         // Initialize the different SslEngine modes
158         if (!sslEngine.getUseClientMode()) {
159             // Those parameters are only valid when in server mode
160             if (sslFilter.isWantClientAuth()) {
161                 sslEngine.setWantClientAuth(true);
162             }
163 
164             if (sslFilter.isNeedClientAuth()) {
165                 sslEngine.setNeedClientAuth(true);
166             }
167         }
168 
169         // Set the cipher suite to use by this SslEngine instance
170         if (sslFilter.getEnabledCipherSuites() != null) {
171             sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
172         }
173 
174         // Set the list of enabled protocols
175         if (sslFilter.getEnabledProtocols() != null) {
176             sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
177         }
178 
179         // TODO : we may not need to call this method...
180         // However, if we don't call it here, the tests are failing. Why?
181         sslEngine.beginHandshake();
182 
183         handshakeStatus = sslEngine.getHandshakeStatus();
184 
185         // Default value
186         writingEncryptedData = false;
187 
188         // We haven't yet started a SSL negotiation
189         // set the flags accordingly
190         firstSSLNegociation = true;
191         handshakeComplete = false;
192 
193         if (LOGGER.isDebugEnabled()) {
194             LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
195         }
196     }
197 
198     /**
199      * Release allocated buffers.
200      */
201     /* no qualifier */void destroy() {
202         if (sslEngine == null) {
203             return;
204         }
205 
206         // Close inbound and flush all remaining data if available.
207         try {
208             sslEngine.closeInbound();
209         } catch (SSLException e) {
210             LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
211         }
212 
213         if (outNetBuffer != null) {
214             outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
215         } else {
216             createOutNetBuffer(0);
217         }
218         try {
219             do {
220                 outNetBuffer.clear();
221             } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
222         } catch (SSLException e) {
223             // Ignore.
224         } finally {
225             outNetBuffer.free();
226             outNetBuffer = null;
227         }
228 
229         sslEngine.closeOutbound();
230         sslEngine = null;
231 
232         preHandshakeEventQueue.clear();
233     }
234 
235     /**
236      * @return The SSL filter which has created this handler
237      */
238     /* no qualifier */SslFilter getSslFilter() {
239         return sslFilter;
240     }
241 
242     /* no qualifier */IoSession getSession() {
243         return session;
244     }
245 
246     /**
247      * Check if we are writing encrypted data.
248      */
249     /* no qualifier */boolean isWritingEncryptedData() {
250         return writingEncryptedData;
251     }
252 
253     /**
254      * Check if handshake is completed.
255      */
256     /* no qualifier */boolean isHandshakeComplete() {
257         return handshakeComplete;
258     }
259 
260     /* no qualifier */boolean isInboundDone() {
261         return sslEngine == null || sslEngine.isInboundDone();
262     }
263 
264     /* no qualifier */boolean isOutboundDone() {
265         return sslEngine == null || sslEngine.isOutboundDone();
266     }
267 
268     /**
269      * Check if there is any need to complete handshake.
270      */
271     /* no qualifier */boolean needToCompleteHandshake() {
272         return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
273     }
274 
275     /* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
276         preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
277     }
278 
279     /* no qualifier */void flushPreHandshakeEvents() throws SSLException {
280         IoFilterEvent scheduledWrite;
281 
282         while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
283             sslFilter
284             .filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter());
285         }
286     }
287 
288     /* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
289         filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
290     }
291 
292     /**
293      * Push the newly received data into a queue, waiting for the SSL session
294      * to be fully established
295      *
296      * @param nextFilter The next filter to call
297      * @param message The incoming data
298      */
299     /* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
300         messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
301     }
302 
303     /* no qualifier */void flushScheduledEvents() {
304         // Fire events only when no lock is hold for this handler.
305         if (Thread.holdsLock(this)) {
306             return;
307         }
308 
309         IoFilterEvent event;
310 
311         // We need synchronization here inevitably because filterWrite can be
312         // called simultaneously and cause 'bad record MAC' integrity error.
313         sslLock.lock();
314 
315         try {
316             while ((event = filterWriteEventQueue.poll()) != null) {
317                 NextFilter nextFilter = event.getNextFilter();
318                 nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
319             }
320 
321             while ((event = messageReceivedEventQueue.poll()) != null) {
322                 NextFilter nextFilter = event.getNextFilter();
323                 nextFilter.messageReceived(session, event.getParameter());
324             }
325         } finally {
326             sslLock.unlock();
327         }
328     }
329 
330     /**
331      * Call when data are read from net. It will perform the initial hanshake or decrypt
332      * the data if SSL has been initialiaed.
333      * 
334      * @param buf buffer to decrypt
335      * @param nextFilter Next filter in chain
336      * @throws SSLException on errors
337      */
338     /* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
339         if (LOGGER.isDebugEnabled()) {
340             if (!isOutboundDone()) {
341                 LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
342             } else {
343                 LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
344             }
345         }
346 
347         // append buf to inNetBuffer
348         if (inNetBuffer == null) {
349             inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
350         }
351 
352         inNetBuffer.put(buf);
353 
354         if (!handshakeComplete) {
355             handshake(nextFilter);
356         } else {
357             // Prepare the net data for reading.
358             inNetBuffer.flip();
359 
360             if (!inNetBuffer.hasRemaining()) {
361                 return;
362             }
363 
364             SSLEngineResult res = unwrap();
365 
366             // prepare to be written again
367             if (inNetBuffer.hasRemaining()) {
368                 inNetBuffer.compact();
369             } else {
370                 inNetBuffer.free();
371                 inNetBuffer = null;
372             }
373 
374             checkStatus(res);
375 
376             renegotiateIfNeeded(nextFilter, res);
377         }
378 
379         if (isInboundDone()) {
380             // Rewind the MINA buffer if not all data is processed and inbound
381             // is finished.
382             int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
383             buf.position(buf.position() - inNetBufferPosition);
384 
385             if (inNetBuffer != null) {
386                 inNetBuffer.free();
387                 inNetBuffer = null;
388             }
389         }
390     }
391 
392     /**
393      * Get decrypted application data.
394      * 
395      * @return buffer with data
396      */
397     /* no qualifier */IoBuffer fetchAppBuffer() {
398         if (this.appBuffer == null) {
399             return IoBuffer.allocate(0);
400         } else {
401             IoBuffer appBuffer = this.appBuffer.flip();
402             this.appBuffer = null;
403 
404             return appBuffer;
405         }
406     }
407 
408     /**
409      * Get encrypted data to be sent.
410      * 
411      * @return buffer with data
412      */
413     /* no qualifier */IoBuffer fetchOutNetBuffer() {
414         IoBuffer answer = outNetBuffer;
415         if (answer == null) {
416             return emptyBuffer;
417         }
418 
419         outNetBuffer = null;
420         return answer.shrink();
421     }
422 
423     /**
424      * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
425      * 
426      * @param src
427      *            data to encrypt
428      * @throws SSLException
429      *             on errors
430      */
431     /* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
432         if (!handshakeComplete) {
433             throw new IllegalStateException();
434         }
435 
436         if (!src.hasRemaining()) {
437             if (outNetBuffer == null) {
438                 outNetBuffer = emptyBuffer;
439             }
440             return;
441         }
442 
443         createOutNetBuffer(src.remaining());
444 
445         // Loop until there is no more data in src
446         while (src.hasRemaining()) {
447 
448             SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
449             if (result.getStatus() == SSLEngineResult.Status.OK) {
450                 if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
451                     doTasks();
452                 }
453             } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
454                 outNetBuffer.capacity(outNetBuffer.capacity() << 1);
455                 outNetBuffer.limit(outNetBuffer.capacity());
456             } else {
457                 throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
458                         + "outNetBuffer: " + outNetBuffer);
459             }
460         }
461 
462         outNetBuffer.flip();
463     }
464 
465     /**
466      * Start SSL shutdown process.
467      * 
468      * @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
469      *         shutdown process is already finished.
470      * @throws SSLException
471      *             on errors
472      */
473     /* no qualifier */boolean closeOutbound() throws SSLException {
474         if (sslEngine == null || sslEngine.isOutboundDone()) {
475             return false;
476         }
477 
478         sslEngine.closeOutbound();
479 
480         createOutNetBuffer(0);
481         SSLEngineResult result;
482 
483         for (;;) {
484             result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
485             if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
486                 outNetBuffer.capacity(outNetBuffer.capacity() << 1);
487                 outNetBuffer.limit(outNetBuffer.capacity());
488             } else {
489                 break;
490             }
491         }
492 
493         if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
494             throw new SSLException("Improper close state: " + result);
495         }
496 
497         outNetBuffer.flip();
498 
499         return true;
500     }
501 
502     /**
503      * @param res
504      * @throws SSLException
505      */
506     private void checkStatus(SSLEngineResult res) throws SSLException {
507 
508         SSLEngineResult.Status status = res.getStatus();
509 
510         /*
511          * The status may be:
512          * OK - Normal operation
513          * OVERFLOW - Should never happen since the application buffer is sized to hold the maximum
514          * packet size.
515          * UNDERFLOW - Need to read more data from the socket. It's normal.
516          * CLOSED - The other peer closed the socket. Also normal.
517          */
518         if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
519             throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
520                     + "appBuffer: " + appBuffer);
521         }
522     }
523 
524     /**
525      * Perform any handshaking processing.
526      */
527     /* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
528         for (;;) {
529             switch (handshakeStatus) {
530             case FINISHED:
531             case NOT_HANDSHAKING:
532                 if (LOGGER.isDebugEnabled()) {
533                     LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
534                 }
535 
536                 session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
537                 handshakeComplete = true;
538 
539                 // Send the SECURE message only if it's the first SSL handshake
540                 if (firstSSLNegociation && session.containsAttribute(SslFilter.USE_NOTIFICATION)) {
541                     // SESSION_SECURED is fired only when it's the first handshake
542                     firstSSLNegociation = false;
543                     scheduleMessageReceived(nextFilter, SslFilter.SESSION_SECURED);
544                 }
545 
546                 if (LOGGER.isDebugEnabled()) {
547                     if (!isOutboundDone()) {
548                         LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
549                     } else {
550                         LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
551                     }
552                 }
553 
554                 return;
555 
556             case NEED_TASK:
557                 if (LOGGER.isDebugEnabled()) {
558                     LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
559                 }
560 
561                 handshakeStatus = doTasks();
562                 break;
563 
564             case NEED_UNWRAP:
565                 if (LOGGER.isDebugEnabled()) {
566                     LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
567                 }
568                 // we need more data read
569                 SSLEngineResult.Status status = unwrapHandshake(nextFilter);
570 
571                 if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
572                         && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
573                     // We need more data or the session is closed
574                     return;
575                 }
576 
577                 break;
578 
579             case NEED_WRAP:
580                 if (LOGGER.isDebugEnabled()) {
581                     LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
582                 }
583 
584                 // First make sure that the out buffer is completely empty.
585                 // Since we
586                 // cannot call wrap with data left on the buffer
587                 if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
588                     return;
589                 }
590 
591                 SSLEngineResult result;
592                 createOutNetBuffer(0);
593 
594                 for (;;) {
595                     result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
596                     if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
597                         outNetBuffer.capacity(outNetBuffer.capacity() << 1);
598                         outNetBuffer.limit(outNetBuffer.capacity());
599                     } else {
600                         break;
601                     }
602                 }
603 
604                 outNetBuffer.flip();
605                 handshakeStatus = result.getHandshakeStatus();
606                 writeNetBuffer(nextFilter);
607                 break;
608 
609             default:
610                 String msg = "Invalid Handshaking State" + handshakeStatus
611                 + " while processing the Handshake for session " + session.getId();
612                 LOGGER.error(msg);
613                 throw new IllegalStateException(msg);
614             }
615         }
616     }
617 
618     private void createOutNetBuffer(int expectedRemaining) {
619         // SSLEngine requires us to allocate unnecessarily big buffer
620         // even for small data. *Shrug*
621         int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
622 
623         if (outNetBuffer != null) {
624             outNetBuffer.capacity(capacity);
625         } else {
626             outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
627         }
628     }
629 
630     /* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
631         // Check if any net data needed to be writen
632         if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
633             // no; bail out
634             return null;
635         }
636 
637         // set flag that we are writing encrypted data
638         // (used in SSLFilter.filterWrite())
639         writingEncryptedData = true;
640 
641         // write net data
642         WriteFuture writeFuture = null;
643 
644         try {
645             IoBuffer writeBuffer = fetchOutNetBuffer();
646             writeFuture = new DefaultWriteFuture(session);
647             sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
648 
649             // loop while more writes required to complete handshake
650             while (needToCompleteHandshake()) {
651                 try {
652                     handshake(nextFilter);
653                 } catch (SSLException ssle) {
654                     SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
655                     newSsle.initCause(ssle);
656                     throw newSsle;
657                 }
658 
659                 IoBuffer outNetBuffer = fetchOutNetBuffer();
660                 if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
661                     writeFuture = new DefaultWriteFuture(session);
662                     sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(outNetBuffer, writeFuture));
663                 }
664             }
665         } finally {
666             writingEncryptedData = false;
667         }
668 
669         return writeFuture;
670     }
671 
672     private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
673         // Prepare the net data for reading.
674         if (inNetBuffer != null) {
675             inNetBuffer.flip();
676         }
677 
678         if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
679             // Need more data.
680             return SSLEngineResult.Status.BUFFER_UNDERFLOW;
681         }
682 
683         SSLEngineResult res = unwrap();
684         handshakeStatus = res.getHandshakeStatus();
685 
686         checkStatus(res);
687 
688         // If handshake finished, no data was produced, and the status is still
689         // ok, try to unwrap more
690         if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
691                 && (res.getStatus() == SSLEngineResult.Status.OK)
692                 && inNetBuffer.hasRemaining()) {
693             res = unwrap();
694 
695             // prepare to be written again
696             if (inNetBuffer.hasRemaining()) {
697                 inNetBuffer.compact();
698             } else {
699                 inNetBuffer.free();
700                 inNetBuffer = null;
701             }
702 
703             renegotiateIfNeeded(nextFilter, res);
704         } else {
705             // prepare to be written again
706             if (inNetBuffer.hasRemaining()) {
707                 inNetBuffer.compact();
708             } else {
709                 inNetBuffer.free();
710                 inNetBuffer = null;
711             }
712         }
713 
714         return res.getStatus();
715     }
716 
717     private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
718         if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
719                 && (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
720                 && (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
721             // Renegotiation required.
722             handshakeComplete = false;
723             handshakeStatus = res.getHandshakeStatus();
724             handshake(nextFilter);
725         }
726     }
727 
728     /**
729      * Decrypt the incoming buffer and move the decrypted data to an
730      * application buffer.
731      */
732     private SSLEngineResult unwrap() throws SSLException {
733         // We first have to create the application buffer if it does not exist
734         if (appBuffer == null) {
735             appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
736         } else {
737             // We already have one, just add the new data into it
738             appBuffer.expand(inNetBuffer.remaining());
739         }
740 
741         SSLEngineResult res;
742 
743         Status status = null;
744         HandshakeStatus handshakeStatus = null;
745 
746         do {
747             // Decode the incoming data
748             res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
749             status = res.getStatus();
750 
751             // We can be processing the Handshake
752             handshakeStatus = res.getHandshakeStatus();
753 
754             if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
755                 // We have to grow the target buffer, it's too small.
756                 // Then we can call the unwrap method again
757                 appBuffer.capacity(appBuffer.capacity() << 1);
758                 appBuffer.limit(appBuffer.capacity());
759                 continue;
760             }
761         } while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
762                 && ((handshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) || (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
763 
764         return res;
765     }
766 
767     /**
768      * Do all the outstanding handshake tasks in the current Thread.
769      */
770     private SSLEngineResult.HandshakeStatus doTasks() {
771         /*
772          * We could run this in a separate thread, but I don't see the need for
773          * this when used from SSLFilter. Use thread filters in MINA instead?
774          */
775         Runnable runnable;
776         while ((runnable = sslEngine.getDelegatedTask()) != null) {
777             // TODO : we may have to use a thread pool here to improve the
778             // performances
779             runnable.run();
780         }
781         return sslEngine.getHandshakeStatus();
782     }
783 
784     /**
785      * Creates a new MINA buffer that is a deep copy of the remaining bytes in
786      * the given buffer (between index buf.position() and buf.limit())
787      * 
788      * @param src
789      *            the buffer to copy
790      * @return the new buffer, ready to read from
791      */
792     /* no qualifier */static IoBuffer copy(ByteBuffer src) {
793         IoBuffer copy = IoBuffer.allocate(src.remaining());
794         copy.put(src);
795         copy.flip();
796         return copy;
797     }
798 
799     public String toString() {
800         StringBuilder sb = new StringBuilder();
801 
802         sb.append("SSLStatus <");
803 
804         if (handshakeComplete) {
805             sb.append("SSL established");
806         } else {
807             sb.append("Processing Handshake").append("; ");
808             sb.append("Status : ").append(handshakeStatus).append("; ");
809         }
810 
811         sb.append(", ");
812         sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
813         sb.append(">");
814 
815         return sb.toString();
816     }
817 
818     /**
819      * Free the allocated buffers
820      */
821     /* no qualifier */void release() {
822         if (inNetBuffer != null) {
823             inNetBuffer.free();
824             inNetBuffer = null;
825         }
826 
827         if (outNetBuffer != null) {
828             outNetBuffer.free();
829             outNetBuffer = null;
830         }
831     }
832 }