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