OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of dart.io; | 5 part of dart.io; |
6 | 6 |
7 /** | 7 /** |
8 * A high-level class for communicating securely over a TCP socket, using | 8 * A high-level class for communicating securely over a TCP socket, using |
9 * TLS and SSL. The [SecureSocket] exposes both a [Stream] and an | 9 * TLS and SSL. The [SecureSocket] exposes both a [Stream] and an |
10 * [IOSink] interface, making it ideal for using together with | 10 * [IOSink] interface, making it ideal for using together with |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 /** | 143 /** |
144 * Get the peer certificate for a connected SecureSocket. If this | 144 * Get the peer certificate for a connected SecureSocket. If this |
145 * SecureSocket is the server end of a secure socket connection, | 145 * SecureSocket is the server end of a secure socket connection, |
146 * [peerCertificate] will return the client certificate, or null, if no | 146 * [peerCertificate] will return the client certificate, or null, if no |
147 * client certificate was received. If it is the client end, | 147 * client certificate was received. If it is the client end, |
148 * [peerCertificate] will return the server's certificate. | 148 * [peerCertificate] will return the server's certificate. |
149 */ | 149 */ |
150 X509Certificate get peerCertificate; | 150 X509Certificate get peerCertificate; |
151 | 151 |
152 /** | 152 /** |
153 * Initializes the NSS library. If [initialize] is not called, the library | 153 * Initializes the NSS library. If [initialize] is not called, the library |
154 * is automatically initialized as if [initialize] were called with no | 154 * is automatically initialized as if [initialize] were called with no |
155 * arguments. | 155 * arguments. If [initialize] is called more than once, or called after |
| 156 * automatic initialization has happened (when a secure connection is made), |
| 157 * then a TlsException is thrown. |
156 * | 158 * |
157 * The optional argument [database] is the path to a certificate database | 159 * The optional argument [database] is the path to a certificate database |
158 * directory containing root certificates for verifying certificate paths on | 160 * directory containing root certificates for verifying certificate paths on |
159 * client connections, and server certificates to provide on server | 161 * client connections, and server certificates to provide on server |
160 * connections. The argument [password] should be used when creating | 162 * connections. The argument [password] should be used when creating |
161 * secure server sockets, to allow the private key of the server | 163 * secure server sockets, to allow the private key of the server |
162 * certificate to be fetched. If [useBuiltinRoots] is true (the default), | 164 * certificate to be fetched. If [useBuiltinRoots] is true (the default), |
163 * then a built-in set of root certificates for trusted certificate | 165 * then a built-in set of root certificates for trusted certificate |
164 * authorities is merged with the certificates in the database. | 166 * authorities is merged with the certificates in the database. |
165 * The list of built-in root certificates, and documentation about this | 167 * The list of built-in root certificates, and documentation about this |
166 * default database, is available at | 168 * default database, is available at |
167 * http://www.mozilla.org/projects/security/certs/included/ . | 169 * http://www.mozilla.org/projects/security/certs/included/ . |
168 * | 170 * |
169 * If the [database] argument is omitted, then only the | 171 * If the [database] argument is omitted, then only the |
170 * builtin root certificates are used. If [useBuiltinRoots] is also false, | 172 * builtin root certificates are used. If [useBuiltinRoots] is also false, |
171 * then no certificates are available. | 173 * then no certificates are available. |
172 * | 174 * |
173 * Examples: | 175 * Examples: |
174 * 1) Use only the builtin root certificates: | 176 * 1) Use only the builtin root certificates: |
175 * SecureSocket.initialize(); or | 177 * SecureSocket.initialize(); or |
176 * | 178 * |
177 * 2) Use a specified database directory and the builtin roots: | 179 * 2) Use a specified database directory and the builtin roots: |
178 * SecureSocket.initialize(database: 'path/to/my/database', | 180 * SecureSocket.initialize(database: 'path/to/my/database', |
179 * password: 'my_password'); | 181 * password: 'my_password'); |
180 * | 182 * |
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
430 String certificateName, | 432 String certificateName, |
431 {bool is_server, | 433 {bool is_server, |
432 RawSocket socket, | 434 RawSocket socket, |
433 StreamSubscription subscription, | 435 StreamSubscription subscription, |
434 List<int> bufferedData, | 436 List<int> bufferedData, |
435 bool requestClientCertificate: false, | 437 bool requestClientCertificate: false, |
436 bool requireClientCertificate: false, | 438 bool requireClientCertificate: false, |
437 bool sendClientCertificate: false, | 439 bool sendClientCertificate: false, |
438 bool onBadCertificate(X509Certificate certificate)}) { | 440 bool onBadCertificate(X509Certificate certificate)}) { |
439 var future; | 441 var future; |
| 442 _verifyFields(host, requestedPort, certificateName, is_server, |
| 443 requestClientCertificate, requireClientCertificate, |
| 444 sendClientCertificate, onBadCertificate); |
440 if (host is String) { | 445 if (host is String) { |
441 if (socket != null) { | 446 if (socket != null) { |
442 future = new Future.value( | 447 future = new Future.value( |
443 (socket.address as dynamic)._cloneWithNewHost(host)); | 448 (socket.address as dynamic)._cloneWithNewHost(host)); |
444 } else { | 449 } else { |
445 future = InternetAddress.lookup(host).then((addrs) => addrs.first); | 450 future = InternetAddress.lookup(host).then((addrs) => addrs.first); |
446 } | 451 } |
447 } else { | 452 } else { |
448 future = new Future.value(host); | 453 future = new Future.value(host); |
449 } | 454 } |
(...skipping 27 matching lines...) Expand all Loading... |
477 bool this.onBadCertificate(X509Certificate certificate)) { | 482 bool this.onBadCertificate(X509Certificate certificate)) { |
478 _controller = new StreamController<RawSocketEvent>( | 483 _controller = new StreamController<RawSocketEvent>( |
479 sync: true, | 484 sync: true, |
480 onListen: _onSubscriptionStateChange, | 485 onListen: _onSubscriptionStateChange, |
481 onPause: _onPauseStateChange, | 486 onPause: _onPauseStateChange, |
482 onResume: _onPauseStateChange, | 487 onResume: _onPauseStateChange, |
483 onCancel: _onSubscriptionStateChange); | 488 onCancel: _onSubscriptionStateChange); |
484 _stream = _controller.stream; | 489 _stream = _controller.stream; |
485 // Throw an ArgumentError if any field is invalid. After this, all | 490 // Throw an ArgumentError if any field is invalid. After this, all |
486 // errors will be reported through the future or the stream. | 491 // errors will be reported through the future or the stream. |
487 _verifyFields(); | |
488 _secureFilter.init(); | 492 _secureFilter.init(); |
489 _filterPointer = _secureFilter._pointer(); | 493 _filterPointer = _secureFilter._pointer(); |
490 _secureFilter.registerHandshakeCompleteCallback( | 494 _secureFilter.registerHandshakeCompleteCallback( |
491 _secureHandshakeCompleteHandler); | 495 _secureHandshakeCompleteHandler); |
492 if (onBadCertificate != null) { | 496 if (onBadCertificate != null) { |
493 _secureFilter.registerBadCertificateCallback(onBadCertificate); | 497 _secureFilter.registerBadCertificateCallback(onBadCertificate); |
494 } | 498 } |
495 var futureSocket; | 499 var futureSocket; |
496 if (socket == null) { | 500 if (socket == null) { |
497 futureSocket = RawSocket.connect(address, requestedPort); | 501 futureSocket = RawSocket.connect(address, requestedPort); |
498 } else { | 502 } else { |
499 futureSocket = new Future.value(socket); | 503 futureSocket = new Future.value(socket); |
500 } | 504 } |
501 futureSocket.then((rawSocket) { | 505 futureSocket.then((rawSocket) { |
| 506 _connectPending = true; |
502 _socket = rawSocket; | 507 _socket = rawSocket; |
503 _socket.readEventsEnabled = true; | 508 _socket.readEventsEnabled = true; |
504 _socket.writeEventsEnabled = false; | 509 _socket.writeEventsEnabled = false; |
505 if (_socketSubscription == null) { | 510 if (_socketSubscription == null) { |
506 // If a current subscription is provided use this otherwise | 511 // If a current subscription is provided use this otherwise |
507 // create a new one. | 512 // create a new one. |
508 _socketSubscription = _socket.listen(_eventDispatcher, | 513 _socketSubscription = _socket.listen(_eventDispatcher, |
509 onError: _errorHandler, | 514 onError: _reportError, |
510 onDone: _doneHandler); | 515 onDone: _doneHandler); |
511 } else { | 516 } else { |
512 _socketSubscription.onData(_eventDispatcher); | 517 _socketSubscription.onData(_eventDispatcher); |
513 _socketSubscription.onError(_errorHandler); | 518 _socketSubscription.onError(_reportError); |
514 _socketSubscription.onDone(_doneHandler); | 519 _socketSubscription.onDone(_doneHandler); |
515 } | 520 } |
516 _connectPending = true; | |
517 _secureFilter.connect(address.host, | 521 _secureFilter.connect(address.host, |
518 (address as dynamic)._sockaddr_storage, | 522 (address as dynamic)._sockaddr_storage, |
519 port, | 523 port, |
520 is_server, | 524 is_server, |
521 certificateName, | 525 certificateName, |
522 requestClientCertificate || | 526 requestClientCertificate || |
523 requireClientCertificate, | 527 requireClientCertificate, |
524 requireClientCertificate, | 528 requireClientCertificate, |
525 sendClientCertificate); | 529 sendClientCertificate); |
526 _secureHandshake(); | 530 _secureHandshake(); |
527 }) | 531 }) |
528 .catchError((error) { | 532 .catchError(_reportError); |
529 _handshakeComplete.completeError(error); | |
530 _close(); | |
531 }); | |
532 } | 533 } |
533 | 534 |
534 StreamSubscription listen(void onData(RawSocketEvent data), | 535 StreamSubscription listen(void onData(RawSocketEvent data), |
535 {void onError(error), | 536 {void onError(error), |
536 void onDone(), | 537 void onDone(), |
537 bool cancelOnError}) { | 538 bool cancelOnError}) { |
538 _sendWriteEvent(); | 539 _sendWriteEvent(); |
539 return _stream.listen(onData, | 540 return _stream.listen(onData, |
540 onError: onError, | 541 onError: onError, |
541 onDone: onDone, | 542 onDone: onDone, |
542 cancelOnError: cancelOnError); | 543 cancelOnError: cancelOnError); |
543 } | 544 } |
544 | 545 |
545 void _verifyFields() { | 546 static void _verifyFields(host, |
546 assert(is_server is bool); | 547 int requestedPort, |
547 assert(_socket == null || _socket is RawSocket); | 548 String certificateName, |
548 if (address is! InternetAddress) { | 549 bool is_server, |
549 throw new ArgumentError( | 550 bool requestClientCertificate, |
550 "RawSecureSocket constructor: host is not an InternetAddress"); | 551 bool requireClientCertificate, |
| 552 bool sendClientCertificate, |
| 553 Function onBadCertificate) { |
| 554 if (host is! String && host is! InternetAddress) { |
| 555 throw new ArgumentError("host is not a String or an InternetAddress"); |
| 556 } |
| 557 if (requestedPort is! int) { |
| 558 throw new ArgumentError("requestedPort is not an int"); |
| 559 } |
| 560 if (requestedPort < 0 || requestedPort > 65535) { |
| 561 throw new ArgumentError("requestedPort is not in the range 0..65535"); |
551 } | 562 } |
552 if (certificateName != null && certificateName is! String) { | 563 if (certificateName != null && certificateName is! String) { |
553 throw new ArgumentError("certificateName is not null or a String"); | 564 throw new ArgumentError("certificateName is not null or a String"); |
554 } | 565 } |
555 if (certificateName == null && is_server) { | 566 if (certificateName == null && is_server) { |
556 throw new ArgumentError("certificateName is null on a server"); | 567 throw new ArgumentError("certificateName is null on a server"); |
557 } | 568 } |
558 if (requestClientCertificate is! bool) { | 569 if (requestClientCertificate is! bool) { |
559 throw new ArgumentError("requestClientCertificate is not a bool"); | 570 throw new ArgumentError("requestClientCertificate is not a bool"); |
560 } | 571 } |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
686 } | 697 } |
687 | 698 |
688 X509Certificate get peerCertificate => _secureFilter.peerCertificate; | 699 X509Certificate get peerCertificate => _secureFilter.peerCertificate; |
689 | 700 |
690 bool setOption(SocketOption option, bool enabled) { | 701 bool setOption(SocketOption option, bool enabled) { |
691 if (_socket == null) return false; | 702 if (_socket == null) return false; |
692 return _socket.setOption(option, enabled); | 703 return _socket.setOption(option, enabled); |
693 } | 704 } |
694 | 705 |
695 void _eventDispatcher(RawSocketEvent event) { | 706 void _eventDispatcher(RawSocketEvent event) { |
696 if (event == RawSocketEvent.READ) { | 707 try { |
697 _readHandler(); | 708 if (event == RawSocketEvent.READ) { |
698 } else if (event == RawSocketEvent.WRITE) { | 709 _readHandler(); |
699 _writeHandler(); | 710 } else if (event == RawSocketEvent.WRITE) { |
700 } else if (event == RawSocketEvent.READ_CLOSED) { | 711 _writeHandler(); |
701 _closeHandler(); | 712 } else if (event == RawSocketEvent.READ_CLOSED) { |
| 713 _closeHandler(); |
| 714 } |
| 715 } catch (e) { |
| 716 _reportError(e); |
702 } | 717 } |
703 } | 718 } |
704 | 719 |
705 void _readHandler() { | 720 void _readHandler() { |
706 _readSocket(); | 721 _readSocket(); |
707 _scheduleFilter(); | 722 _scheduleFilter(); |
708 } | 723 } |
709 | 724 |
710 void _writeHandler() { | 725 void _writeHandler() { |
711 _writeSocket(); | 726 _writeSocket(); |
712 _scheduleFilter(); | 727 _scheduleFilter(); |
713 } | 728 } |
714 | 729 |
715 void _doneHandler() { | 730 void _doneHandler() { |
716 if (_filterStatus.readEmpty) { | 731 if (_filterStatus.readEmpty) { |
717 _close(); | 732 _close(); |
718 } | 733 } |
719 } | 734 } |
720 | 735 |
721 void _errorHandler(e) { | 736 void _reportError(e) { |
722 _reportError(e, 'Error on underlying RawSocket'); | |
723 } | |
724 | |
725 void _reportError(e, String message) { | |
726 // TODO(whesse): Call _reportError from all internal functions that throw. | |
727 if (e is SocketException) { | |
728 e = new SocketException('$message (${e.message})', e.osError); | |
729 } else if (e is OSError) { | |
730 e = new SocketException(message, e); | |
731 } else { | |
732 e = new SocketException('$message (${e.toString()})', null); | |
733 } | |
734 if (_connectPending) { | 737 if (_connectPending) { |
| 738 // _connectPending is true after the underlying connection has been |
| 739 // made, but before the handshake has completed. |
| 740 if (e is! TlsException) { |
| 741 e = new HandshakeException("$e", null); |
| 742 } |
735 _handshakeComplete.completeError(e); | 743 _handshakeComplete.completeError(e); |
736 } else { | 744 } else { |
737 _controller.addError(e); | 745 _controller.addError(e); |
738 } | 746 } |
739 _close(); | 747 _close(); |
740 } | 748 } |
741 | 749 |
742 void _closeHandler() { | 750 void _closeHandler() { |
743 if (_status == CONNECTED) { | 751 if (_status == CONNECTED) { |
744 if (_closedRead) return; | 752 if (_closedRead) return; |
745 _socketClosedRead = true; | 753 _socketClosedRead = true; |
746 if (_filterStatus.readEmpty) { | 754 if (_filterStatus.readEmpty) { |
747 _closedRead = true; | 755 _closedRead = true; |
748 _controller.add(RawSocketEvent.READ_CLOSED); | 756 _controller.add(RawSocketEvent.READ_CLOSED); |
749 if (_socketClosedWrite) { | 757 if (_socketClosedWrite) { |
750 _close(); | 758 _close(); |
751 } | 759 } |
752 } else { | 760 } else { |
753 _scheduleFilter(); | 761 _scheduleFilter(); |
754 } | 762 } |
755 } else if (_status == HANDSHAKE) { | 763 } else if (_status == HANDSHAKE) { |
756 _socketClosedRead = true; | 764 _socketClosedRead = true; |
757 if (_filterStatus.readEmpty) { | 765 if (_filterStatus.readEmpty) { |
758 _reportError( | 766 _reportError( |
759 new SocketException('Connection terminated during handshake'), | 767 new HandshakeException('Connection terminated during handshake')); |
760 'RawSecureSocket error'); | |
761 } else { | 768 } else { |
762 _secureHandshake(); | 769 _secureHandshake(); |
763 } | 770 } |
764 } | 771 } |
765 } | 772 } |
766 | 773 |
767 void _secureHandshake() { | 774 void _secureHandshake() { |
768 try { | 775 try { |
769 _secureFilter.handshake(); | 776 _secureFilter.handshake(); |
770 _filterStatus.writeEmpty = false; | 777 _filterStatus.writeEmpty = false; |
771 _readSocket(); | 778 _readSocket(); |
772 _writeSocket(); | 779 _writeSocket(); |
773 _scheduleFilter(); | 780 _scheduleFilter(); |
774 } catch (e) { | 781 } catch (e) { |
775 _reportError(e, "RawSecureSocket error"); | 782 _reportError(e); |
776 } | 783 } |
777 } | 784 } |
778 | 785 |
779 void _secureHandshakeCompleteHandler() { | 786 void _secureHandshakeCompleteHandler() { |
780 _status = CONNECTED; | 787 _status = CONNECTED; |
781 if (_connectPending) { | 788 if (_connectPending) { |
782 _connectPending = false; | 789 _connectPending = false; |
783 // We don't want user code to run synchronously in this callback. | 790 // We don't want user code to run synchronously in this callback. |
784 Timer.run(() => _handshakeComplete.complete(this)); | 791 Timer.run(() => _handshakeComplete.complete(this)); |
785 } | 792 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
831 } | 838 } |
832 if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) { | 839 if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) { |
833 // Checks for and handles all cases of partially closed sockets. | 840 // Checks for and handles all cases of partially closed sockets. |
834 shutdown(SocketDirection.SEND); | 841 shutdown(SocketDirection.SEND); |
835 if (_status == CLOSED) return; | 842 if (_status == CLOSED) return; |
836 } | 843 } |
837 if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) { | 844 if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) { |
838 if (_status == HANDSHAKE) { | 845 if (_status == HANDSHAKE) { |
839 _secureFilter.handshake(); | 846 _secureFilter.handshake(); |
840 if (_status == HANDSHAKE) { | 847 if (_status == HANDSHAKE) { |
841 _reportError( | 848 throw new HandshakeException( |
842 new SocketException('Connection terminated during handshake'), | 849 'Connection terminated during handshake'); |
843 'RawSecureSocket error'); | |
844 } | 850 } |
845 } | 851 } |
846 _closeHandler(); | 852 _closeHandler(); |
847 } | 853 } |
848 if (_status == CLOSED) return; | 854 if (_status == CLOSED) return; |
849 if (_filterStatus.progress) { | 855 if (_filterStatus.progress) { |
850 _filterPending = true; | 856 _filterPending = true; |
851 if (_filterStatus.writePlaintextNoLongerFull) _sendWriteEvent(); | 857 if (_filterStatus.writePlaintextNoLongerFull) _sendWriteEvent(); |
852 if (_filterStatus.readEncryptedNoLongerFull) _readSocket(); | 858 if (_filterStatus.readEncryptedNoLongerFull) _readSocket(); |
853 if (_filterStatus.writeEncryptedNoLongerEmpty) _writeSocket(); | 859 if (_filterStatus.writeEncryptedNoLongerEmpty) _writeSocket(); |
854 if (_filterStatus.readPlaintextNoLongerEmpty) _scheduleReadEvent(); | 860 if (_filterStatus.readPlaintextNoLongerEmpty) _scheduleReadEvent(); |
855 if (_status == HANDSHAKE) _secureHandshake(); | 861 if (_status == HANDSHAKE) _secureHandshake(); |
856 } | 862 } |
857 _tryFilter(); | 863 _tryFilter(); |
858 }); | 864 }).catchError(_reportError); |
859 } | 865 } |
860 } | 866 } |
861 | 867 |
862 List<int> _readSocketOrBufferedData(int bytes) { | 868 List<int> _readSocketOrBufferedData(int bytes) { |
863 if (_bufferedData != null) { | 869 if (_bufferedData != null) { |
864 if (bytes > _bufferedData.length - _bufferedDataIndex) { | 870 if (bytes > _bufferedData.length - _bufferedDataIndex) { |
865 bytes = _bufferedData.length - _bufferedDataIndex; | 871 bytes = _bufferedData.length - _bufferedDataIndex; |
866 } | 872 } |
867 var result = _bufferedData.sublist(_bufferedDataIndex, | 873 var result = _bufferedData.sublist(_bufferedDataIndex, |
868 _bufferedDataIndex + bytes); | 874 _bufferedDataIndex + bytes); |
869 _bufferedDataIndex += bytes; | 875 _bufferedDataIndex += bytes; |
870 if (_bufferedData.length == _bufferedDataIndex) { | 876 if (_bufferedData.length == _bufferedDataIndex) { |
871 _bufferedData = null; | 877 _bufferedData = null; |
872 } | 878 } |
873 return result; | 879 return result; |
874 } else if (!_socketClosedRead) { | 880 } else if (!_socketClosedRead) { |
875 try { | 881 return _socket.read(bytes); |
876 return _socket.read(bytes); | |
877 } catch (e) { | |
878 _reportError(e, "RawSecureSocket error reading encrypted socket"); | |
879 return null; | |
880 } | |
881 } else { | 882 } else { |
882 return null; | 883 return null; |
883 } | 884 } |
884 } | 885 } |
885 | 886 |
886 void _readSocket() { | 887 void _readSocket() { |
887 if (_status == CLOSED) return; | 888 if (_status == CLOSED) return; |
888 var buffer = _secureFilter.buffers[READ_ENCRYPTED]; | 889 var buffer = _secureFilter.buffers[READ_ENCRYPTED]; |
889 if (buffer.writeFromSource(_readSocketOrBufferedData) > 0) { | 890 if (buffer.writeFromSource(_readSocketOrBufferedData) > 0) { |
890 _filterStatus.readEmpty = false; | 891 _filterStatus.readEmpty = false; |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1154 void handshake(); | 1155 void handshake(); |
1155 void init(); | 1156 void init(); |
1156 X509Certificate get peerCertificate; | 1157 X509Certificate get peerCertificate; |
1157 int processBuffer(int bufferIndex); | 1158 int processBuffer(int bufferIndex); |
1158 void registerBadCertificateCallback(Function callback); | 1159 void registerBadCertificateCallback(Function callback); |
1159 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); | 1160 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); |
1160 int _pointer(); | 1161 int _pointer(); |
1161 | 1162 |
1162 List<_ExternalBuffer> get buffers; | 1163 List<_ExternalBuffer> get buffers; |
1163 } | 1164 } |
| 1165 |
| 1166 /** A secure networking exception caused by a failure in the |
| 1167 * TLS/SSL protocol. |
| 1168 */ |
| 1169 class TlsException implements IOException { |
| 1170 final String type; |
| 1171 final String message; |
| 1172 final OSError osError; |
| 1173 |
| 1174 const TlsException([String message = "", |
| 1175 OSError osError = null]) |
| 1176 : this._("TlsException", message, osError); |
| 1177 |
| 1178 const TlsException._(String this.type, |
| 1179 String this.message, |
| 1180 OSError this.osError); |
| 1181 |
| 1182 String toString() { |
| 1183 StringBuffer sb = new StringBuffer(); |
| 1184 sb.write(type); |
| 1185 if (!message.isEmpty) { |
| 1186 sb.write(": $message"); |
| 1187 if (osError != null) { |
| 1188 sb.write(" ($osError)"); |
| 1189 } |
| 1190 } else if (osError != null) { |
| 1191 sb.write(": $osError"); |
| 1192 } |
| 1193 return sb.toString(); |
| 1194 } |
| 1195 } |
| 1196 |
| 1197 |
| 1198 /** |
| 1199 * An exception that happens in the handshake phase of establishing |
| 1200 * a secure network connection. |
| 1201 */ |
| 1202 class HandshakeException extends TlsException { |
| 1203 const HandshakeException([String message = "", |
| 1204 OSError osError = null]) |
| 1205 : super._("HandshakeException", message, osError); |
| 1206 } |
| 1207 |
| 1208 |
| 1209 /** |
| 1210 * An exception that happens in the handshake phase of establishing |
| 1211 * a secure network connection, when looking up or verifying a |
| 1212 * certificate. |
| 1213 */ |
| 1214 class CertificateException extends TlsException { |
| 1215 const CertificateException([String message = "", |
| 1216 OSError osError = null]) |
| 1217 : super._("CertificateException", message, osError); |
| 1218 } |
OLD | NEW |