Index: pkg/dev_compiler/tool/input_sdk/lib/io/secure_socket.dart |
diff --git a/pkg/dev_compiler/tool/input_sdk/lib/io/secure_socket.dart b/pkg/dev_compiler/tool/input_sdk/lib/io/secure_socket.dart |
deleted file mode 100644 |
index 380a8c466abd282facda77184ed96728b4e0003b..0000000000000000000000000000000000000000 |
--- a/pkg/dev_compiler/tool/input_sdk/lib/io/secure_socket.dart |
+++ /dev/null |
@@ -1,1263 +0,0 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-part of dart.io; |
- |
-/** |
- * A high-level class for communicating securely over a TCP socket, using |
- * TLS and SSL. The [SecureSocket] exposes both a [Stream] and an |
- * [IOSink] interface, making it ideal for using together with |
- * other [Stream]s. |
- */ |
-abstract class SecureSocket implements Socket { |
- external factory SecureSocket._(RawSecureSocket rawSocket); |
- |
- /** |
- * Constructs a new secure client socket and connects it to the given |
- * [host] on port [port]. The returned Future will complete with a |
- * [SecureSocket] that is connected and ready for subscription. |
- * |
- * The certificate provided by the server is checked |
- * using the trusted certificates set in the SecurityContext object. |
- * The default SecurityContext object contains a built-in set of trusted |
- * root certificates for well-known certificate authorities. |
- * |
- * [onBadCertificate] is an optional handler for unverifiable certificates. |
- * The handler receives the [X509Certificate], and can inspect it and |
- * decide (or let the user decide) whether to accept |
- * the connection or not. The handler should return true |
- * to continue the [SecureSocket] connection. |
- */ |
- static Future<SecureSocket> connect( |
- host, |
- int port, |
- {SecurityContext context, |
- bool onBadCertificate(X509Certificate certificate), |
- List<String> supportedProtocols}) { |
- return RawSecureSocket.connect(host, |
- port, |
- context: context, |
- onBadCertificate: onBadCertificate, |
- supportedProtocols: supportedProtocols) |
- .then((rawSocket) => new SecureSocket._(rawSocket)); |
- } |
- |
- /** |
- * Takes an already connected [socket] and starts client side TLS |
- * handshake to make the communication secure. When the returned |
- * future completes the [SecureSocket] has completed the TLS |
- * handshake. Using this function requires that the other end of the |
- * connection is prepared for TLS handshake. |
- * |
- * If the [socket] already has a subscription, this subscription |
- * will no longer receive and events. In most cases calling |
- * `pause` on this subscription before starting TLS handshake is |
- * the right thing to do. |
- * |
- * If the [host] argument is passed it will be used as the host name |
- * for the TLS handshake. If [host] is not passed the host name from |
- * the [socket] will be used. The [host] can be either a [String] or |
- * an [InternetAddress]. |
- * |
- * Calling this function will _not_ cause a DNS host lookup. If the |
- * [host] passed is a [String] the [InternetAddress] for the |
- * resulting [SecureSocket] will have the passed in [host] as its |
- * host value and the internet address of the already connected |
- * socket as its address value. |
- * |
- * See [connect] for more information on the arguments. |
- * |
- */ |
- static Future<SecureSocket> secure( |
- Socket socket, |
- {host, |
- SecurityContext context, |
- bool onBadCertificate(X509Certificate certificate)}) { |
- var completer = new Completer(); |
- (socket as dynamic)._detachRaw() |
- .then((detachedRaw) { |
- return RawSecureSocket.secure( |
- detachedRaw[0], |
- subscription: detachedRaw[1], |
- host: host, |
- context: context, |
- onBadCertificate: onBadCertificate); |
- }) |
- .then((raw) { |
- completer.complete(new SecureSocket._(raw)); |
- }); |
- return completer.future; |
- } |
- |
- /** |
- * Takes an already connected [socket] and starts server side TLS |
- * handshake to make the communication secure. When the returned |
- * future completes the [SecureSocket] has completed the TLS |
- * handshake. Using this function requires that the other end of the |
- * connection is going to start the TLS handshake. |
- * |
- * If the [socket] already has a subscription, this subscription |
- * will no longer receive and events. In most cases calling |
- * [:pause:] on this subscription before starting TLS handshake is |
- * the right thing to do. |
- * |
- * If some of the data of the TLS handshake has already been read |
- * from the socket this data can be passed in the [bufferedData] |
- * parameter. This data will be processed before any other data |
- * available on the socket. |
- * |
- * See [SecureServerSocket.bind] for more information on the |
- * arguments. |
- * |
- */ |
- static Future<SecureSocket> secureServer( |
- Socket socket, |
- SecurityContext context, |
- {List<int> bufferedData, |
- bool requestClientCertificate: false, |
- bool requireClientCertificate: false, |
- List<String> supportedProtocols}) { |
- var completer = new Completer(); |
- (socket as dynamic)._detachRaw() |
- .then((detachedRaw) { |
- return RawSecureSocket.secureServer( |
- detachedRaw[0], |
- context, |
- subscription: detachedRaw[1], |
- bufferedData: bufferedData, |
- requestClientCertificate: requestClientCertificate, |
- requireClientCertificate: requireClientCertificate, |
- supportedProtocols: supportedProtocols); |
- }) |
- .then((raw) { |
- completer.complete(new SecureSocket._(raw)); |
- }); |
- return completer.future; |
- } |
- |
- /** |
- * Get the peer certificate for a connected SecureSocket. If this |
- * SecureSocket is the server end of a secure socket connection, |
- * [peerCertificate] will return the client certificate, or null, if no |
- * client certificate was received. If it is the client end, |
- * [peerCertificate] will return the server's certificate. |
- */ |
- X509Certificate get peerCertificate; |
- |
- /** |
- * Get the protocol which was selected during protocol negotiation. |
- */ |
- String get selectedProtocol; |
- |
- /** |
- * Renegotiate an existing secure connection, renewing the session keys |
- * and possibly changing the connection properties. |
- * |
- * This repeats the SSL or TLS handshake, with options that allow clearing |
- * the session cache and requesting a client certificate. |
- */ |
- void renegotiate({bool useSessionCache: true, |
- bool requestClientCertificate: false, |
- bool requireClientCertificate: false}); |
-} |
- |
- |
-/** |
- * RawSecureSocket provides a secure (SSL or TLS) network connection. |
- * Client connections to a server are provided by calling |
- * RawSecureSocket.connect. A secure server, created with |
- * [RawSecureServerSocket], also returns RawSecureSocket objects representing |
- * the server end of a secure connection. |
- * The certificate provided by the server is checked |
- * using the trusted certificates set in the SecurityContext object. |
- * The default [SecurityContext] object contains a built-in set of trusted |
- * root certificates for well-known certificate authorities. |
- */ |
-abstract class RawSecureSocket implements RawSocket { |
- /** |
- * Constructs a new secure client socket and connect it to the given |
- * host on the given port. The returned [Future] is completed with the |
- * RawSecureSocket when it is connected and ready for subscription. |
- * |
- * The certificate provided by the server is checked using the trusted |
- * certificates set in the SecurityContext object If a certificate and key are |
- * set on the client, using [SecurityContext.useCertificateChain] and |
- * [SecurityContext.usePrivateKey], and the server asks for a client |
- * certificate, then that client certificate is sent to the server. |
- * |
- * [onBadCertificate] is an optional handler for unverifiable certificates. |
- * The handler receives the [X509Certificate], and can inspect it and |
- * decide (or let the user decide) whether to accept |
- * the connection or not. The handler should return true |
- * to continue the [RawSecureSocket] connection. |
- */ |
- static Future<RawSecureSocket> connect( |
- host, |
- int port, |
- {SecurityContext context, |
- bool onBadCertificate(X509Certificate certificate), |
- List<String> supportedProtocols}) { |
- _RawSecureSocket._verifyFields( |
- host, |
- port, |
- false, |
- false, |
- false, |
- onBadCertificate); |
- return RawSocket.connect(host, port) |
- .then((socket) { |
- return secure(socket, |
- context: context, |
- onBadCertificate: onBadCertificate, |
- supportedProtocols: supportedProtocols); |
- }); |
- } |
- |
- /** |
- * Takes an already connected [socket] and starts client side TLS |
- * handshake to make the communication secure. When the returned |
- * future completes the [RawSecureSocket] has completed the TLS |
- * handshake. Using this function requires that the other end of the |
- * connection is prepared for TLS handshake. |
- * |
- * If the [socket] already has a subscription, pass the existing |
- * subscription in the [subscription] parameter. The [secure] |
- * operation will take over the subscription by replacing the |
- * handlers with it own secure processing. The caller must not touch |
- * this subscription anymore. Passing a paused subscription is an |
- * error. |
- * |
- * If the [host] argument is passed it will be used as the host name |
- * for the TLS handshake. If [host] is not passed the host name from |
- * the [socket] will be used. The [host] can be either a [String] or |
- * an [InternetAddress]. |
- * |
- * Calling this function will _not_ cause a DNS host lookup. If the |
- * [host] passed is a [String] the [InternetAddress] for the |
- * resulting [SecureSocket] will have this passed in [host] as its |
- * host value and the internet address of the already connected |
- * socket as its address value. |
- * |
- * See [connect] for more information on the arguments. |
- * |
- */ |
- static Future<RawSecureSocket> secure( |
- RawSocket socket, |
- {StreamSubscription subscription, |
- host, |
- SecurityContext context, |
- bool onBadCertificate(X509Certificate certificate), |
- List<String> supportedProtocols}) { |
- socket.readEventsEnabled = false; |
- socket.writeEventsEnabled = false; |
- return _RawSecureSocket.connect( |
- host != null ? host : socket.address.host, |
- socket.port, |
- is_server: false, |
- socket: socket, |
- subscription: subscription, |
- context: context, |
- onBadCertificate: onBadCertificate, |
- supportedProtocols: supportedProtocols); |
- } |
- |
- /** |
- * Takes an already connected [socket] and starts server side TLS |
- * handshake to make the communication secure. When the returned |
- * future completes the [RawSecureSocket] has completed the TLS |
- * handshake. Using this function requires that the other end of the |
- * connection is going to start the TLS handshake. |
- * |
- * If the [socket] already has a subscription, pass the existing |
- * subscription in the [subscription] parameter. The [secureServer] |
- * operation will take over the subscription by replacing the |
- * handlers with it own secure processing. The caller must not touch |
- * this subscription anymore. Passing a paused subscription is an |
- * error. |
- * |
- * If some of the data of the TLS handshake has already been read |
- * from the socket this data can be passed in the [bufferedData] |
- * parameter. This data will be processed before any other data |
- * available on the socket. |
- * |
- * See [RawSecureServerSocket.bind] for more information on the |
- * arguments. |
- * |
- */ |
- static Future<RawSecureSocket> secureServer( |
- RawSocket socket, |
- SecurityContext context, |
- {StreamSubscription subscription, |
- List<int> bufferedData, |
- bool requestClientCertificate: false, |
- bool requireClientCertificate: false, |
- List<String> supportedProtocols}) { |
- socket.readEventsEnabled = false; |
- socket.writeEventsEnabled = false; |
- return _RawSecureSocket.connect( |
- socket.address, |
- socket.remotePort, |
- context: context, |
- is_server: true, |
- socket: socket, |
- subscription: subscription, |
- bufferedData: bufferedData, |
- requestClientCertificate: requestClientCertificate, |
- requireClientCertificate: requireClientCertificate, |
- supportedProtocols: supportedProtocols); |
- } |
- |
- /** |
- * Renegotiate an existing secure connection, renewing the session keys |
- * and possibly changing the connection properties. |
- * |
- * This repeats the SSL or TLS handshake, with options that allow clearing |
- * the session cache and requesting a client certificate. |
- */ |
- void renegotiate({bool useSessionCache: true, |
- bool requestClientCertificate: false, |
- bool requireClientCertificate: false}); |
- |
- /** |
- * Get the peer certificate for a connected RawSecureSocket. If this |
- * RawSecureSocket is the server end of a secure socket connection, |
- * [peerCertificate] will return the client certificate, or null, if no |
- * client certificate was received. If it is the client end, |
- * [peerCertificate] will return the server's certificate. |
- */ |
- X509Certificate get peerCertificate; |
- |
- /** |
- * Get the protocol which was selected during protocol negotiation. |
- */ |
- String get selectedProtocol; |
-} |
- |
- |
-/** |
- * X509Certificate represents an SSL certificate, with accessors to |
- * get the fields of the certificate. |
- */ |
-abstract class X509Certificate { |
- external factory X509Certificate._(); |
- |
- String get subject; |
- String get issuer; |
- DateTime get startValidity; |
- DateTime get endValidity; |
-} |
- |
- |
-class _FilterStatus { |
- bool progress = false; // The filter read or wrote data to the buffers. |
- bool readEmpty = true; // The read buffers and decryption filter are empty. |
- bool writeEmpty = true; // The write buffers and encryption filter are empty. |
- // These are set if a buffer changes state from empty or full. |
- bool readPlaintextNoLongerEmpty = false; |
- bool writePlaintextNoLongerFull = false; |
- bool readEncryptedNoLongerFull = false; |
- bool writeEncryptedNoLongerEmpty = false; |
- |
- _FilterStatus(); |
-} |
- |
- |
-class _RawSecureSocket extends Stream<RawSocketEvent> |
- implements RawSecureSocket { |
- // Status states |
- static final int HANDSHAKE = 201; |
- static final int CONNECTED = 202; |
- static final int CLOSED = 203; |
- |
- // Buffer identifiers. |
- // These must agree with those in the native C++ implementation. |
- static final int READ_PLAINTEXT = 0; |
- static final int WRITE_PLAINTEXT = 1; |
- static final int READ_ENCRYPTED = 2; |
- static final int WRITE_ENCRYPTED = 3; |
- static final int NUM_BUFFERS = 4; |
- |
- // Is a buffer identifier for an encrypted buffer? |
- static bool _isBufferEncrypted(int identifier) => identifier >= READ_ENCRYPTED; |
- |
- RawSocket _socket; |
- final Completer<_RawSecureSocket> _handshakeComplete = |
- new Completer<_RawSecureSocket>(); |
- StreamController<RawSocketEvent> _controller; |
- Stream<RawSocketEvent> _stream; |
- StreamSubscription<RawSocketEvent> _socketSubscription; |
- List<int> _bufferedData; |
- int _bufferedDataIndex = 0; |
- final InternetAddress address; |
- final bool is_server; |
- SecurityContext context; |
- final bool requestClientCertificate; |
- final bool requireClientCertificate; |
- final Function onBadCertificate; |
- |
- var _status = HANDSHAKE; |
- bool _writeEventsEnabled = true; |
- bool _readEventsEnabled = true; |
- int _pauseCount = 0; |
- bool _pendingReadEvent = false; |
- bool _socketClosedRead = false; // The network socket is closed for reading. |
- bool _socketClosedWrite = false; // The network socket is closed for writing. |
- bool _closedRead = false; // The secure socket has fired an onClosed event. |
- bool _closedWrite = false; // The secure socket has been closed for writing. |
- Completer _closeCompleter = new Completer(); // The network socket is gone. |
- _FilterStatus _filterStatus = new _FilterStatus(); |
- bool _connectPending = true; |
- bool _filterPending = false; |
- bool _filterActive = false; |
- |
- _SecureFilter _secureFilter = new _SecureFilter(); |
- String _selectedProtocol; |
- |
- static Future<_RawSecureSocket> connect( |
- dynamic/*String|InternetAddress*/ host, |
- int requestedPort, |
- {bool is_server, |
- SecurityContext context, |
- RawSocket socket, |
- StreamSubscription subscription, |
- List<int> bufferedData, |
- bool requestClientCertificate: false, |
- bool requireClientCertificate: false, |
- bool onBadCertificate(X509Certificate certificate), |
- List<String> supportedProtocols}) { |
- _verifyFields(host, requestedPort, is_server, |
- requestClientCertificate, requireClientCertificate, |
- onBadCertificate); |
- if (host is InternetAddress) host = host.host; |
- InternetAddress address = socket.address; |
- if (host != null) { |
- address = InternetAddress._cloneWithNewHost(address, host); |
- } |
- return new _RawSecureSocket(address, |
- requestedPort, |
- is_server, |
- context, |
- socket, |
- subscription, |
- bufferedData, |
- requestClientCertificate, |
- requireClientCertificate, |
- onBadCertificate, |
- supportedProtocols) |
- ._handshakeComplete.future; |
- } |
- |
- _RawSecureSocket( |
- this.address, |
- int requestedPort, |
- this.is_server, |
- this.context, |
- RawSocket this._socket, |
- this._socketSubscription, |
- this._bufferedData, |
- this.requestClientCertificate, |
- this.requireClientCertificate, |
- this.onBadCertificate(X509Certificate certificate), |
- List<String> supportedProtocols) { |
- if (context == null) { |
- context = SecurityContext.defaultContext; |
- } |
- _controller = new StreamController<RawSocketEvent>( |
- sync: true, |
- onListen: _onSubscriptionStateChange, |
- onPause: _onPauseStateChange, |
- onResume: _onPauseStateChange, |
- onCancel: _onSubscriptionStateChange); |
- _stream = _controller.stream; |
- // Throw an ArgumentError if any field is invalid. After this, all |
- // errors will be reported through the future or the stream. |
- _secureFilter.init(); |
- _secureFilter.registerHandshakeCompleteCallback( |
- _secureHandshakeCompleteHandler); |
- if (onBadCertificate != null) { |
- _secureFilter.registerBadCertificateCallback(_onBadCertificateWrapper); |
- } |
- _socket.readEventsEnabled = true; |
- _socket.writeEventsEnabled = false; |
- if (_socketSubscription == null) { |
- // If a current subscription is provided use this otherwise |
- // create a new one. |
- _socketSubscription = _socket.listen(_eventDispatcher, |
- onError: _reportError, |
- onDone: _doneHandler); |
- } else { |
- if (_socketSubscription.isPaused) { |
- _socket.close(); |
- throw new ArgumentError( |
- "Subscription passed to TLS upgrade is paused"); |
- } |
- // If we are upgrading a socket that is already closed for read, |
- // report an error as if we received READ_CLOSED during the handshake. |
- dynamic s = _socket; // Cast to dynamic to avoid warning. |
- if (s._socket.closedReadEventSent) { |
- _eventDispatcher(RawSocketEvent.READ_CLOSED); |
- } |
- _socketSubscription |
- ..onData(_eventDispatcher) |
- ..onError(_reportError) |
- ..onDone(_doneHandler); |
- } |
- try { |
- var encodedProtocols = |
- SecurityContext._protocolsToLengthEncoding(supportedProtocols); |
- _secureFilter.connect(address.host, |
- context, |
- is_server, |
- requestClientCertificate || |
- requireClientCertificate, |
- requireClientCertificate, |
- encodedProtocols); |
- _secureHandshake(); |
- } catch (e, s) { |
- _reportError(e, s); |
- } |
- } |
- |
- StreamSubscription<RawSocketEvent> listen(void onData(RawSocketEvent data), |
- {Function onError, |
- void onDone(), |
- bool cancelOnError}) { |
- _sendWriteEvent(); |
- return _stream.listen(onData, |
- onError: onError, |
- onDone: onDone, |
- cancelOnError: cancelOnError); |
- } |
- |
- static void _verifyFields(host, |
- int requestedPort, |
- bool is_server, |
- bool requestClientCertificate, |
- bool requireClientCertificate, |
- 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 (requestClientCertificate is! bool) { |
- throw new ArgumentError("requestClientCertificate is not a bool"); |
- } |
- if (requireClientCertificate is! bool) { |
- throw new ArgumentError("requireClientCertificate is not a bool"); |
- } |
- if (onBadCertificate != null && onBadCertificate is! Function) { |
- throw new ArgumentError("onBadCertificate is not null or a Function"); |
- } |
- } |
- |
- int get port => _socket.port; |
- |
- InternetAddress get remoteAddress => _socket.remoteAddress; |
- |
- int get remotePort => _socket.remotePort; |
- |
- void set _owner(owner) { |
- (_socket as dynamic)._owner = owner; |
- } |
- |
- int available() { |
- return _status != CONNECTED ? 0 |
- : _secureFilter.buffers[READ_PLAINTEXT].length; |
- } |
- |
- Future<RawSecureSocket> close() { |
- shutdown(SocketDirection.BOTH); |
- return _closeCompleter.future; |
- } |
- |
- void _completeCloseCompleter([dummy]) { |
- if (!_closeCompleter.isCompleted) _closeCompleter.complete(this); |
- } |
- |
- void _close() { |
- _closedWrite = true; |
- _closedRead = true; |
- if (_socket != null) { |
- _socket.close().then(_completeCloseCompleter); |
- } else { |
- _completeCloseCompleter(); |
- } |
- _socketClosedWrite = true; |
- _socketClosedRead = true; |
- if (!_filterActive && _secureFilter != null) { |
- _secureFilter.destroy(); |
- _secureFilter = null; |
- } |
- if (_socketSubscription != null) { |
- _socketSubscription.cancel(); |
- } |
- _controller.close(); |
- _status = CLOSED; |
- } |
- |
- void shutdown(SocketDirection direction) { |
- if (direction == SocketDirection.SEND || |
- direction == SocketDirection.BOTH) { |
- _closedWrite = true; |
- if (_filterStatus.writeEmpty) { |
- _socket.shutdown(SocketDirection.SEND); |
- _socketClosedWrite = true; |
- if (_closedRead) { |
- _close(); |
- } |
- } |
- } |
- if (direction == SocketDirection.RECEIVE || |
- direction == SocketDirection.BOTH) { |
- _closedRead = true; |
- _socketClosedRead = true; |
- _socket.shutdown(SocketDirection.RECEIVE); |
- if (_socketClosedWrite) { |
- _close(); |
- } |
- } |
- } |
- |
- bool get writeEventsEnabled => _writeEventsEnabled; |
- |
- void set writeEventsEnabled(bool value) { |
- _writeEventsEnabled = value; |
- if (value) { |
- Timer.run(() => _sendWriteEvent()); |
- } |
- } |
- |
- bool get readEventsEnabled => _readEventsEnabled; |
- |
- void set readEventsEnabled(bool value) { |
- _readEventsEnabled = value; |
- _scheduleReadEvent(); |
- } |
- |
- List<int> read([int length]) { |
- if (length != null && (length is! int || length < 0)) { |
- throw new ArgumentError( |
- "Invalid length parameter in SecureSocket.read (length: $length)"); |
- } |
- if (_closedRead) { |
- throw new SocketException("Reading from a closed socket"); |
- } |
- if (_status != CONNECTED) { |
- return null; |
- } |
- var result = _secureFilter.buffers[READ_PLAINTEXT].read(length); |
- _scheduleFilter(); |
- return result; |
- } |
- |
- // Write the data to the socket, and schedule the filter to encrypt it. |
- int write(List<int> data, [int offset, int bytes]) { |
- if (bytes != null && (bytes is! int || bytes < 0)) { |
- throw new ArgumentError( |
- "Invalid bytes parameter in SecureSocket.read (bytes: $bytes)"); |
- } |
- if (offset != null && (offset is! int || offset < 0)) { |
- throw new ArgumentError( |
- "Invalid offset parameter in SecureSocket.read (offset: $offset)"); |
- } |
- if (_closedWrite) { |
- _controller.addError(new SocketException("Writing to a closed socket")); |
- return 0; |
- } |
- if (_status != CONNECTED) return 0; |
- if (offset == null) offset = 0; |
- if (bytes == null) bytes = data.length - offset; |
- |
- int written = |
- _secureFilter.buffers[WRITE_PLAINTEXT].write(data, offset, bytes); |
- if (written > 0) { |
- _filterStatus.writeEmpty = false; |
- } |
- _scheduleFilter(); |
- return written; |
- } |
- |
- X509Certificate get peerCertificate => _secureFilter.peerCertificate; |
- |
- String get selectedProtocol => _selectedProtocol; |
- |
- bool _onBadCertificateWrapper(X509Certificate certificate) { |
- if (onBadCertificate == null) return false; |
- var result = onBadCertificate(certificate); |
- if (result is bool) return result; |
- throw new HandshakeException( |
- "onBadCertificate callback returned non-boolean $result"); |
- } |
- |
- bool setOption(SocketOption option, bool enabled) { |
- if (_socket == null) return false; |
- return _socket.setOption(option, enabled); |
- } |
- |
- void _eventDispatcher(RawSocketEvent event) { |
- try { |
- if (event == RawSocketEvent.READ) { |
- _readHandler(); |
- } else if (event == RawSocketEvent.WRITE) { |
- _writeHandler(); |
- } else if (event == RawSocketEvent.READ_CLOSED) { |
- _closeHandler(); |
- } |
- } catch (e, stackTrace) { |
- _reportError(e, stackTrace); |
- } |
- } |
- |
- void _readHandler() { |
- _readSocket(); |
- _scheduleFilter(); |
- } |
- |
- void _writeHandler() { |
- _writeSocket(); |
- _scheduleFilter(); |
- } |
- |
- void _doneHandler() { |
- if (_filterStatus.readEmpty) { |
- _close(); |
- } |
- } |
- |
- void _reportError(e, [StackTrace stackTrace]) { |
- if (_status == CLOSED) { |
- return; |
- } else if (_connectPending) { |
- // _connectPending is true until the handshake has completed, and the |
- // _handshakeComplete future returned from SecureSocket.connect has |
- // completed. Before this point, we must complete it with an error. |
- _handshakeComplete.completeError(e, stackTrace); |
- } else { |
- _controller.addError(e, stackTrace); |
- } |
- _close(); |
- } |
- |
- void _closeHandler() { |
- if (_status == CONNECTED) { |
- if (_closedRead) return; |
- _socketClosedRead = true; |
- if (_filterStatus.readEmpty) { |
- _closedRead = true; |
- _controller.add(RawSocketEvent.READ_CLOSED); |
- if (_socketClosedWrite) { |
- _close(); |
- } |
- } else { |
- _scheduleFilter(); |
- } |
- } else if (_status == HANDSHAKE) { |
- _socketClosedRead = true; |
- if (_filterStatus.readEmpty) { |
- _reportError( |
- new HandshakeException('Connection terminated during handshake'), |
- null); |
- } else { |
- _secureHandshake(); |
- } |
- } |
- } |
- |
- void _secureHandshake() { |
- try { |
- _secureFilter.handshake(); |
- _filterStatus.writeEmpty = false; |
- _readSocket(); |
- _writeSocket(); |
- _scheduleFilter(); |
- } catch (e, stackTrace) { |
- _reportError(e, stackTrace); |
- } |
- } |
- |
- void renegotiate({bool useSessionCache: true, |
- bool requestClientCertificate: false, |
- bool requireClientCertificate: false}) { |
- if (_status != CONNECTED) { |
- throw new HandshakeException( |
- "Called renegotiate on a non-connected socket"); |
- } |
- _secureFilter.renegotiate(useSessionCache, |
- requestClientCertificate, |
- requireClientCertificate); |
- _status = HANDSHAKE; |
- _filterStatus.writeEmpty = false; |
- _scheduleFilter(); |
- } |
- |
- void _secureHandshakeCompleteHandler() { |
- _status = CONNECTED; |
- if (_connectPending) { |
- _connectPending = false; |
- try { |
- _selectedProtocol = _secureFilter.selectedProtocol(); |
- // We don't want user code to run synchronously in this callback. |
- Timer.run(() => _handshakeComplete.complete(this)); |
- } catch (error, stack) { |
- _handshakeComplete.completeError(error, stack); |
- } |
- } |
- } |
- |
- void _onPauseStateChange() { |
- if (_controller.isPaused) { |
- _pauseCount++; |
- } else { |
- _pauseCount--; |
- if (_pauseCount == 0) { |
- _scheduleReadEvent(); |
- _sendWriteEvent(); // Can send event synchronously. |
- } |
- } |
- |
- if (!_socketClosedRead || !_socketClosedWrite) { |
- if (_controller.isPaused) { |
- _socketSubscription.pause(); |
- } else { |
- _socketSubscription.resume(); |
- } |
- } |
- } |
- |
- void _onSubscriptionStateChange() { |
- if (_controller.hasListener) { |
- // TODO(ajohnsen): Do something here? |
- } |
- } |
- |
- void _scheduleFilter() { |
- _filterPending = true; |
- _tryFilter(); |
- } |
- |
- void _tryFilter() { |
- if (_status == CLOSED) { |
- return; |
- } |
- if (_filterPending && !_filterActive) { |
- _filterActive = true; |
- _filterPending = false; |
- _pushAllFilterStages().then((status) { |
- _filterStatus = status; |
- _filterActive = false; |
- if (_status == CLOSED) { |
- _secureFilter.destroy(); |
- _secureFilter = null; |
- return; |
- } |
- _socket.readEventsEnabled = true; |
- if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) { |
- // Checks for and handles all cases of partially closed sockets. |
- shutdown(SocketDirection.SEND); |
- if (_status == CLOSED) { |
- return; |
- } |
- } |
- if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) { |
- if (_status == HANDSHAKE) { |
- _secureFilter.handshake(); |
- if (_status == HANDSHAKE) { |
- throw new HandshakeException( |
- 'Connection terminated during handshake'); |
- } |
- } |
- _closeHandler(); |
- } |
- if (_status == CLOSED) { |
- return; |
- } |
- if (_filterStatus.progress) { |
- _filterPending = true; |
- if (_filterStatus.writeEncryptedNoLongerEmpty) { |
- _writeSocket(); |
- } |
- if (_filterStatus.writePlaintextNoLongerFull) { |
- _sendWriteEvent(); |
- } |
- if (_filterStatus.readEncryptedNoLongerFull) { |
- _readSocket(); |
- } |
- if (_filterStatus.readPlaintextNoLongerEmpty) { |
- _scheduleReadEvent(); |
- } |
- if (_status == HANDSHAKE) { |
- _secureHandshake(); |
- } |
- } |
- _tryFilter(); |
- }).catchError(_reportError); |
- } |
- } |
- |
- List<int> _readSocketOrBufferedData(int bytes) { |
- if (_bufferedData != null) { |
- if (bytes > _bufferedData.length - _bufferedDataIndex) { |
- bytes = _bufferedData.length - _bufferedDataIndex; |
- } |
- var result = _bufferedData.sublist(_bufferedDataIndex, |
- _bufferedDataIndex + bytes); |
- _bufferedDataIndex += bytes; |
- if (_bufferedData.length == _bufferedDataIndex) { |
- _bufferedData = null; |
- } |
- return result; |
- } else if (!_socketClosedRead) { |
- return _socket.read(bytes); |
- } else { |
- return null; |
- } |
- } |
- |
- void _readSocket() { |
- if (_status == CLOSED) return; |
- var buffer = _secureFilter.buffers[READ_ENCRYPTED]; |
- if (buffer.writeFromSource(_readSocketOrBufferedData) > 0) { |
- _filterStatus.readEmpty = false; |
- } else { |
- _socket.readEventsEnabled = false; |
- } |
- } |
- |
- void _writeSocket() { |
- if (_socketClosedWrite) return; |
- var buffer = _secureFilter.buffers[WRITE_ENCRYPTED]; |
- if (buffer.readToSocket(_socket)) { // Returns true if blocked |
- _socket.writeEventsEnabled = true; |
- } |
- } |
- |
- // If a read event should be sent, add it to the controller. |
- _scheduleReadEvent() { |
- if (!_pendingReadEvent && |
- _readEventsEnabled && |
- _pauseCount == 0 && |
- _secureFilter != null && |
- !_secureFilter.buffers[READ_PLAINTEXT].isEmpty) { |
- _pendingReadEvent = true; |
- Timer.run(_sendReadEvent); |
- } |
- } |
- |
- _sendReadEvent() { |
- _pendingReadEvent = false; |
- if (_status != CLOSED && |
- _readEventsEnabled && |
- _pauseCount == 0 && |
- _secureFilter != null && |
- !_secureFilter.buffers[READ_PLAINTEXT].isEmpty) { |
- _controller.add(RawSocketEvent.READ); |
- _scheduleReadEvent(); |
- } |
- } |
- |
- // If a write event should be sent, add it to the controller. |
- _sendWriteEvent() { |
- if (!_closedWrite && |
- _writeEventsEnabled && |
- _pauseCount == 0 && |
- _secureFilter != null && |
- _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) { |
- _writeEventsEnabled = false; |
- _controller.add(RawSocketEvent.WRITE); |
- } |
- } |
- |
- Future<_FilterStatus> _pushAllFilterStages() { |
- bool wasInHandshake = _status != CONNECTED; |
- List args = new List(2 + NUM_BUFFERS * 2); |
- args[0] = _secureFilter._pointer(); |
- args[1] = wasInHandshake; |
- var bufs = _secureFilter.buffers; |
- for (var i = 0; i < NUM_BUFFERS; ++i) { |
- args[2 * i + 2] = bufs[i].start; |
- args[2 * i + 3] = bufs[i].end; |
- } |
- |
- return _IOService._dispatch(_SSL_PROCESS_FILTER, args).then((response) { |
- if (response.length == 2) { |
- if (wasInHandshake) { |
- // If we're in handshake, throw a handshake error. |
- _reportError( |
- new HandshakeException('${response[1]} error ${response[0]}'), |
- null); |
- } else { |
- // If we're connected, throw a TLS error. |
- _reportError(new TlsException('${response[1]} error ${response[0]}'), |
- null); |
- } |
- } |
- int start(int index) => response[2 * index]; |
- int end(int index) => response[2 * index + 1]; |
- |
- _FilterStatus status = new _FilterStatus(); |
- // Compute writeEmpty as "write plaintext buffer and write encrypted |
- // buffer were empty when we started and are empty now". |
- status.writeEmpty = bufs[WRITE_PLAINTEXT].isEmpty && |
- start(WRITE_ENCRYPTED) == end(WRITE_ENCRYPTED); |
- // If we were in handshake when this started, _writeEmpty may be false |
- // because the handshake wrote data after we checked. |
- if (wasInHandshake) status.writeEmpty = false; |
- |
- // Compute readEmpty as "both read buffers were empty when we started |
- // and are empty now". |
- status.readEmpty = bufs[READ_ENCRYPTED].isEmpty && |
- start(READ_PLAINTEXT) == end(READ_PLAINTEXT); |
- |
- _ExternalBuffer buffer = bufs[WRITE_PLAINTEXT]; |
- int new_start = start(WRITE_PLAINTEXT); |
- if (new_start != buffer.start) { |
- status.progress = true; |
- if (buffer.free == 0) { |
- status.writePlaintextNoLongerFull = true; |
- } |
- buffer.start = new_start; |
- } |
- buffer = bufs[READ_ENCRYPTED]; |
- new_start = start(READ_ENCRYPTED); |
- if (new_start != buffer.start) { |
- status.progress = true; |
- if (buffer.free == 0) { |
- status.readEncryptedNoLongerFull = true; |
- } |
- buffer.start = new_start; |
- } |
- buffer = bufs[WRITE_ENCRYPTED]; |
- int new_end = end(WRITE_ENCRYPTED); |
- if (new_end != buffer.end) { |
- status.progress = true; |
- if (buffer.length == 0) { |
- status.writeEncryptedNoLongerEmpty = true; |
- } |
- buffer.end = new_end; |
- } |
- buffer = bufs[READ_PLAINTEXT]; |
- new_end = end(READ_PLAINTEXT); |
- if (new_end != buffer.end) { |
- status.progress = true; |
- if (buffer.length == 0) { |
- status.readPlaintextNoLongerEmpty = true; |
- } |
- buffer.end = new_end; |
- } |
- return status; |
- }); |
- } |
-} |
- |
- |
-/** |
- * A circular buffer backed by an external byte array. Accessed from |
- * both C++ and Dart code in an unsynchronized way, with one reading |
- * and one writing. All updates to start and end are done by Dart code. |
- */ |
-class _ExternalBuffer { |
- List data; // This will be a ExternalByteArray, backed by C allocated data. |
- int start; |
- int end; |
- final size; |
- |
- _ExternalBuffer(this.size) { |
- start = end = size ~/ 2; |
- } |
- |
- void advanceStart(int bytes) { |
- assert(start > end || start + bytes <= end); |
- start += bytes; |
- if (start >= size) { |
- start -= size; |
- assert(start <= end); |
- assert(start < size); |
- } |
- } |
- |
- void advanceEnd(int bytes) { |
- assert(start <= end || start > end + bytes); |
- end += bytes; |
- if (end >= size) { |
- end -= size; |
- assert(end < start); |
- assert(end < size); |
- } |
- } |
- |
- bool get isEmpty => end == start; |
- |
- int get length => |
- start > end ? size + end - start : end - start; |
- |
- int get linearLength => |
- start > end ? size - start : end - start; |
- |
- int get free => |
- start > end ? start - end - 1 : size + start - end - 1; |
- |
- int get linearFree { |
- if (start > end) return start - end - 1; |
- if (start == 0) return size - end - 1; |
- return size - end; |
- } |
- |
- List<int> read(int bytes) { |
- if (bytes == null) { |
- bytes = length; |
- } else { |
- bytes = min(bytes, length); |
- } |
- if (bytes == 0) return null; |
- List<int> result = new Uint8List(bytes); |
- int bytesRead = 0; |
- // Loop over zero, one, or two linear data ranges. |
- while (bytesRead < bytes) { |
- int toRead = min(bytes - bytesRead, linearLength); |
- result.setRange(bytesRead, |
- bytesRead + toRead, |
- data, |
- start); |
- advanceStart(toRead); |
- bytesRead += toRead; |
- } |
- return result; |
- } |
- |
- int write(List<int> inputData, int offset, int bytes) { |
- if (bytes > free) { |
- bytes = free; |
- } |
- int written = 0; |
- int toWrite = min(bytes, linearFree); |
- // Loop over zero, one, or two linear data ranges. |
- while (toWrite > 0) { |
- data.setRange(end, end + toWrite, inputData, offset); |
- advanceEnd(toWrite); |
- offset += toWrite; |
- written += toWrite; |
- toWrite = min(bytes - written, linearFree); |
- } |
- return written; |
- } |
- |
- int writeFromSource(List<int> getData(int requested)) { |
- int written = 0; |
- int toWrite = linearFree; |
- // Loop over zero, one, or two linear data ranges. |
- while (toWrite > 0) { |
- // Source returns at most toWrite bytes, and it returns null when empty. |
- var inputData = getData(toWrite); |
- if (inputData == null || inputData.length == 0) break; |
- var len = inputData.length; |
- data.setRange(end, end + len, inputData); |
- advanceEnd(len); |
- written += len; |
- toWrite = linearFree; |
- } |
- return written; |
- } |
- |
- bool readToSocket(RawSocket socket) { |
- // Loop over zero, one, or two linear data ranges. |
- while (true) { |
- var toWrite = linearLength; |
- if (toWrite == 0) return false; |
- int bytes = socket.write(data, start, toWrite); |
- advanceStart(bytes); |
- if (bytes < toWrite) { |
- // The socket has blocked while we have data to write. |
- return true; |
- } |
- } |
- } |
-} |
- |
- |
-abstract class _SecureFilter { |
- external factory _SecureFilter(); |
- |
- void connect(String hostName, |
- SecurityContext context, |
- bool is_server, |
- bool requestClientCertificate, |
- bool requireClientCertificate, |
- Uint8List protocols); |
- void destroy(); |
- void handshake(); |
- String selectedProtocol(); |
- void rehandshake(); |
- void renegotiate(bool useSessionCache, |
- bool requestClientCertificate, |
- bool requireClientCertificate); |
- void init(); |
- X509Certificate get peerCertificate; |
- int processBuffer(int bufferIndex); |
- void registerBadCertificateCallback(Function callback); |
- void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); |
- |
- // This call may cause a reference counted pointer in the native |
- // implementation to be retained. It should only be called when the resulting |
- // value is passed to the IO service through a call to dispatch(). |
- int _pointer(); |
- |
- 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._(this.type, this.message, 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); |
-} |