| Index: sdk/lib/io/secure_socket.dart
|
| diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart
|
| index a8b0eaafacf7ff0ba81e3d648d944444af1adcc9..8733f32dd3e58c896d87065f67f0f658c65a442e 100644
|
| --- a/sdk/lib/io/secure_socket.dart
|
| +++ b/sdk/lib/io/secure_socket.dart
|
| @@ -150,16 +150,18 @@ abstract class SecureSocket implements Socket {
|
| X509Certificate get peerCertificate;
|
|
|
| /**
|
| - * Initializes the NSS library. If [initialize] is not called, the library
|
| + * Initializes the NSS library. If [initialize] is not called, the library
|
| * is automatically initialized as if [initialize] were called with no
|
| - * arguments.
|
| + * arguments. If [initialize] is called more than once, or called after
|
| + * automatic initialization has happened (when a secure connection is made),
|
| + * then a TlsException is thrown.
|
| *
|
| * The optional argument [database] is the path to a certificate database
|
| * directory containing root certificates for verifying certificate paths on
|
| * client connections, and server certificates to provide on server
|
| - * connections. The argument [password] should be used when creating
|
| + * connections. The argument [password] should be used when creating
|
| * secure server sockets, to allow the private key of the server
|
| - * certificate to be fetched. If [useBuiltinRoots] is true (the default),
|
| + * certificate to be fetched. If [useBuiltinRoots] is true (the default),
|
| * then a built-in set of root certificates for trusted certificate
|
| * authorities is merged with the certificates in the database.
|
| * The list of built-in root certificates, and documentation about this
|
| @@ -167,7 +169,7 @@ abstract class SecureSocket implements Socket {
|
| * http://www.mozilla.org/projects/security/certs/included/ .
|
| *
|
| * If the [database] argument is omitted, then only the
|
| - * builtin root certificates are used. If [useBuiltinRoots] is also false,
|
| + * builtin root certificates are used. If [useBuiltinRoots] is also false,
|
| * then no certificates are available.
|
| *
|
| * Examples:
|
| @@ -437,6 +439,9 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| bool sendClientCertificate: false,
|
| bool onBadCertificate(X509Certificate certificate)}) {
|
| var future;
|
| + _verifyFields(host, requestedPort, certificateName, is_server,
|
| + requestClientCertificate, requireClientCertificate,
|
| + sendClientCertificate, onBadCertificate);
|
| if (host is String) {
|
| if (socket != null) {
|
| future = new Future.value(
|
| @@ -484,7 +489,6 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| _stream = _controller.stream;
|
| // Throw an ArgumentError if any field is invalid. After this, all
|
| // errors will be reported through the future or the stream.
|
| - _verifyFields();
|
| _secureFilter.init();
|
| _filterPointer = _secureFilter._pointer();
|
| _secureFilter.registerHandshakeCompleteCallback(
|
| @@ -499,6 +503,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| futureSocket = new Future.value(socket);
|
| }
|
| futureSocket.then((rawSocket) {
|
| + _connectPending = true;
|
| _socket = rawSocket;
|
| _socket.readEventsEnabled = true;
|
| _socket.writeEventsEnabled = false;
|
| @@ -506,14 +511,13 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| // If a current subscription is provided use this otherwise
|
| // create a new one.
|
| _socketSubscription = _socket.listen(_eventDispatcher,
|
| - onError: _errorHandler,
|
| + onError: _reportError,
|
| onDone: _doneHandler);
|
| } else {
|
| _socketSubscription.onData(_eventDispatcher);
|
| - _socketSubscription.onError(_errorHandler);
|
| + _socketSubscription.onError(_reportError);
|
| _socketSubscription.onDone(_doneHandler);
|
| }
|
| - _connectPending = true;
|
| _secureFilter.connect(address.host,
|
| (address as dynamic)._sockaddr_storage,
|
| port,
|
| @@ -525,10 +529,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| sendClientCertificate);
|
| _secureHandshake();
|
| })
|
| - .catchError((error) {
|
| - _handshakeComplete.completeError(error);
|
| - _close();
|
| - });
|
| + .catchError(_reportError);
|
| }
|
|
|
| StreamSubscription listen(void onData(RawSocketEvent data),
|
| @@ -542,12 +543,22 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| cancelOnError: cancelOnError);
|
| }
|
|
|
| - void _verifyFields() {
|
| - assert(is_server is bool);
|
| - assert(_socket == null || _socket is RawSocket);
|
| - if (address is! InternetAddress) {
|
| - throw new ArgumentError(
|
| - "RawSecureSocket constructor: host is not an InternetAddress");
|
| + static void _verifyFields(host,
|
| + int requestedPort,
|
| + String certificateName,
|
| + bool is_server,
|
| + bool requestClientCertificate,
|
| + bool requireClientCertificate,
|
| + bool sendClientCertificate,
|
| + Function onBadCertificate) {
|
| + if (host is! String && host is! InternetAddress) {
|
| + throw new ArgumentError("host is not a String or an InternetAddress");
|
| + }
|
| + if (requestedPort is! int) {
|
| + throw new ArgumentError("requestedPort is not an int");
|
| + }
|
| + if (requestedPort < 0 || requestedPort > 65535) {
|
| + throw new ArgumentError("requestedPort is not in the range 0..65535");
|
| }
|
| if (certificateName != null && certificateName is! String) {
|
| throw new ArgumentError("certificateName is not null or a String");
|
| @@ -693,12 +704,16 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| }
|
|
|
| void _eventDispatcher(RawSocketEvent event) {
|
| - if (event == RawSocketEvent.READ) {
|
| - _readHandler();
|
| - } else if (event == RawSocketEvent.WRITE) {
|
| - _writeHandler();
|
| - } else if (event == RawSocketEvent.READ_CLOSED) {
|
| - _closeHandler();
|
| + try {
|
| + if (event == RawSocketEvent.READ) {
|
| + _readHandler();
|
| + } else if (event == RawSocketEvent.WRITE) {
|
| + _writeHandler();
|
| + } else if (event == RawSocketEvent.READ_CLOSED) {
|
| + _closeHandler();
|
| + }
|
| + } catch (e) {
|
| + _reportError(e);
|
| }
|
| }
|
|
|
| @@ -718,20 +733,13 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| }
|
| }
|
|
|
| - void _errorHandler(e) {
|
| - _reportError(e, 'Error on underlying RawSocket');
|
| - }
|
| -
|
| - void _reportError(e, String message) {
|
| - // TODO(whesse): Call _reportError from all internal functions that throw.
|
| - if (e is SocketException) {
|
| - e = new SocketException('$message (${e.message})', e.osError);
|
| - } else if (e is OSError) {
|
| - e = new SocketException(message, e);
|
| - } else {
|
| - e = new SocketException('$message (${e.toString()})', null);
|
| - }
|
| + void _reportError(e) {
|
| if (_connectPending) {
|
| + // _connectPending is true after the underlying connection has been
|
| + // made, but before the handshake has completed.
|
| + if (e is! TlsException) {
|
| + e = new HandshakeException("$e", null);
|
| + }
|
| _handshakeComplete.completeError(e);
|
| } else {
|
| _controller.addError(e);
|
| @@ -756,8 +764,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| _socketClosedRead = true;
|
| if (_filterStatus.readEmpty) {
|
| _reportError(
|
| - new SocketException('Connection terminated during handshake'),
|
| - 'RawSecureSocket error');
|
| + new HandshakeException('Connection terminated during handshake'));
|
| } else {
|
| _secureHandshake();
|
| }
|
| @@ -772,7 +779,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| _writeSocket();
|
| _scheduleFilter();
|
| } catch (e) {
|
| - _reportError(e, "RawSecureSocket error");
|
| + _reportError(e);
|
| }
|
| }
|
|
|
| @@ -838,9 +845,8 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| if (_status == HANDSHAKE) {
|
| _secureFilter.handshake();
|
| if (_status == HANDSHAKE) {
|
| - _reportError(
|
| - new SocketException('Connection terminated during handshake'),
|
| - 'RawSecureSocket error');
|
| + throw new HandshakeException(
|
| + 'Connection terminated during handshake');
|
| }
|
| }
|
| _closeHandler();
|
| @@ -855,7 +861,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| if (_status == HANDSHAKE) _secureHandshake();
|
| }
|
| _tryFilter();
|
| - });
|
| + }).catchError(_reportError);
|
| }
|
| }
|
|
|
| @@ -872,12 +878,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
|
| }
|
| return result;
|
| } else if (!_socketClosedRead) {
|
| - try {
|
| - return _socket.read(bytes);
|
| - } catch (e) {
|
| - _reportError(e, "RawSecureSocket error reading encrypted socket");
|
| - return null;
|
| - }
|
| + return _socket.read(bytes);
|
| } else {
|
| return null;
|
| }
|
| @@ -1161,3 +1162,57 @@ abstract class _SecureFilter {
|
|
|
| List<_ExternalBuffer> get buffers;
|
| }
|
| +
|
| +/** A secure networking exception caused by a failure in the
|
| + * TLS/SSL protocol.
|
| + */
|
| +class TlsException implements IOException {
|
| + final String type;
|
| + final String message;
|
| + final OSError osError;
|
| +
|
| + const TlsException([String message = "",
|
| + OSError osError = null])
|
| + : this._("TlsException", message, osError);
|
| +
|
| + const TlsException._(String this.type,
|
| + String this.message,
|
| + OSError this.osError);
|
| +
|
| + String toString() {
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write(type);
|
| + if (!message.isEmpty) {
|
| + sb.write(": $message");
|
| + if (osError != null) {
|
| + sb.write(" ($osError)");
|
| + }
|
| + } else if (osError != null) {
|
| + sb.write(": $osError");
|
| + }
|
| + return sb.toString();
|
| + }
|
| +}
|
| +
|
| +
|
| +/**
|
| + * An exception that happens in the handshake phase of establishing
|
| + * a secure network connection.
|
| + */
|
| +class HandshakeException extends TlsException {
|
| + const HandshakeException([String message = "",
|
| + OSError osError = null])
|
| + : super._("HandshakeException", message, osError);
|
| +}
|
| +
|
| +
|
| +/**
|
| + * An exception that happens in the handshake phase of establishing
|
| + * a secure network connection, when looking up or verifying a
|
| + * certificate.
|
| + */
|
| +class CertificateException extends TlsException {
|
| + const CertificateException([String message = "",
|
| + OSError osError = null])
|
| + : super._("CertificateException", message, osError);
|
| +}
|
|
|