| 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);
|
| -}
|
|
|