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