• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package org.apache.harmony.xnet.provider.jsse;
19 
20 
21 import java.io.IOException;
22 import java.nio.BufferUnderflowException;
23 import java.nio.ByteBuffer;
24 import java.nio.ReadOnlyBufferException;
25 import javax.net.ssl.SSLEngine;
26 import javax.net.ssl.SSLEngineResult;
27 import javax.net.ssl.SSLException;
28 import javax.net.ssl.SSLHandshakeException;
29 import javax.net.ssl.SSLSession;
30 
31 /**
32  * Implementation of SSLEngine.
33  * @see javax.net.ssl.SSLEngine class documentation for more information.
34  */
35 public class SSLEngineImpl extends SSLEngine {
36 
37     // indicates if peer mode was set
38     private boolean peer_mode_was_set = false;
39     // indicates if handshake has been started
40     private boolean handshake_started = false;
41     // indicates if inbound operations finished
42     private boolean isInboundDone = false;
43     // indicates if outbound operations finished
44     private boolean isOutboundDone = false;
45     // indicates if close_notify alert had been sent to another peer
46     private boolean close_notify_was_sent = false;
47     // indicates if close_notify alert had been received from another peer
48     private boolean close_notify_was_received = false;
49     // indicates if engine was closed (it means that
50     // all the works on it are done, except (probably) some finalizing work)
51     private boolean engine_was_closed = false;
52     // indicates if engine was shutted down (it means that
53     // all cleaning work had been done and the engine is not operable)
54     private boolean engine_was_shutteddown = false;
55 
56     // record protocol to be used
57     protected SSLRecordProtocol recordProtocol;
58     // input stream for record protocol
59     private SSLBufferedInput recProtIS;
60     // handshake protocol to be used
61     private HandshakeProtocol handshakeProtocol;
62     // alert protocol to be used
63     private AlertProtocol alertProtocol;
64     // place where application data will be stored
65     private SSLEngineAppData appData;
66     // outcoming application data stream
67     private SSLEngineDataStream dataStream = new SSLEngineDataStream();
68     // active session object
69     private SSLSessionImpl session;
70 
71     // peer configuration parameters
72     protected SSLParametersImpl sslParameters;
73 
74     // in case of emergency situations when data could not be
75     // placed in destination buffers it will be stored in this
76     // fields
77     private byte[] remaining_wrapped_data = null;
78     private byte[] remaining_hsh_data = null;
79 
80     // logger
81     private Logger.Stream logger = Logger.getStream("engine");
82 
SSLEngineImpl(SSLParametersImpl sslParameters)83     protected SSLEngineImpl(SSLParametersImpl sslParameters) {
84         this.sslParameters = sslParameters;
85     }
86 
SSLEngineImpl(String host, int port, SSLParametersImpl sslParameters)87     protected SSLEngineImpl(String host, int port, SSLParametersImpl sslParameters) {
88         super(host, port);
89         this.sslParameters = sslParameters;
90     }
91 
92     /**
93      * Starts the handshake.
94      * @throws  SSLException
95      * @see javax.net.ssl.SSLEngine#beginHandshake() method documentation
96      * for more information
97      */
98     @Override
beginHandshake()99     public void beginHandshake() throws SSLException {
100         if (engine_was_closed) {
101             throw new SSLException("Engine has already been closed.");
102         }
103         if (!peer_mode_was_set) {
104             throw new IllegalStateException("Client/Server mode was not set");
105         }
106         if (!handshake_started) {
107             handshake_started = true;
108             if (getUseClientMode()) {
109                 handshakeProtocol = new ClientHandshakeImpl(this);
110             } else {
111                 handshakeProtocol = new ServerHandshakeImpl(this);
112             }
113             appData = new SSLEngineAppData();
114             alertProtocol = new AlertProtocol();
115             recProtIS = new SSLBufferedInput();
116             recordProtocol = new SSLRecordProtocol(handshakeProtocol,
117                     alertProtocol, recProtIS, appData);
118         }
119         handshakeProtocol.start();
120     }
121 
122     /**
123      * Closes inbound operations of this engine
124      * @throws  SSLException
125      * @see javax.net.ssl.SSLEngine#closeInbound() method documentation
126      * for more information
127      */
128     @Override
closeInbound()129     public void closeInbound() throws SSLException {
130         if (logger != null) {
131             logger.println("closeInbound() "+isInboundDone);
132         }
133         if (isInboundDone) {
134             return;
135         }
136         isInboundDone = true;
137         engine_was_closed = true;
138         if (handshake_started) {
139             if (!close_notify_was_received) {
140                 if (session != null) {
141                     session.invalidate();
142                 }
143                 alertProtocol.alert(AlertProtocol.FATAL,
144                         AlertProtocol.INTERNAL_ERROR);
145                 throw new SSLException("Inbound is closed before close_notify "
146                         + "alert has been received.");
147             }
148         } else {
149             // engine is closing before initial handshake has been made
150             shutdown();
151         }
152     }
153 
154     /**
155      * Closes outbound operations of this engine
156      * @see javax.net.ssl.SSLEngine#closeOutbound() method documentation
157      * for more information
158      */
159     @Override
closeOutbound()160     public void closeOutbound() {
161         if (logger != null) {
162             logger.println("closeOutbound() "+isOutboundDone);
163         }
164         if (isOutboundDone) {
165             return;
166         }
167         isOutboundDone = true;
168         if (handshake_started) {
169             // initial handshake had been started
170             alertProtocol.alert(AlertProtocol.WARNING,
171                     AlertProtocol.CLOSE_NOTIFY);
172             close_notify_was_sent = true;
173         } else {
174             // engine is closing before initial handshake has been made
175             shutdown();
176         }
177         engine_was_closed = true;
178     }
179 
180     /**
181      * Returns handshake's delegated tasks to be run
182      * @return the delegated task to be executed.
183      * @see javax.net.ssl.SSLEngine#getDelegatedTask() method documentation
184      * for more information
185      */
186     @Override
getDelegatedTask()187     public Runnable getDelegatedTask() {
188         return handshakeProtocol.getTask();
189     }
190 
191     /**
192      * Returns names of supported cipher suites.
193      * @return array of strings containing the names of supported cipher suites
194      * @see javax.net.ssl.SSLEngine#getSupportedCipherSuites() method
195      * documentation for more information
196      */
197     @Override
getSupportedCipherSuites()198     public String[] getSupportedCipherSuites() {
199         return CipherSuite.getSupportedCipherSuiteNames();
200     }
201 
202     // --------------- SSLParameters based methods ---------------------
203 
204     /**
205      * This method works according to the specification of implemented class.
206      * @see javax.net.ssl.SSLEngine#getEnabledCipherSuites() method
207      * documentation for more information
208      */
209     @Override
getEnabledCipherSuites()210     public String[] getEnabledCipherSuites() {
211         return sslParameters.getEnabledCipherSuites();
212     }
213 
214     /**
215      * This method works according to the specification of implemented class.
216      * @see javax.net.ssl.SSLEngine#setEnabledCipherSuites(String[]) method
217      * documentation for more information
218      */
219     @Override
setEnabledCipherSuites(String[] suites)220     public void setEnabledCipherSuites(String[] suites) {
221         sslParameters.setEnabledCipherSuites(suites);
222     }
223 
224     /**
225      * This method works according to the specification of implemented class.
226      * @see javax.net.ssl.SSLEngine#getSupportedProtocols() method
227      * documentation for more information
228      */
229     @Override
getSupportedProtocols()230     public String[] getSupportedProtocols() {
231         return ProtocolVersion.supportedProtocols.clone();
232     }
233 
234     /**
235      * This method works according to the specification of implemented class.
236      * @see javax.net.ssl.SSLEngine#getEnabledProtocols() method
237      * documentation for more information
238      */
239     @Override
getEnabledProtocols()240     public String[] getEnabledProtocols() {
241         return sslParameters.getEnabledProtocols();
242     }
243 
244     /**
245      * This method works according to the specification of implemented class.
246      * @see javax.net.ssl.SSLEngine#setEnabledProtocols(String[]) method
247      * documentation for more information
248      */
249     @Override
setEnabledProtocols(String[] protocols)250     public void setEnabledProtocols(String[] protocols) {
251         sslParameters.setEnabledProtocols(protocols);
252     }
253 
254     /**
255      * This method works according to the specification of implemented class.
256      * @see javax.net.ssl.SSLEngine#setUseClientMode(boolean) method
257      * documentation for more information
258      */
259     @Override
setUseClientMode(boolean mode)260     public void setUseClientMode(boolean mode) {
261         if (handshake_started) {
262             throw new IllegalArgumentException(
263             "Could not change the mode after the initial handshake has begun.");
264         }
265         sslParameters.setUseClientMode(mode);
266         peer_mode_was_set = true;
267     }
268 
269     /**
270      * This method works according to the specification of implemented class.
271      * @see javax.net.ssl.SSLEngine#getUseClientMode() method
272      * documentation for more information
273      */
274     @Override
getUseClientMode()275     public boolean getUseClientMode() {
276         return sslParameters.getUseClientMode();
277     }
278 
279     /**
280      * This method works according to the specification of implemented class.
281      * @see javax.net.ssl.SSLEngine#setNeedClientAuth(boolean) method
282      * documentation for more information
283      */
284     @Override
setNeedClientAuth(boolean need)285     public void setNeedClientAuth(boolean need) {
286         sslParameters.setNeedClientAuth(need);
287     }
288 
289     /**
290      * This method works according to the specification of implemented class.
291      * @see javax.net.ssl.SSLEngine#getNeedClientAuth() method
292      * documentation for more information
293      */
294     @Override
getNeedClientAuth()295     public boolean getNeedClientAuth() {
296         return sslParameters.getNeedClientAuth();
297     }
298 
299     /**
300      * This method works according to the specification of implemented class.
301      * @see javax.net.ssl.SSLEngine#setWantClientAuth(boolean) method
302      * documentation for more information
303      */
304     @Override
setWantClientAuth(boolean want)305     public void setWantClientAuth(boolean want) {
306         sslParameters.setWantClientAuth(want);
307     }
308 
309     /**
310      * This method works according to the specification of implemented class.
311      * @see javax.net.ssl.SSLEngine#getWantClientAuth() method
312      * documentation for more information
313      */
314     @Override
getWantClientAuth()315     public boolean getWantClientAuth() {
316         return sslParameters.getWantClientAuth();
317     }
318 
319     /**
320      * This method works according to the specification of implemented class.
321      * @see javax.net.ssl.SSLEngine#setEnableSessionCreation(boolean) method
322      * documentation for more information
323      */
324     @Override
setEnableSessionCreation(boolean flag)325     public void setEnableSessionCreation(boolean flag) {
326         sslParameters.setEnableSessionCreation(flag);
327     }
328 
329     /**
330      * This method works according to the specification of implemented class.
331      * @see javax.net.ssl.SSLEngine#getEnableSessionCreation() method
332      * documentation for more information
333      */
334     @Override
getEnableSessionCreation()335     public boolean getEnableSessionCreation() {
336         return sslParameters.getEnableSessionCreation();
337     }
338 
339     // -----------------------------------------------------------------
340 
341     /**
342      * This method works according to the specification of implemented class.
343      * @see javax.net.ssl.SSLEngine#getHandshakeStatus() method
344      * documentation for more information
345      */
346     @Override
getHandshakeStatus()347     public SSLEngineResult.HandshakeStatus getHandshakeStatus() {
348         if (!handshake_started || engine_was_shutteddown) {
349             // initial handshake has not been started yet
350             return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
351         }
352         if (alertProtocol.hasAlert()) {
353             // need to send an alert
354             return SSLEngineResult.HandshakeStatus.NEED_WRAP;
355         }
356         if (close_notify_was_sent && !close_notify_was_received) {
357             // waiting for "close_notify" response
358             return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
359         }
360         return handshakeProtocol.getStatus();
361     }
362 
363     /**
364      * This method works according to the specification of implemented class.
365      * @see javax.net.ssl.SSLEngine#getSession() method
366      * documentation for more information
367      */
368     @Override
getSession()369     public SSLSession getSession() {
370         if (session != null) {
371             return session;
372         }
373         return SSLSessionImpl.NULL_SESSION;
374     }
375 
376     /**
377      * This method works according to the specification of implemented class.
378      * @see javax.net.ssl.SSLEngine#isInboundDone() method
379      * documentation for more information
380      */
381     @Override
isInboundDone()382     public boolean isInboundDone() {
383         return isInboundDone || engine_was_closed;
384     }
385 
386     /**
387      * This method works according to the specification of implemented class.
388      * @see javax.net.ssl.SSLEngine#isOutboundDone() method
389      * documentation for more information
390      */
391     @Override
isOutboundDone()392     public boolean isOutboundDone() {
393         return isOutboundDone;
394     }
395 
396     /**
397      * Decodes one complete SSL/TLS record provided in the source buffer.
398      * If decoded record contained application data, this data will
399      * be placed in the destination buffers.
400      * For more information about TLS record fragmentation see
401      * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
402      * @param src source buffer containing SSL/TLS record.
403      * @param dsts destination buffers to place received application data.
404      * @see javax.net.ssl.SSLEngine#unwrap(ByteBuffer,ByteBuffer[],int,int)
405      * method documentation for more information
406      */
407     @Override
unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length)408     public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts,
409                                 int offset, int length) throws SSLException {
410         if (engine_was_shutteddown) {
411             return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
412                     SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
413         }
414         if ((src == null) || (dsts == null)) {
415             throw new IllegalStateException(
416                     "Some of the input parameters are null");
417         }
418 
419         if (!handshake_started) {
420             beginHandshake();
421         }
422 
423         SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
424         // If is is initial handshake or connection closure stage,
425         // check if this call was made in spite of handshake status
426         if ((session == null || engine_was_closed) && (
427                     handshakeStatus.equals(
428                         SSLEngineResult.HandshakeStatus.NEED_WRAP) ||
429                     handshakeStatus.equals(
430                         SSLEngineResult.HandshakeStatus.NEED_TASK))) {
431             return new SSLEngineResult(
432                     getEngineStatus(), handshakeStatus, 0, 0);
433         }
434 
435         if (src.remaining() < recordProtocol.getMinRecordSize()) {
436             return new SSLEngineResult(
437                     SSLEngineResult.Status.BUFFER_UNDERFLOW,
438                     getHandshakeStatus(), 0, 0);
439         }
440 
441         try {
442             src.mark();
443             // check the destination buffers and count their capacity
444             int capacity = 0;
445             for (int i=offset; i<offset+length; i++) {
446                 if (dsts[i] == null) {
447                     throw new IllegalStateException(
448                             "Some of the input parameters are null");
449                 }
450                 if (dsts[i].isReadOnly()) {
451                     throw new ReadOnlyBufferException();
452                 }
453                 capacity += dsts[i].remaining();
454             }
455             if (capacity < recordProtocol.getDataSize(src.remaining())) {
456                 return new SSLEngineResult(
457                         SSLEngineResult.Status.BUFFER_OVERFLOW,
458                         getHandshakeStatus(), 0, 0);
459             }
460             recProtIS.setSourceBuffer(src);
461             // unwrap the record contained in source buffer, pass it
462             // to appropriate client protocol (alert, handshake, or app)
463             // and retrieve the type of unwrapped data
464             int type = recordProtocol.unwrap();
465             // process the data and return the result
466             switch (type) {
467                 case ContentType.HANDSHAKE:
468                 case ContentType.CHANGE_CIPHER_SPEC:
469                     if (handshakeProtocol.getStatus().equals(
470                             SSLEngineResult.HandshakeStatus.FINISHED)) {
471                         session = recordProtocol.getSession();
472                     }
473                     break;
474                 case ContentType.APPLICATION_DATA:
475                     break;
476                 case ContentType.ALERT:
477                     if (alertProtocol.isFatalAlert()) {
478                         alertProtocol.setProcessed();
479                         if (session != null) {
480                             session.invalidate();
481                         }
482                         String description = "Fatal alert received "
483                             + alertProtocol.getAlertDescription();
484                         shutdown();
485                         throw new SSLException(description);
486                     } else {
487                         if (logger != null) {
488                             logger.println("Warning allert has been received: "
489                                 + alertProtocol.getAlertDescription());
490                         }
491                         switch(alertProtocol.getDescriptionCode()) {
492                             case AlertProtocol.CLOSE_NOTIFY:
493                                 alertProtocol.setProcessed();
494                                 close_notify_was_received = true;
495                                 if (!close_notify_was_sent) {
496                                     closeOutbound();
497                                     closeInbound();
498                                 } else {
499                                     closeInbound();
500                                     shutdown();
501                                 }
502                                 break;
503                             case AlertProtocol.NO_RENEGOTIATION:
504                                 alertProtocol.setProcessed();
505                                 if (session == null) {
506                                     // message received during the initial
507                                     // handshake
508                                     throw new AlertException(
509                                         AlertProtocol.HANDSHAKE_FAILURE,
510                                         new SSLHandshakeException(
511                                             "Received no_renegotiation "
512                                             + "during the initial handshake"));
513                                 } else {
514                                     // just stop the handshake
515                                     handshakeProtocol.stop();
516                                 }
517                                 break;
518                             default:
519                                 alertProtocol.setProcessed();
520                         }
521                     }
522                     break;
523             }
524             return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(),
525                     recProtIS.consumed(),
526                     // place the app. data (if any) into the dest. buffers
527                     // and get the number of produced bytes:
528                     appData.placeTo(dsts, offset, length));
529         } catch (BufferUnderflowException e) {
530             // there was not enought data ource buffer to make complete packet
531             src.reset();
532             return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW,
533                     getHandshakeStatus(), 0, 0);
534         } catch (AlertException e) {
535             // fatal alert occured
536             alertProtocol.alert(AlertProtocol.FATAL, e.getDescriptionCode());
537             engine_was_closed = true;
538             src.reset();
539             if (session != null) {
540                 session.invalidate();
541             }
542             // shutdown work will be made after the alert will be sent
543             // to another peer (by wrap method)
544             throw e.getReason();
545         } catch (SSLException e) {
546             throw e;
547         } catch (IOException e) {
548             alertProtocol.alert(AlertProtocol.FATAL,
549                     AlertProtocol.INTERNAL_ERROR);
550             engine_was_closed = true;
551             // shutdown work will be made after the alert will be sent
552             // to another peer (by wrap method)
553             throw new SSLException(e.getMessage());
554         }
555     }
556 
557     /**
558      * Encodes the application data into SSL/TLS record. If handshake status
559      * of the engine differs from NOT_HANDSHAKING the operation can work
560      * without consuming of the source data.
561      * For more information about TLS record fragmentation see
562      * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
563      * @param srcs the source buffers with application data to be encoded
564      * into SSL/TLS record.
565      * @param offset the offset in the destination buffers array pointing to
566      * the first buffer with the source data.
567      * @param len specifies the maximum number of buffers to be procesed.
568      * @param dst the destination buffer where encoded data will be placed.
569      * @see javax.net.ssl.SSLEngine#wrap(ByteBuffer[],int,int,ByteBuffer) method
570      * documentation for more information
571      */
572     @Override
wrap(ByteBuffer[] srcs, int offset, int len, ByteBuffer dst)573     public SSLEngineResult wrap(ByteBuffer[] srcs, int offset,
574                             int len, ByteBuffer dst) throws SSLException {
575         if (engine_was_shutteddown) {
576             return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
577                     SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
578         }
579         if ((srcs == null) || (dst == null)) {
580             throw new IllegalStateException(
581                     "Some of the input parameters are null");
582         }
583         if (dst.isReadOnly()) {
584             throw new ReadOnlyBufferException();
585         }
586 
587         if (!handshake_started) {
588             beginHandshake();
589         }
590 
591         SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
592         // If it is an initial handshake or connection closure stage,
593         // check if this call was made in spite of handshake status
594         if ((session == null || engine_was_closed) && (
595                 handshakeStatus.equals(
596                         SSLEngineResult.HandshakeStatus.NEED_UNWRAP) ||
597                 handshakeStatus.equals(
598                         SSLEngineResult.HandshakeStatus.NEED_TASK))) {
599             return new SSLEngineResult(
600                     getEngineStatus(), handshakeStatus, 0, 0);
601         }
602 
603         int capacity = dst.remaining();
604         int produced = 0;
605 
606         if (alertProtocol.hasAlert()) {
607             // we have an alert to be sent
608             if (capacity < recordProtocol.getRecordSize(2)) {
609                 return new SSLEngineResult(
610                         SSLEngineResult.Status.BUFFER_OVERFLOW,
611                         handshakeStatus, 0, 0);
612             }
613             byte[] alert_data = alertProtocol.wrap();
614             // place the alert record into destination
615             dst.put(alert_data);
616             if (alertProtocol.isFatalAlert()) {
617                 alertProtocol.setProcessed();
618                 if (session != null) {
619                     session.invalidate();
620                 }
621                 // fatal alert has been sent, so shut down the engine
622                 shutdown();
623                 return new SSLEngineResult(
624                         SSLEngineResult.Status.CLOSED,
625                         SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
626                         0, alert_data.length);
627             } else {
628                 alertProtocol.setProcessed();
629                 // check if the works on this engine have been done
630                 if (close_notify_was_sent && close_notify_was_received) {
631                     shutdown();
632                     return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
633                             SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
634                             0, alert_data.length);
635                 }
636                 return new SSLEngineResult(
637                         getEngineStatus(),
638                         getHandshakeStatus(),
639                         0, alert_data.length);
640             }
641         }
642 
643         if (capacity < recordProtocol.getMinRecordSize()) {
644             if (logger != null) {
645                 logger.println("Capacity of the destination("
646                         +capacity+") < MIN_PACKET_SIZE("
647                         +recordProtocol.getMinRecordSize()+")");
648             }
649             return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
650                         handshakeStatus, 0, 0);
651         }
652 
653         try {
654             if (!handshakeStatus.equals(
655                         SSLEngineResult.HandshakeStatus.NEED_WRAP)) {
656                 // so we wraps application data
657                 dataStream.setSourceBuffers(srcs, offset, len);
658                 if ((capacity < SSLRecordProtocol.MAX_SSL_PACKET_SIZE) &&
659                     (capacity < recordProtocol.getRecordSize(
660                                                  dataStream.available()))) {
661                     if (logger != null) {
662                         logger.println("The destination buffer("
663                                 +capacity+") can not take the resulting packet("
664                                 + recordProtocol.getRecordSize(
665                                     dataStream.available())+")");
666                     }
667                     return new SSLEngineResult(
668                             SSLEngineResult.Status.BUFFER_OVERFLOW,
669                             handshakeStatus, 0, 0);
670                 }
671                 if (remaining_wrapped_data == null) {
672                     remaining_wrapped_data =
673                         recordProtocol.wrap(ContentType.APPLICATION_DATA,
674                                 dataStream);
675                 }
676                 if (capacity < remaining_wrapped_data.length) {
677                     // It should newer happen because we checked the destination
678                     // buffer size, but there is a possibility
679                     // (if dest buffer was filled outside)
680                     // so we just remember the data into remaining_wrapped_data
681                     // and will enclose it during the the next call
682                     return new SSLEngineResult(
683                             SSLEngineResult.Status.BUFFER_OVERFLOW,
684                             handshakeStatus, dataStream.consumed(), 0);
685                 } else {
686                     dst.put(remaining_wrapped_data);
687                     produced = remaining_wrapped_data.length;
688                     remaining_wrapped_data = null;
689                     return new SSLEngineResult(getEngineStatus(),
690                             handshakeStatus, dataStream.consumed(), produced);
691                 }
692             } else {
693                 if (remaining_hsh_data == null) {
694                     remaining_hsh_data = handshakeProtocol.wrap();
695                 }
696                 if (capacity < remaining_hsh_data.length) {
697                     // It should newer happen because we checked the destination
698                     // buffer size, but there is a possibility
699                     // (if dest buffer was filled outside)
700                     // so we just remember the data into remaining_hsh_data
701                     // and will enclose it during the the next call
702                     return new SSLEngineResult(
703                             SSLEngineResult.Status.BUFFER_OVERFLOW,
704                             handshakeStatus, 0, 0);
705                 } else {
706                     dst.put(remaining_hsh_data);
707                     produced = remaining_hsh_data.length;
708                     remaining_hsh_data = null;
709 
710                     handshakeStatus = handshakeProtocol.getStatus();
711                     if (handshakeStatus.equals(
712                             SSLEngineResult.HandshakeStatus.FINISHED)) {
713                         session = recordProtocol.getSession();
714                     }
715                 }
716                 return new SSLEngineResult(
717                         getEngineStatus(), getHandshakeStatus(), 0, produced);
718             }
719         } catch (AlertException e) {
720             // fatal alert occured
721             alertProtocol.alert(AlertProtocol.FATAL, e.getDescriptionCode());
722             engine_was_closed = true;
723             if (session != null) {
724                 session.invalidate();
725             }
726             // shutdown work will be made after the alert will be sent
727             // to another peer (by wrap method)
728             throw e.getReason();
729         }
730     }
731 
732     // Shutdownes the engine and makes all cleanup work.
shutdown()733     private void shutdown() {
734         engine_was_closed = true;
735         engine_was_shutteddown = true;
736         isOutboundDone = true;
737         isInboundDone = true;
738         if (handshake_started) {
739             alertProtocol.shutdown();
740             alertProtocol = null;
741             handshakeProtocol.shutdown();
742             handshakeProtocol = null;
743             recordProtocol.shutdown();
744             recordProtocol = null;
745         }
746     }
747 
748 
getEngineStatus()749     private SSLEngineResult.Status getEngineStatus() {
750         return (engine_was_closed)
751             ? SSLEngineResult.Status.CLOSED
752             : SSLEngineResult.Status.OK;
753     }
754 }
755