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 419 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
430 String certificateName, | 430 String certificateName, |
431 {bool is_server, | 431 {bool is_server, |
432 RawSocket socket, | 432 RawSocket socket, |
433 StreamSubscription subscription, | 433 StreamSubscription subscription, |
434 List<int> bufferedData, | 434 List<int> bufferedData, |
435 bool requestClientCertificate: false, | 435 bool requestClientCertificate: false, |
436 bool requireClientCertificate: false, | 436 bool requireClientCertificate: false, |
437 bool sendClientCertificate: false, | 437 bool sendClientCertificate: false, |
438 bool onBadCertificate(X509Certificate certificate)}) { | 438 bool onBadCertificate(X509Certificate certificate)}) { |
439 var future; | 439 var future; |
440 _verifyFields(host, requestedPort, certificateName, is_server, | |
441 requestClientCertificate, requireClientCertificate, | |
442 sendClientCertificate, onBadCertificate); | |
440 if (host is String) { | 443 if (host is String) { |
441 if (socket != null) { | 444 if (socket != null) { |
442 future = new Future.value( | 445 future = new Future.value( |
443 (socket.address as dynamic)._cloneWithNewHost(host)); | 446 (socket.address as dynamic)._cloneWithNewHost(host)); |
444 } else { | 447 } else { |
445 future = InternetAddress.lookup(host).then((addrs) => addrs.first); | 448 future = InternetAddress.lookup(host).then((addrs) => addrs.first); |
446 } | 449 } |
447 } else { | 450 } else { |
448 future = new Future.value(host); | 451 future = new Future.value(host); |
449 } | 452 } |
(...skipping 27 matching lines...) Expand all Loading... | |
477 bool this.onBadCertificate(X509Certificate certificate)) { | 480 bool this.onBadCertificate(X509Certificate certificate)) { |
478 _controller = new StreamController<RawSocketEvent>( | 481 _controller = new StreamController<RawSocketEvent>( |
479 sync: true, | 482 sync: true, |
480 onListen: _onSubscriptionStateChange, | 483 onListen: _onSubscriptionStateChange, |
481 onPause: _onPauseStateChange, | 484 onPause: _onPauseStateChange, |
482 onResume: _onPauseStateChange, | 485 onResume: _onPauseStateChange, |
483 onCancel: _onSubscriptionStateChange); | 486 onCancel: _onSubscriptionStateChange); |
484 _stream = _controller.stream; | 487 _stream = _controller.stream; |
485 // Throw an ArgumentError if any field is invalid. After this, all | 488 // Throw an ArgumentError if any field is invalid. After this, all |
486 // errors will be reported through the future or the stream. | 489 // errors will be reported through the future or the stream. |
487 _verifyFields(); | |
488 _secureFilter.init(); | 490 _secureFilter.init(); |
489 _filterPointer = _secureFilter._pointer(); | 491 _filterPointer = _secureFilter._pointer(); |
490 _secureFilter.registerHandshakeCompleteCallback( | 492 _secureFilter.registerHandshakeCompleteCallback( |
491 _secureHandshakeCompleteHandler); | 493 _secureHandshakeCompleteHandler); |
492 if (onBadCertificate != null) { | 494 if (onBadCertificate != null) { |
493 _secureFilter.registerBadCertificateCallback(onBadCertificate); | 495 _secureFilter.registerBadCertificateCallback(onBadCertificate); |
494 } | 496 } |
495 var futureSocket; | 497 var futureSocket; |
496 if (socket == null) { | 498 if (socket == null) { |
497 futureSocket = RawSocket.connect(address, requestedPort); | 499 futureSocket = RawSocket.connect(address, requestedPort); |
498 } else { | 500 } else { |
499 futureSocket = new Future.value(socket); | 501 futureSocket = new Future.value(socket); |
500 } | 502 } |
501 futureSocket.then((rawSocket) { | 503 futureSocket.then((rawSocket) { |
504 _connectPending = true; | |
502 _socket = rawSocket; | 505 _socket = rawSocket; |
503 _socket.readEventsEnabled = true; | 506 _socket.readEventsEnabled = true; |
504 _socket.writeEventsEnabled = false; | 507 _socket.writeEventsEnabled = false; |
505 if (_socketSubscription == null) { | 508 if (_socketSubscription == null) { |
506 // If a current subscription is provided use this otherwise | 509 // If a current subscription is provided use this otherwise |
507 // create a new one. | 510 // create a new one. |
508 _socketSubscription = _socket.listen(_eventDispatcher, | 511 _socketSubscription = _socket.listen(_eventDispatcher, |
509 onError: _errorHandler, | 512 onError: _reportError, |
510 onDone: _doneHandler); | 513 onDone: _doneHandler); |
511 } else { | 514 } else { |
512 _socketSubscription.onData(_eventDispatcher); | 515 _socketSubscription.onData(_eventDispatcher); |
513 _socketSubscription.onError(_errorHandler); | 516 _socketSubscription.onError(_reportError); |
514 _socketSubscription.onDone(_doneHandler); | 517 _socketSubscription.onDone(_doneHandler); |
515 } | 518 } |
516 _connectPending = true; | |
517 _secureFilter.connect(address.host, | 519 _secureFilter.connect(address.host, |
518 (address as dynamic)._sockaddr_storage, | 520 (address as dynamic)._sockaddr_storage, |
519 port, | 521 port, |
520 is_server, | 522 is_server, |
521 certificateName, | 523 certificateName, |
522 requestClientCertificate || | 524 requestClientCertificate || |
523 requireClientCertificate, | 525 requireClientCertificate, |
524 requireClientCertificate, | 526 requireClientCertificate, |
525 sendClientCertificate); | 527 sendClientCertificate); |
526 _secureHandshake(); | 528 _secureHandshake(); |
527 }) | 529 }) |
528 .catchError((error) { | 530 .catchError(_reportError); |
529 _handshakeComplete.completeError(error); | |
530 _close(); | |
531 }); | |
532 } | 531 } |
533 | 532 |
534 StreamSubscription listen(void onData(RawSocketEvent data), | 533 StreamSubscription listen(void onData(RawSocketEvent data), |
535 {void onError(error), | 534 {void onError(error), |
536 void onDone(), | 535 void onDone(), |
537 bool cancelOnError}) { | 536 bool cancelOnError}) { |
538 _sendWriteEvent(); | 537 _sendWriteEvent(); |
539 return _stream.listen(onData, | 538 return _stream.listen(onData, |
540 onError: onError, | 539 onError: onError, |
541 onDone: onDone, | 540 onDone: onDone, |
542 cancelOnError: cancelOnError); | 541 cancelOnError: cancelOnError); |
543 } | 542 } |
544 | 543 |
545 void _verifyFields() { | 544 static void _verifyFields(host, |
Søren Gjesse
2013/06/25 06:35:49
Indentation.
Bill Hesse
2013/06/25 12:41:14
Done.
| |
546 assert(is_server is bool); | 545 int requestedPort, |
547 assert(_socket == null || _socket is RawSocket); | 546 String certificateName, |
548 if (address is! InternetAddress) { | 547 bool is_server, |
549 throw new ArgumentError( | 548 bool requestClientCertificate, |
550 "RawSecureSocket constructor: host is not an InternetAddress"); | 549 bool requireClientCertificate, |
550 bool sendClientCertificate, | |
551 Function onBadCertificate) { | |
552 if (host is! String && host is! InternetAddress) { | |
553 throw new ArgumentError("host is not a String or an InternetAddress"); | |
554 } | |
555 if (requestedPort is! int) { | |
556 throw new ArgumentError("requestedPort is not an int"); | |
557 } | |
558 if (requestedPort < 0 || requestedPort > 65535) { | |
559 throw new ArgumentError("requestedPort is not in the range 0..65535"); | |
551 } | 560 } |
552 if (certificateName != null && certificateName is! String) { | 561 if (certificateName != null && certificateName is! String) { |
553 throw new ArgumentError("certificateName is not null or a String"); | 562 throw new ArgumentError("certificateName is not null or a String"); |
554 } | 563 } |
555 if (certificateName == null && is_server) { | 564 if (certificateName == null && is_server) { |
556 throw new ArgumentError("certificateName is null on a server"); | 565 throw new ArgumentError("certificateName is null on a server"); |
557 } | 566 } |
558 if (requestClientCertificate is! bool) { | 567 if (requestClientCertificate is! bool) { |
559 throw new ArgumentError("requestClientCertificate is not a bool"); | 568 throw new ArgumentError("requestClientCertificate is not a bool"); |
560 } | 569 } |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
686 } | 695 } |
687 | 696 |
688 X509Certificate get peerCertificate => _secureFilter.peerCertificate; | 697 X509Certificate get peerCertificate => _secureFilter.peerCertificate; |
689 | 698 |
690 bool setOption(SocketOption option, bool enabled) { | 699 bool setOption(SocketOption option, bool enabled) { |
691 if (_socket == null) return false; | 700 if (_socket == null) return false; |
692 return _socket.setOption(option, enabled); | 701 return _socket.setOption(option, enabled); |
693 } | 702 } |
694 | 703 |
695 void _eventDispatcher(RawSocketEvent event) { | 704 void _eventDispatcher(RawSocketEvent event) { |
696 if (event == RawSocketEvent.READ) { | 705 try { |
697 _readHandler(); | 706 if (event == RawSocketEvent.READ) { |
698 } else if (event == RawSocketEvent.WRITE) { | 707 _readHandler(); |
699 _writeHandler(); | 708 } else if (event == RawSocketEvent.WRITE) { |
700 } else if (event == RawSocketEvent.READ_CLOSED) { | 709 _writeHandler(); |
701 _closeHandler(); | 710 } else if (event == RawSocketEvent.READ_CLOSED) { |
711 _closeHandler(); | |
712 } | |
713 } catch (e) { | |
714 _reportError(e); | |
702 } | 715 } |
703 } | 716 } |
704 | 717 |
705 void _readHandler() { | 718 void _readHandler() { |
706 _readSocket(); | 719 _readSocket(); |
707 _scheduleFilter(); | 720 _scheduleFilter(); |
708 } | 721 } |
709 | 722 |
710 void _writeHandler() { | 723 void _writeHandler() { |
711 _writeSocket(); | 724 _writeSocket(); |
712 _scheduleFilter(); | 725 _scheduleFilter(); |
713 } | 726 } |
714 | 727 |
715 void _doneHandler() { | 728 void _doneHandler() { |
716 if (_filterStatus.readEmpty) { | 729 if (_filterStatus.readEmpty) { |
717 _close(); | 730 _close(); |
718 } | 731 } |
719 } | 732 } |
720 | 733 |
721 void _errorHandler(e) { | 734 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) { | 735 if (_connectPending) { |
736 // _connectPending is true after the underlying connection has been | |
737 // made, but before the handshake has completed. | |
738 if (e is! TlsException) { | |
739 e = new HandshakeException("$e", null); | |
740 } | |
735 _handshakeComplete.completeError(e); | 741 _handshakeComplete.completeError(e); |
736 } else { | 742 } else { |
737 _controller.addError(e); | 743 _controller.addError(e); |
738 } | 744 } |
739 _close(); | 745 _close(); |
740 } | 746 } |
741 | 747 |
742 void _closeHandler() { | 748 void _closeHandler() { |
743 if (_status == CONNECTED) { | 749 if (_status == CONNECTED) { |
744 if (_closedRead) return; | 750 if (_closedRead) return; |
745 _socketClosedRead = true; | 751 _socketClosedRead = true; |
746 if (_filterStatus.readEmpty) { | 752 if (_filterStatus.readEmpty) { |
747 _closedRead = true; | 753 _closedRead = true; |
748 _controller.add(RawSocketEvent.READ_CLOSED); | 754 _controller.add(RawSocketEvent.READ_CLOSED); |
749 if (_socketClosedWrite) { | 755 if (_socketClosedWrite) { |
750 _close(); | 756 _close(); |
751 } | 757 } |
752 } else { | 758 } else { |
753 _scheduleFilter(); | 759 _scheduleFilter(); |
754 } | 760 } |
755 } else if (_status == HANDSHAKE) { | 761 } else if (_status == HANDSHAKE) { |
756 _socketClosedRead = true; | 762 _socketClosedRead = true; |
757 if (_filterStatus.readEmpty) { | 763 if (_filterStatus.readEmpty) { |
758 _reportError( | 764 _reportError( |
759 new SocketException('Connection terminated during handshake'), | 765 new HandshakeException('Connection terminated during handshake')); |
760 'RawSecureSocket error'); | |
761 } else { | 766 } else { |
762 _secureHandshake(); | 767 _secureHandshake(); |
763 } | 768 } |
764 } | 769 } |
765 } | 770 } |
766 | 771 |
767 void _secureHandshake() { | 772 void _secureHandshake() { |
768 try { | 773 try { |
769 _secureFilter.handshake(); | 774 _secureFilter.handshake(); |
770 _filterStatus.writeEmpty = false; | 775 _filterStatus.writeEmpty = false; |
771 _readSocket(); | 776 _readSocket(); |
772 _writeSocket(); | 777 _writeSocket(); |
773 _scheduleFilter(); | 778 _scheduleFilter(); |
774 } catch (e) { | 779 } catch (e) { |
775 _reportError(e, "RawSecureSocket error"); | 780 _reportError(e); |
776 } | 781 } |
777 } | 782 } |
778 | 783 |
779 void _secureHandshakeCompleteHandler() { | 784 void _secureHandshakeCompleteHandler() { |
780 _status = CONNECTED; | 785 _status = CONNECTED; |
781 if (_connectPending) { | 786 if (_connectPending) { |
782 _connectPending = false; | 787 _connectPending = false; |
783 // We don't want user code to run synchronously in this callback. | 788 // We don't want user code to run synchronously in this callback. |
784 Timer.run(() => _handshakeComplete.complete(this)); | 789 Timer.run(() => _handshakeComplete.complete(this)); |
785 } | 790 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
831 } | 836 } |
832 if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) { | 837 if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) { |
833 // Checks for and handles all cases of partially closed sockets. | 838 // Checks for and handles all cases of partially closed sockets. |
834 shutdown(SocketDirection.SEND); | 839 shutdown(SocketDirection.SEND); |
835 if (_status == CLOSED) return; | 840 if (_status == CLOSED) return; |
836 } | 841 } |
837 if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) { | 842 if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) { |
838 if (_status == HANDSHAKE) { | 843 if (_status == HANDSHAKE) { |
839 _secureFilter.handshake(); | 844 _secureFilter.handshake(); |
840 if (_status == HANDSHAKE) { | 845 if (_status == HANDSHAKE) { |
841 _reportError( | 846 throw new HandshakeException( |
842 new SocketException('Connection terminated during handshake'), | 847 'Connection terminated during handshake'); |
843 'RawSecureSocket error'); | |
844 } | 848 } |
845 } | 849 } |
846 _closeHandler(); | 850 _closeHandler(); |
847 } | 851 } |
848 if (_status == CLOSED) return; | 852 if (_status == CLOSED) return; |
849 if (_filterStatus.progress) { | 853 if (_filterStatus.progress) { |
850 _filterPending = true; | 854 _filterPending = true; |
851 if (_filterStatus.writePlaintextNoLongerFull) _sendWriteEvent(); | 855 if (_filterStatus.writePlaintextNoLongerFull) _sendWriteEvent(); |
852 if (_filterStatus.readEncryptedNoLongerFull) _readSocket(); | 856 if (_filterStatus.readEncryptedNoLongerFull) _readSocket(); |
853 if (_filterStatus.writeEncryptedNoLongerEmpty) _writeSocket(); | 857 if (_filterStatus.writeEncryptedNoLongerEmpty) _writeSocket(); |
854 if (_filterStatus.readPlaintextNoLongerEmpty) _scheduleReadEvent(); | 858 if (_filterStatus.readPlaintextNoLongerEmpty) _scheduleReadEvent(); |
855 if (_status == HANDSHAKE) _secureHandshake(); | 859 if (_status == HANDSHAKE) _secureHandshake(); |
856 } | 860 } |
857 _tryFilter(); | 861 _tryFilter(); |
858 }); | 862 }).catchError(_reportError); |
859 } | 863 } |
860 } | 864 } |
861 | 865 |
862 List<int> _readSocketOrBufferedData(int bytes) { | 866 List<int> _readSocketOrBufferedData(int bytes) { |
863 if (_bufferedData != null) { | 867 if (_bufferedData != null) { |
864 if (bytes > _bufferedData.length - _bufferedDataIndex) { | 868 if (bytes > _bufferedData.length - _bufferedDataIndex) { |
865 bytes = _bufferedData.length - _bufferedDataIndex; | 869 bytes = _bufferedData.length - _bufferedDataIndex; |
866 } | 870 } |
867 var result = _bufferedData.sublist(_bufferedDataIndex, | 871 var result = _bufferedData.sublist(_bufferedDataIndex, |
868 _bufferedDataIndex + bytes); | 872 _bufferedDataIndex + bytes); |
869 _bufferedDataIndex += bytes; | 873 _bufferedDataIndex += bytes; |
870 if (_bufferedData.length == _bufferedDataIndex) { | 874 if (_bufferedData.length == _bufferedDataIndex) { |
871 _bufferedData = null; | 875 _bufferedData = null; |
872 } | 876 } |
873 return result; | 877 return result; |
874 } else if (!_socketClosedRead) { | 878 } else if (!_socketClosedRead) { |
875 try { | 879 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 { | 880 } else { |
882 return null; | 881 return null; |
883 } | 882 } |
884 } | 883 } |
885 | 884 |
886 void _readSocket() { | 885 void _readSocket() { |
887 if (_status == CLOSED) return; | 886 if (_status == CLOSED) return; |
888 var buffer = _secureFilter.buffers[READ_ENCRYPTED]; | 887 var buffer = _secureFilter.buffers[READ_ENCRYPTED]; |
889 if (buffer.writeFromSource(_readSocketOrBufferedData) > 0) { | 888 if (buffer.writeFromSource(_readSocketOrBufferedData) > 0) { |
890 _filterStatus.readEmpty = false; | 889 _filterStatus.readEmpty = false; |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1154 void handshake(); | 1153 void handshake(); |
1155 void init(); | 1154 void init(); |
1156 X509Certificate get peerCertificate; | 1155 X509Certificate get peerCertificate; |
1157 int processBuffer(int bufferIndex); | 1156 int processBuffer(int bufferIndex); |
1158 void registerBadCertificateCallback(Function callback); | 1157 void registerBadCertificateCallback(Function callback); |
1159 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); | 1158 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); |
1160 int _pointer(); | 1159 int _pointer(); |
1161 | 1160 |
1162 List<_ExternalBuffer> get buffers; | 1161 List<_ExternalBuffer> get buffers; |
1163 } | 1162 } |
1163 | |
1164 /** A secure networking exception caused by a failure in the | |
1165 * TLS/SSL protocol. | |
1166 */ | |
1167 class TlsException implements IOException { | |
1168 const TlsException([String message = "", | |
1169 OSError osError = null]) | |
1170 : this._("TlsException", message, osError); | |
1171 | |
1172 const TlsException._(String this.type, | |
1173 String this.message, | |
1174 OSError this.osError); | |
1175 | |
1176 String toString() { | |
1177 StringBuffer sb = new StringBuffer(); | |
1178 sb.write(type); | |
1179 if (!message.isEmpty) { | |
1180 sb.write(": $message"); | |
1181 if (osError != null) { | |
1182 sb.write(" ($osError)"); | |
1183 } | |
1184 } else if (osError != null) { | |
1185 sb.write(": $osError"); | |
1186 } | |
1187 return sb.toString(); | |
1188 } | |
1189 final String type; | |
Anders Johnsen
2013/06/25 05:55:03
Move to top.
Bill Hesse
2013/06/25 12:41:14
Done.
| |
1190 final String message; | |
1191 final OSError osError; | |
1192 } | |
1193 | |
1194 | |
1195 /** | |
1196 * An exception that happens in the handshake phase of establishing | |
1197 * a secure network connection. | |
1198 */ | |
Søren Gjesse
2013/06/25 06:35:49
As mentioned before maybe just drop HandshakeExcep
| |
1199 class HandshakeException extends TlsException { | |
1200 const HandshakeException([String message = "", | |
1201 OSError osError = null]) | |
1202 : super._("HandshakeException", message, osError); | |
1203 } | |
1204 | |
1205 | |
1206 /** | |
1207 * An exception that happens in the handshake phase of establishing | |
1208 * a secure network connection, when looking up or verifying a | |
1209 * certificate. | |
1210 */ | |
Søren Gjesse
2013/06/25 06:35:49
Ca we add some more information to this exception
Bill Hesse
2013/06/25 12:41:14
Done.
| |
1211 class CertificateException extends TlsException { | |
1212 const CertificateException([String message = "", | |
1213 OSError osError = null]) | |
1214 : super._("CertificateException", message, osError); | |
1215 } | |
OLD | NEW |