Home | History | Annotate | Download | only in conscrypt
      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.conscrypt;
     19 
     20 import java.io.IOException;
     21 import java.io.InputStream;
     22 import java.io.OutputStream;
     23 import java.net.InetAddress;
     24 import java.net.SocketAddress;
     25 import java.net.SocketException;
     26 import java.net.UnknownHostException;
     27 import java.util.ArrayList;
     28 import javax.net.ssl.HandshakeCompletedEvent;
     29 import javax.net.ssl.HandshakeCompletedListener;
     30 import javax.net.ssl.SSLEngineResult;
     31 import javax.net.ssl.SSLException;
     32 import javax.net.ssl.SSLSession;
     33 import javax.net.ssl.SSLSocket;
     34 
     35 /**
     36  * SSLSocket implementation.
     37  * @see javax.net.ssl.SSLSocket class documentation for more information.
     38  */
     39 public class SSLSocketImpl extends SSLSocket {
     40 
     41     // indicates if handshake has been started
     42     private boolean handshake_started = false;
     43 
     44     // used when we're wrapping a socket
     45     private final String wrappedHost;
     46     private final int wrappedPort;
     47 
     48     // record protocol to be used
     49     protected SSLRecordProtocol recordProtocol;
     50     // handshake protocol to be used
     51     private HandshakeProtocol handshakeProtocol;
     52     // alert protocol to be used
     53     private AlertProtocol alertProtocol;
     54     // application data input stream, this stream is presented by
     55     // ssl socket as an input stream. Additionally this object is a
     56     // place where application data will be stored by record protocol
     57     private SSLSocketInputStream appDataIS;
     58     // outgoing application data stream
     59     private SSLSocketOutputStream appDataOS;
     60     // active session object
     61     private SSLSessionImpl session;
     62 
     63     private boolean socket_was_closed = false;
     64 
     65     // the sslParameters object encapsulates all the info
     66     // about supported and enabled cipher suites and protocols,
     67     // as well as the information about client/server mode of
     68     // ssl socket, whether it require/want client authentication or not,
     69     // and controls whether new SSL sessions may be established by this
     70     // socket or not.
     71     protected SSLParametersImpl sslParameters;
     72     // super's streams to be wrapped:
     73     protected InputStream input;
     74     protected OutputStream output;
     75     // handshake complete listeners
     76     private ArrayList<HandshakeCompletedListener> listeners;
     77     // logger
     78     private Logger.Stream logger = Logger.getStream("socket");
     79 
     80     // ----------------- Constructors and initializers --------------------
     81 
     82     /**
     83      * Constructor
     84      * @param   sslParameters:  SSLParametersImpl
     85      * @see javax.net.ssl.SSLSocket#SSLSocket() method documentation
     86      * for more information.
     87      */
     88     protected SSLSocketImpl(SSLParametersImpl sslParameters) {
     89         this.sslParameters = sslParameters;
     90         this.wrappedHost = null;
     91         this.wrappedPort = -1;
     92         // init should be called after creation!
     93     }
     94 
     95     /**
     96      * Constructor
     97      * @param   host:   String
     98      * @param   port:   int
     99      * @param   sslParameters:  SSLParametersImpl
    100      * @throws  IOException
    101      * @throws  UnknownHostException
    102      * @see javax.net.ssl.SSLSocket#SSLSocket(String,int)
    103      * method documentation for more information.
    104      */
    105     protected SSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
    106             throws IOException, UnknownHostException {
    107         super(host, port);
    108         this.wrappedHost = host;
    109         this.wrappedPort = port;
    110         this.sslParameters = sslParameters;
    111         init();
    112     }
    113 
    114     /**
    115      * Constructor
    116      * @param   host:   String
    117      * @param   port:   int
    118      * @param   localHost:  InetAddress
    119      * @param   localPort:  int
    120      * @param   sslParameters:  SSLParametersImpl
    121      * @throws  IOException
    122      * @throws  UnknownHostException
    123      * @see javax.net.ssl.SSLSocket#SSLSocket(String,int,InetAddress,int)
    124      * method documentation for more information.
    125      */
    126     protected SSLSocketImpl(String host, int port,
    127             InetAddress localHost, int localPort,
    128             SSLParametersImpl sslParameters) throws IOException,
    129             UnknownHostException {
    130         super(host, port, localHost, localPort);
    131         this.wrappedHost = host;
    132         this.wrappedPort = port;
    133         this.sslParameters = sslParameters;
    134         init();
    135     }
    136 
    137     /**
    138      * Constructor
    139      * @param   host:   InetAddress
    140      * @param   port:   int
    141      * @param   sslParameters:  SSLParametersImpl
    142      * @return
    143      * @throws  IOException
    144      * @see javax.net.ssl.SSLSocket#SSLSocket(InetAddress,int)
    145      * method documentation for more information.
    146      */
    147     protected SSLSocketImpl(InetAddress host, int port,
    148             SSLParametersImpl sslParameters) throws IOException {
    149         super(host, port);
    150         this.sslParameters = sslParameters;
    151         this.wrappedHost = null;
    152         this.wrappedPort = -1;
    153         init();
    154     }
    155 
    156     /**
    157      * Constructor
    158      * @param   address:    InetAddress
    159      * @param   port:   int
    160      * @param   localAddress:   InetAddress
    161      * @param   localPort:  int
    162      * @param   sslParameters:  SSLParametersImpl
    163      * @return
    164      * @throws  IOException
    165      * @see javax.net.ssl.SSLSocket#SSLSocket(InetAddress,int,InetAddress,int)
    166      * method documentation for more information.
    167      */
    168     protected SSLSocketImpl(InetAddress address, int port,
    169             InetAddress localAddress, int localPort,
    170             SSLParametersImpl sslParameters) throws IOException {
    171         super(address, port, localAddress, localPort);
    172         this.sslParameters = sslParameters;
    173         this.wrappedHost = null;
    174         this.wrappedPort = -1;
    175         init();
    176     }
    177 
    178     /**
    179      * Initialize the SSL socket.
    180      */
    181     protected void init() throws IOException {
    182         if (appDataIS != null) {
    183             // already initialized
    184             return;
    185         }
    186         initTransportLayer();
    187         appDataIS = new SSLSocketInputStream(this);
    188         appDataOS = new SSLSocketOutputStream(this);
    189     }
    190 
    191     /**
    192      * Initialize the transport data streams.
    193      */
    194     protected void initTransportLayer() throws IOException {
    195         input = super.getInputStream();
    196         output = super.getOutputStream();
    197     }
    198 
    199     /**
    200      * Closes the transport data streams.
    201      */
    202     protected void closeTransportLayer() throws IOException {
    203         super.close();
    204         if (input != null) {
    205             input.close();
    206             output.close();
    207         }
    208     }
    209 
    210     String getWrappedHostName() {
    211         return wrappedHost;
    212     }
    213 
    214     int getWrappedPort() {
    215         return wrappedPort;
    216     }
    217 
    218     String getPeerHostName() {
    219         if (wrappedHost != null) {
    220             return wrappedHost;
    221         }
    222         InetAddress inetAddress = super.getInetAddress();
    223         if (inetAddress != null) {
    224             return inetAddress.getHostName();
    225         }
    226         return null;
    227     }
    228 
    229     int getPeerPort() {
    230         return (wrappedPort == -1) ? super.getPort() : wrappedPort;
    231     }
    232 
    233     // --------------- SSLParameters based methods ---------------------
    234 
    235     /**
    236      * This method works according to the specification of implemented class.
    237      * @see javax.net.ssl.SSLSocket#getSupportedCipherSuites()
    238      * method documentation for more information
    239      */
    240     @Override
    241     public String[] getSupportedCipherSuites() {
    242         return CipherSuite.getSupportedCipherSuiteNames();
    243     }
    244 
    245     /**
    246      * This method works according to the specification of implemented class.
    247      * @see javax.net.ssl.SSLSocket#getEnabledCipherSuites()
    248      * method documentation for more information
    249      */
    250     @Override
    251     public String[] getEnabledCipherSuites() {
    252         return sslParameters.getEnabledCipherSuites();
    253     }
    254 
    255     /**
    256      * This method works according to the specification of implemented class.
    257      * @see javax.net.ssl.SSLSocket#setEnabledCipherSuites(String[])
    258      * method documentation for more information
    259      */
    260     @Override
    261     public void setEnabledCipherSuites(String[] suites) {
    262         sslParameters.setEnabledCipherSuites(suites);
    263     }
    264 
    265     /**
    266      * This method works according to the specification of implemented class.
    267      * @see javax.net.ssl.SSLSocket#getSupportedProtocols()
    268      * method documentation for more information
    269      */
    270     @Override
    271     public String[] getSupportedProtocols() {
    272         return ProtocolVersion.supportedProtocols.clone();
    273     }
    274 
    275     /**
    276      * This method works according to the specification of implemented class.
    277      * @see javax.net.ssl.SSLSocket#getEnabledProtocols()
    278      * method documentation for more information
    279      */
    280     @Override
    281     public String[] getEnabledProtocols() {
    282         return sslParameters.getEnabledProtocols();
    283     }
    284 
    285     /**
    286      * This method works according to the specification of implemented class.
    287      * @see javax.net.ssl.SSLSocket#setEnabledProtocols(String[])
    288      * method documentation for more information
    289      */
    290     @Override
    291     public void setEnabledProtocols(String[] protocols) {
    292         sslParameters.setEnabledProtocols(protocols);
    293     }
    294 
    295     /**
    296      * This method works according to the specification of implemented class.
    297      * @see javax.net.ssl.SSLSocket#setUseClientMode(boolean)
    298      * method documentation for more information
    299      */
    300     @Override
    301     public void setUseClientMode(boolean mode) {
    302         if (handshake_started) {
    303             throw new IllegalArgumentException(
    304             "Could not change the mode after the initial handshake has begun.");
    305         }
    306         sslParameters.setUseClientMode(mode);
    307     }
    308 
    309     /**
    310      * This method works according to the specification of implemented class.
    311      * @see javax.net.ssl.SSLSocket#getUseClientMode()
    312      * method documentation for more information
    313      */
    314     @Override
    315     public boolean getUseClientMode() {
    316         return sslParameters.getUseClientMode();
    317     }
    318 
    319     /**
    320      * This method works according to the specification of implemented class.
    321      * @see javax.net.ssl.SSLSocket#setNeedClientAuth(boolean)
    322      * method documentation for more information
    323      */
    324     @Override
    325     public void setNeedClientAuth(boolean need) {
    326         sslParameters.setNeedClientAuth(need);
    327     }
    328 
    329     /**
    330      * This method works according to the specification of implemented class.
    331      * @see javax.net.ssl.SSLSocket#getNeedClientAuth()
    332      * method documentation for more information
    333      */
    334     @Override
    335     public boolean getNeedClientAuth() {
    336         return sslParameters.getNeedClientAuth();
    337     }
    338 
    339     /**
    340      * This method works according to the specification of implemented class.
    341      * @see javax.net.ssl.SSLSocket#setWantClientAuth(boolean)
    342      * method documentation for more information
    343      */
    344     @Override
    345     public void setWantClientAuth(boolean want) {
    346         sslParameters.setWantClientAuth(want);
    347     }
    348 
    349     /**
    350      * This method works according to the specification of implemented class.
    351      * @see javax.net.ssl.SSLSocket#getWantClientAuth()
    352      * method documentation for more information
    353      */
    354     @Override
    355     public boolean getWantClientAuth() {
    356         return sslParameters.getWantClientAuth();
    357     }
    358 
    359     /**
    360      * This method works according to the specification of implemented class.
    361      * @see javax.net.ssl.SSLSocket#setEnableSessionCreation(boolean)
    362      * method documentation for more information
    363      */
    364     @Override
    365     public void setEnableSessionCreation(boolean flag) {
    366         sslParameters.setEnableSessionCreation(flag);
    367     }
    368 
    369     /**
    370      * This method works according to the specification of implemented class.
    371      * @see javax.net.ssl.SSLSocket#getEnableSessionCreation()
    372      * method documentation for more information
    373      */
    374     @Override
    375     public boolean getEnableSessionCreation() {
    376         return sslParameters.getEnableSessionCreation();
    377     }
    378 
    379     // -----------------------------------------------------------------
    380 
    381     /**
    382      * This method works according to the specification of implemented class.
    383      * @see javax.net.ssl.SSLSocket#getSession()
    384      * method documentation for more information
    385      */
    386     @Override
    387     public SSLSession getSession() {
    388         if (!handshake_started) {
    389             try {
    390                 startHandshake();
    391             } catch (IOException e) {
    392                 // return an invalid session with
    393                 // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
    394                 return SSLSessionImpl.getNullSession();
    395             }
    396         }
    397         return session;
    398     }
    399 
    400     /**
    401      * This method works according to the specification of implemented class.
    402      * @see javax.net.ssl.SSLSocket#addHandshakeCompletedListener(HandshakeCompletedListener)
    403      * method documentation for more information
    404      */
    405     @Override
    406     public void addHandshakeCompletedListener(
    407             HandshakeCompletedListener listener) {
    408         if (listener == null) {
    409             throw new IllegalArgumentException("Provided listener is null");
    410         }
    411         if (listeners == null) {
    412             listeners = new ArrayList<HandshakeCompletedListener>();
    413         }
    414         listeners.add(listener);
    415     }
    416 
    417     /**
    418      * This method works according to the specification of implemented class.
    419      * @see javax.net.ssl.SSLSocket#removeHandshakeCompletedListener(HandshakeCompletedListener)
    420      * method documentation for more information
    421      */
    422     @Override
    423     public void removeHandshakeCompletedListener(
    424             HandshakeCompletedListener listener) {
    425         if (listener == null) {
    426             throw new IllegalArgumentException("Provided listener is null");
    427         }
    428         if (listeners == null) {
    429             throw new IllegalArgumentException(
    430                     "Provided listener is not registered");
    431         }
    432         if (!listeners.remove(listener)) {
    433             throw new IllegalArgumentException(
    434                     "Provided listener is not registered");
    435         }
    436     }
    437 
    438     /**
    439      * Performs the handshake process over the SSL/TLS connection
    440      * as described in rfc 2246, TLS v1 specification
    441      * http://www.ietf.org/rfc/rfc2246.txt. If the initial handshake
    442      * has been already done, this method initiates rehandshake.
    443      * This method works according to the specification of implemented class.
    444      * @see javax.net.ssl.SSLSocket#startHandshake()
    445      * method documentation for more information
    446      */
    447     @Override
    448     public void startHandshake() throws IOException {
    449         if (appDataIS == null) {
    450             throw new IOException("Socket is not connected.");
    451         }
    452         if (socket_was_closed) {
    453             throw new IOException("Socket has already been closed.");
    454         }
    455 
    456         if (!handshake_started) {
    457             handshake_started = true;
    458             if (sslParameters.getUseClientMode()) {
    459                 if (logger != null) {
    460                     logger.println("SSLSocketImpl: CLIENT");
    461                 }
    462                 handshakeProtocol = new ClientHandshakeImpl(this);
    463             } else {
    464                 if (logger != null) {
    465                     logger.println("SSLSocketImpl: SERVER");
    466                 }
    467                 handshakeProtocol = new ServerHandshakeImpl(this);
    468             }
    469 
    470             alertProtocol = new AlertProtocol();
    471             recordProtocol = new SSLRecordProtocol(handshakeProtocol,
    472                     alertProtocol, new SSLStreamedInput(input),
    473                     appDataIS.dataPoint);
    474         }
    475 
    476         if (logger != null) {
    477             logger.println("SSLSocketImpl.startHandshake");
    478         }
    479 
    480         handshakeProtocol.start();
    481 
    482         doHandshake();
    483 
    484         if (logger != null) {
    485             logger.println("SSLSocketImpl.startHandshake: END");
    486         }
    487     }
    488 
    489     /**
    490      * This method works according to the specification of implemented class.
    491      * @see javax.net.ssl.SSLSocket#getInputStream()
    492      * method documentation for more information
    493      */
    494     @Override
    495     public InputStream getInputStream() throws IOException {
    496         if (socket_was_closed) {
    497             throw new IOException("Socket has already been closed.");
    498         }
    499         return appDataIS;
    500     }
    501 
    502     /**
    503      * This method works according to the specification of implemented class.
    504      * @see javax.net.ssl.SSLSocket#getOutputStream()
    505      * method documentation for more information
    506      */
    507     @Override
    508     public OutputStream getOutputStream() throws IOException {
    509         if (socket_was_closed) {
    510             throw new IOException("Socket has already been closed.");
    511         }
    512         return appDataOS;
    513     }
    514 
    515     /**
    516      * This method works according to the specification of implemented class.
    517      * @see java.net.Socket#connect(SocketAddress)
    518      * method documentation for more information
    519      */
    520     @Override
    521     public void connect(SocketAddress endpoint) throws IOException {
    522         super.connect(endpoint);
    523         init();
    524     }
    525 
    526     /**
    527      * This method works according to the specification of implemented class.
    528      * @see java.net.Socket#connect(SocketAddress,int)
    529      * method documentation for more information
    530      */
    531     @Override
    532     public void connect(SocketAddress endpoint, int timeout)
    533             throws IOException {
    534         super.connect(endpoint, timeout);
    535         init();
    536     }
    537 
    538     /**
    539      * This method works according to the specification of implemented class.
    540      * @see javax.net.ssl.SSLSocket#close()
    541      * method documentation for more information
    542      */
    543     @Override
    544     public void close() throws IOException {
    545         if (logger != null) {
    546             logger.println("SSLSocket.close "+socket_was_closed);
    547         }
    548         if (!socket_was_closed) {
    549             if (handshake_started) {
    550                 alertProtocol.alert(AlertProtocol.WARNING,
    551                         AlertProtocol.CLOSE_NOTIFY);
    552                 try {
    553                     output.write(alertProtocol.wrap());
    554                 } catch (IOException ex) { }
    555                 alertProtocol.setProcessed();
    556             }
    557             shutdown();
    558             closeTransportLayer();
    559             socket_was_closed = true;
    560         }
    561     }
    562 
    563     /**
    564      * This method is not supported for SSLSocket implementation.
    565      */
    566     @Override
    567     public void sendUrgentData(int data) throws IOException {
    568         throw new SocketException(
    569                 "Method sendUrgentData() is not supported.");
    570     }
    571 
    572     /**
    573      * This method is not supported for SSLSocket implementation.
    574      */
    575     @Override
    576     public void setOOBInline(boolean on) throws SocketException {
    577         throw new SocketException(
    578                 "Methods sendUrgentData, setOOBInline are not supported.");
    579     }
    580 
    581     // -----------------------------------------------------------------
    582 
    583     private void shutdown() {
    584         if (handshake_started) {
    585             alertProtocol.shutdown();
    586             alertProtocol = null;
    587             handshakeProtocol.shutdown();
    588             handshakeProtocol = null;
    589             recordProtocol.shutdown();
    590             recordProtocol = null;
    591         }
    592         socket_was_closed = true;
    593     }
    594 
    595     /**
    596      * This method is called by SSLSocketInputStream class
    597      * when client application tries to read application data from
    598      * the stream, but there is no data in its underlying buffer.
    599      * @throws  IOException
    600      */
    601     protected void needAppData() throws IOException {
    602         if (!handshake_started) {
    603             startHandshake();
    604         }
    605         int type;
    606         if (logger != null) {
    607             logger.println("SSLSocket.needAppData..");
    608         }
    609         try {
    610             while(appDataIS.available() == 0) {
    611                 // read and unwrap the record contained in the transport
    612                 // input stream (SSLStreamedInput), pass it
    613                 // to appropriate client protocol (alert, handshake, or app)
    614                 // and retrieve the type of unwrapped data
    615                 switch (type = recordProtocol.unwrap()) {
    616                     case ContentType.HANDSHAKE:
    617                         if (!handshakeProtocol.getStatus().equals(
    618                                 SSLEngineResult.HandshakeStatus
    619                                 .NOT_HANDSHAKING)) {
    620                             // handshake protocol got addressed to it message
    621                             // and did not ignore it, so it's a rehandshake
    622                             doHandshake();
    623                         }
    624                         break;
    625                     case ContentType.ALERT:
    626                         processAlert();
    627                         if (socket_was_closed) {
    628                             return;
    629                         }
    630                         break;
    631                     case ContentType.APPLICATION_DATA:
    632                         if (logger != null) {
    633                             logger.println(
    634                                     "SSLSocket.needAppData: got the data");
    635                         }
    636                         break;
    637                     default:
    638                         // will throw exception
    639                         reportFatalAlert(AlertProtocol.UNEXPECTED_MESSAGE,
    640                                 new SSLException("Unexpected message of type "
    641                                     + type + " has been got"));
    642                 }
    643                 if (alertProtocol.hasAlert()) {
    644                     // warning alert occurred during wrap or unwrap
    645                     // (note: fatal alert causes AlertException
    646                     // to be thrown)
    647                     output.write(alertProtocol.wrap());
    648                     alertProtocol.setProcessed();
    649                 }
    650                 if (socket_was_closed) {
    651                     appDataIS.setEnd();
    652                     return;
    653                 }
    654             }
    655         } catch (AlertException e) {
    656             // will throw exception
    657             reportFatalAlert(e.getDescriptionCode(), e.getReason());
    658         } catch (EndOfSourceException e) {
    659             // end of socket's input stream has been reached
    660             appDataIS.setEnd();
    661         }
    662         if (logger != null) {
    663             logger.println("SSLSocket.needAppData: app data len: "
    664                     + appDataIS.available());
    665         }
    666     }
    667 
    668     /**
    669      * This method is called by SSLSocketOutputStream when a client application
    670      * tries to send the data over ssl protocol.
    671      */
    672     protected void writeAppData(byte[] data, int offset, int len) throws IOException {
    673         if (!handshake_started) {
    674             startHandshake();
    675         }
    676         if (logger != null) {
    677             logger.println("SSLSocket.writeAppData: " +
    678                     len + " " + SSLRecordProtocol.MAX_DATA_LENGTH);
    679             //logger.println(new String(data, offset, len));
    680         }
    681         try {
    682             if (len < SSLRecordProtocol.MAX_DATA_LENGTH) {
    683                 output.write(recordProtocol.wrap(ContentType.APPLICATION_DATA,
    684                             data, offset, len));
    685             } else {
    686                 while (len >= SSLRecordProtocol.MAX_DATA_LENGTH) {
    687                     output.write(recordProtocol.wrap(
    688                                 ContentType.APPLICATION_DATA, data, offset,
    689                                 SSLRecordProtocol.MAX_DATA_LENGTH));
    690                     offset += SSLRecordProtocol.MAX_DATA_LENGTH;
    691                     len -= SSLRecordProtocol.MAX_DATA_LENGTH;
    692                 }
    693                 if (len > 0) {
    694                     output.write(
    695                         recordProtocol.wrap(ContentType.APPLICATION_DATA,
    696                                             data, offset, len));
    697                 }
    698             }
    699         } catch (AlertException e) {
    700             // will throw exception
    701             reportFatalAlert(e.getDescriptionCode(), e.getReason());
    702         }
    703     }
    704 
    705     /*
    706      * Performs handshake process over this connection. The handshake
    707      * process is directed by the handshake status code provided by
    708      * handshake protocol. If this status is NEED_WRAP, method retrieves
    709      * handshake message from handshake protocol and sends it to another peer.
    710      * If this status is NEED_UNWRAP, method receives and processes handshake
    711      * message from another peer. Each of this stages (wrap/unwrap) change
    712      * the state of handshake protocol and this process is performed
    713      * until handshake status is FINISHED. After handshake process is finished
    714      * handshake completed event are sent to the registered listeners.
    715      * For more information about the handshake process see
    716      * TLS v1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 7.3.
    717      */
    718     private void doHandshake() throws IOException {
    719         SSLEngineResult.HandshakeStatus status;
    720         int type;
    721         try {
    722             while (!(status = handshakeProtocol.getStatus()).equals(
    723                         SSLEngineResult.HandshakeStatus.FINISHED)) {
    724                 if (logger != null) {
    725                     String s = (status.equals(
    726                                 SSLEngineResult.HandshakeStatus.NEED_WRAP))
    727                         ? "NEED_WRAP"
    728                         : (status.equals(
    729                                 SSLEngineResult.HandshakeStatus.NEED_UNWRAP))
    730                             ? "NEED_UNWRAP"
    731                             : "STATUS: OTHER!";
    732                     logger.println("SSLSocketImpl: HS status: "+s+" "+status);
    733                 }
    734                 if (status.equals(SSLEngineResult.HandshakeStatus.NEED_WRAP)) {
    735                     output.write(handshakeProtocol.wrap());
    736                 } else if (status.equals(
    737                             SSLEngineResult.HandshakeStatus.NEED_UNWRAP)) {
    738                     // read and unwrap the record contained in the transport
    739                     // input stream (SSLStreamedInput), pass it
    740                     // to appropriate client protocol (alert, handshake, or app)
    741                     // and retrieve the type of unwrapped data
    742                     switch (type = recordProtocol.unwrap()) {
    743                         case ContentType.HANDSHAKE:
    744                         case ContentType.CHANGE_CIPHER_SPEC:
    745                             break;
    746                         case ContentType.APPLICATION_DATA:
    747                             // So it's rehandshake and
    748                             // if app data buffer will be overloaded
    749                             // it will throw alert exception.
    750                             // Probably we should count the number of
    751                             // not handshaking data and make additional
    752                             // constraints (do not expect buffer overflow).
    753                             break;
    754                         case ContentType.ALERT:
    755                             processAlert();
    756                             if (socket_was_closed) {
    757                                 return;
    758                             }
    759                             break;
    760                         default:
    761                             // will throw exception
    762                             reportFatalAlert(AlertProtocol.UNEXPECTED_MESSAGE,
    763                                     new SSLException(
    764                                         "Unexpected message of type "
    765                                         + type + " has been got"));
    766                     }
    767                 } else {
    768                     // will throw exception
    769                     reportFatalAlert(AlertProtocol.INTERNAL_ERROR,
    770                         new SSLException(
    771                             "Handshake passed unexpected status: "+status));
    772                 }
    773                 if (alertProtocol.hasAlert()) {
    774                     // warning alert occurred during wrap or unwrap
    775                     // (note: fatal alert causes AlertException
    776                     // to be thrown)
    777                     output.write(alertProtocol.wrap());
    778                     alertProtocol.setProcessed();
    779                 }
    780             }
    781         } catch (EndOfSourceException e) {
    782             appDataIS.setEnd();
    783             throw new IOException("Connection was closed");
    784         } catch (AlertException e) {
    785             // will throw exception
    786             reportFatalAlert(e.getDescriptionCode(), e.getReason());
    787         }
    788 
    789         session = recordProtocol.getSession();
    790         if (listeners != null) {
    791             // notify the listeners
    792             HandshakeCompletedEvent event =
    793                 new HandshakeCompletedEvent(this, session);
    794             int size = listeners.size();
    795             for (int i=0; i<size; i++) {
    796                 listeners.get(i)
    797                     .handshakeCompleted(event);
    798             }
    799         }
    800     }
    801 
    802     /*
    803      * Process received alert message
    804      */
    805     private void processAlert() throws IOException {
    806         if (!alertProtocol.hasAlert()) {
    807             return;
    808         }
    809         if (alertProtocol.isFatalAlert()) {
    810             alertProtocol.setProcessed();
    811             String description = "Fatal alert received "
    812                 + alertProtocol.getAlertDescription();
    813             shutdown();
    814             throw new SSLException(description);
    815         }
    816 
    817         if (logger != null) {
    818             logger.println("Warning alert received: "
    819                 + alertProtocol.getAlertDescription());
    820         }
    821         switch(alertProtocol.getDescriptionCode()) {
    822             case AlertProtocol.CLOSE_NOTIFY:
    823                 alertProtocol.setProcessed();
    824                 appDataIS.setEnd();
    825                 close();
    826                 return;
    827             default:
    828                 alertProtocol.setProcessed();
    829             // TODO: process other warning messages
    830         }
    831     }
    832 
    833     /*
    834      * Sends fatal alert message and throws exception
    835      */
    836     private void reportFatalAlert(byte description_code,
    837             SSLException reason) throws IOException {
    838         alertProtocol.alert(AlertProtocol.FATAL, description_code);
    839         try {
    840             // the output stream can be closed
    841             output.write(alertProtocol.wrap());
    842         } catch (IOException ex) { }
    843         alertProtocol.setProcessed();
    844         shutdown();
    845         throw reason;
    846     }
    847 }
    848