| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of dart.io; | |
| 6 | |
| 7 /** | |
| 8 * A high-level class for communicating securely over a TCP socket, using | |
| 9 * TLS and SSL. The [SecureSocket] exposes both a [Stream] and an | |
| 10 * [IOSink] interface, making it ideal for using together with | |
| 11 * other [Stream]s. | |
| 12 */ | |
| 13 abstract class SecureSocket implements Socket { | |
| 14 external factory SecureSocket._(RawSecureSocket rawSocket); | |
| 15 | |
| 16 /** | |
| 17 * Constructs a new secure client socket and connects it to the given | |
| 18 * [host] on port [port]. The returned Future will complete with a | |
| 19 * [SecureSocket] that is connected and ready for subscription. | |
| 20 * | |
| 21 * The certificate provided by the server is checked | |
| 22 * using the trusted certificates set in the SecurityContext object. | |
| 23 * The default SecurityContext object contains a built-in set of trusted | |
| 24 * root certificates for well-known certificate authorities. | |
| 25 * | |
| 26 * [onBadCertificate] is an optional handler for unverifiable certificates. | |
| 27 * The handler receives the [X509Certificate], and can inspect it and | |
| 28 * decide (or let the user decide) whether to accept | |
| 29 * the connection or not. The handler should return true | |
| 30 * to continue the [SecureSocket] connection. | |
| 31 */ | |
| 32 static Future<SecureSocket> connect( | |
| 33 host, | |
| 34 int port, | |
| 35 {SecurityContext context, | |
| 36 bool onBadCertificate(X509Certificate certificate), | |
| 37 List<String> supportedProtocols}) { | |
| 38 return RawSecureSocket.connect(host, | |
| 39 port, | |
| 40 context: context, | |
| 41 onBadCertificate: onBadCertificate, | |
| 42 supportedProtocols: supportedProtocols) | |
| 43 .then((rawSocket) => new SecureSocket._(rawSocket)); | |
| 44 } | |
| 45 | |
| 46 /** | |
| 47 * Takes an already connected [socket] and starts client side TLS | |
| 48 * handshake to make the communication secure. When the returned | |
| 49 * future completes the [SecureSocket] has completed the TLS | |
| 50 * handshake. Using this function requires that the other end of the | |
| 51 * connection is prepared for TLS handshake. | |
| 52 * | |
| 53 * If the [socket] already has a subscription, this subscription | |
| 54 * will no longer receive and events. In most cases calling | |
| 55 * `pause` on this subscription before starting TLS handshake is | |
| 56 * the right thing to do. | |
| 57 * | |
| 58 * If the [host] argument is passed it will be used as the host name | |
| 59 * for the TLS handshake. If [host] is not passed the host name from | |
| 60 * the [socket] will be used. The [host] can be either a [String] or | |
| 61 * an [InternetAddress]. | |
| 62 * | |
| 63 * Calling this function will _not_ cause a DNS host lookup. If the | |
| 64 * [host] passed is a [String] the [InternetAddress] for the | |
| 65 * resulting [SecureSocket] will have the passed in [host] as its | |
| 66 * host value and the internet address of the already connected | |
| 67 * socket as its address value. | |
| 68 * | |
| 69 * See [connect] for more information on the arguments. | |
| 70 * | |
| 71 */ | |
| 72 static Future<SecureSocket> secure( | |
| 73 Socket socket, | |
| 74 {host, | |
| 75 SecurityContext context, | |
| 76 bool onBadCertificate(X509Certificate certificate)}) { | |
| 77 var completer = new Completer(); | |
| 78 (socket as dynamic)._detachRaw() | |
| 79 .then((detachedRaw) { | |
| 80 return RawSecureSocket.secure( | |
| 81 detachedRaw[0], | |
| 82 subscription: detachedRaw[1], | |
| 83 host: host, | |
| 84 context: context, | |
| 85 onBadCertificate: onBadCertificate); | |
| 86 }) | |
| 87 .then((raw) { | |
| 88 completer.complete(new SecureSocket._(raw)); | |
| 89 }); | |
| 90 return completer.future; | |
| 91 } | |
| 92 | |
| 93 /** | |
| 94 * Takes an already connected [socket] and starts server side TLS | |
| 95 * handshake to make the communication secure. When the returned | |
| 96 * future completes the [SecureSocket] has completed the TLS | |
| 97 * handshake. Using this function requires that the other end of the | |
| 98 * connection is going to start the TLS handshake. | |
| 99 * | |
| 100 * If the [socket] already has a subscription, this subscription | |
| 101 * will no longer receive and events. In most cases calling | |
| 102 * [:pause:] on this subscription before starting TLS handshake is | |
| 103 * the right thing to do. | |
| 104 * | |
| 105 * If some of the data of the TLS handshake has already been read | |
| 106 * from the socket this data can be passed in the [bufferedData] | |
| 107 * parameter. This data will be processed before any other data | |
| 108 * available on the socket. | |
| 109 * | |
| 110 * See [SecureServerSocket.bind] for more information on the | |
| 111 * arguments. | |
| 112 * | |
| 113 */ | |
| 114 static Future<SecureSocket> secureServer( | |
| 115 Socket socket, | |
| 116 SecurityContext context, | |
| 117 {List<int> bufferedData, | |
| 118 bool requestClientCertificate: false, | |
| 119 bool requireClientCertificate: false, | |
| 120 List<String> supportedProtocols}) { | |
| 121 var completer = new Completer(); | |
| 122 (socket as dynamic)._detachRaw() | |
| 123 .then((detachedRaw) { | |
| 124 return RawSecureSocket.secureServer( | |
| 125 detachedRaw[0], | |
| 126 context, | |
| 127 subscription: detachedRaw[1], | |
| 128 bufferedData: bufferedData, | |
| 129 requestClientCertificate: requestClientCertificate, | |
| 130 requireClientCertificate: requireClientCertificate, | |
| 131 supportedProtocols: supportedProtocols); | |
| 132 }) | |
| 133 .then((raw) { | |
| 134 completer.complete(new SecureSocket._(raw)); | |
| 135 }); | |
| 136 return completer.future; | |
| 137 } | |
| 138 | |
| 139 /** | |
| 140 * Get the peer certificate for a connected SecureSocket. If this | |
| 141 * SecureSocket is the server end of a secure socket connection, | |
| 142 * [peerCertificate] will return the client certificate, or null, if no | |
| 143 * client certificate was received. If it is the client end, | |
| 144 * [peerCertificate] will return the server's certificate. | |
| 145 */ | |
| 146 X509Certificate get peerCertificate; | |
| 147 | |
| 148 /** | |
| 149 * Get the protocol which was selected during protocol negotiation. | |
| 150 */ | |
| 151 String get selectedProtocol; | |
| 152 | |
| 153 /** | |
| 154 * Renegotiate an existing secure connection, renewing the session keys | |
| 155 * and possibly changing the connection properties. | |
| 156 * | |
| 157 * This repeats the SSL or TLS handshake, with options that allow clearing | |
| 158 * the session cache and requesting a client certificate. | |
| 159 */ | |
| 160 void renegotiate({bool useSessionCache: true, | |
| 161 bool requestClientCertificate: false, | |
| 162 bool requireClientCertificate: false}); | |
| 163 } | |
| 164 | |
| 165 | |
| 166 /** | |
| 167 * RawSecureSocket provides a secure (SSL or TLS) network connection. | |
| 168 * Client connections to a server are provided by calling | |
| 169 * RawSecureSocket.connect. A secure server, created with | |
| 170 * [RawSecureServerSocket], also returns RawSecureSocket objects representing | |
| 171 * the server end of a secure connection. | |
| 172 * The certificate provided by the server is checked | |
| 173 * using the trusted certificates set in the SecurityContext object. | |
| 174 * The default [SecurityContext] object contains a built-in set of trusted | |
| 175 * root certificates for well-known certificate authorities. | |
| 176 */ | |
| 177 abstract class RawSecureSocket implements RawSocket { | |
| 178 /** | |
| 179 * Constructs a new secure client socket and connect it to the given | |
| 180 * host on the given port. The returned [Future] is completed with the | |
| 181 * RawSecureSocket when it is connected and ready for subscription. | |
| 182 * | |
| 183 * The certificate provided by the server is checked using the trusted | |
| 184 * certificates set in the SecurityContext object If a certificate and key are | |
| 185 * set on the client, using [SecurityContext.useCertificateChain] and | |
| 186 * [SecurityContext.usePrivateKey], and the server asks for a client | |
| 187 * certificate, then that client certificate is sent to the server. | |
| 188 * | |
| 189 * [onBadCertificate] is an optional handler for unverifiable certificates. | |
| 190 * The handler receives the [X509Certificate], and can inspect it and | |
| 191 * decide (or let the user decide) whether to accept | |
| 192 * the connection or not. The handler should return true | |
| 193 * to continue the [RawSecureSocket] connection. | |
| 194 */ | |
| 195 static Future<RawSecureSocket> connect( | |
| 196 host, | |
| 197 int port, | |
| 198 {SecurityContext context, | |
| 199 bool onBadCertificate(X509Certificate certificate), | |
| 200 List<String> supportedProtocols}) { | |
| 201 _RawSecureSocket._verifyFields( | |
| 202 host, | |
| 203 port, | |
| 204 false, | |
| 205 false, | |
| 206 false, | |
| 207 onBadCertificate); | |
| 208 return RawSocket.connect(host, port) | |
| 209 .then((socket) { | |
| 210 return secure(socket, | |
| 211 context: context, | |
| 212 onBadCertificate: onBadCertificate, | |
| 213 supportedProtocols: supportedProtocols); | |
| 214 }); | |
| 215 } | |
| 216 | |
| 217 /** | |
| 218 * Takes an already connected [socket] and starts client side TLS | |
| 219 * handshake to make the communication secure. When the returned | |
| 220 * future completes the [RawSecureSocket] has completed the TLS | |
| 221 * handshake. Using this function requires that the other end of the | |
| 222 * connection is prepared for TLS handshake. | |
| 223 * | |
| 224 * If the [socket] already has a subscription, pass the existing | |
| 225 * subscription in the [subscription] parameter. The [secure] | |
| 226 * operation will take over the subscription by replacing the | |
| 227 * handlers with it own secure processing. The caller must not touch | |
| 228 * this subscription anymore. Passing a paused subscription is an | |
| 229 * error. | |
| 230 * | |
| 231 * If the [host] argument is passed it will be used as the host name | |
| 232 * for the TLS handshake. If [host] is not passed the host name from | |
| 233 * the [socket] will be used. The [host] can be either a [String] or | |
| 234 * an [InternetAddress]. | |
| 235 * | |
| 236 * Calling this function will _not_ cause a DNS host lookup. If the | |
| 237 * [host] passed is a [String] the [InternetAddress] for the | |
| 238 * resulting [SecureSocket] will have this passed in [host] as its | |
| 239 * host value and the internet address of the already connected | |
| 240 * socket as its address value. | |
| 241 * | |
| 242 * See [connect] for more information on the arguments. | |
| 243 * | |
| 244 */ | |
| 245 static Future<RawSecureSocket> secure( | |
| 246 RawSocket socket, | |
| 247 {StreamSubscription subscription, | |
| 248 host, | |
| 249 SecurityContext context, | |
| 250 bool onBadCertificate(X509Certificate certificate), | |
| 251 List<String> supportedProtocols}) { | |
| 252 socket.readEventsEnabled = false; | |
| 253 socket.writeEventsEnabled = false; | |
| 254 return _RawSecureSocket.connect( | |
| 255 host != null ? host : socket.address.host, | |
| 256 socket.port, | |
| 257 is_server: false, | |
| 258 socket: socket, | |
| 259 subscription: subscription, | |
| 260 context: context, | |
| 261 onBadCertificate: onBadCertificate, | |
| 262 supportedProtocols: supportedProtocols); | |
| 263 } | |
| 264 | |
| 265 /** | |
| 266 * Takes an already connected [socket] and starts server side TLS | |
| 267 * handshake to make the communication secure. When the returned | |
| 268 * future completes the [RawSecureSocket] has completed the TLS | |
| 269 * handshake. Using this function requires that the other end of the | |
| 270 * connection is going to start the TLS handshake. | |
| 271 * | |
| 272 * If the [socket] already has a subscription, pass the existing | |
| 273 * subscription in the [subscription] parameter. The [secureServer] | |
| 274 * operation will take over the subscription by replacing the | |
| 275 * handlers with it own secure processing. The caller must not touch | |
| 276 * this subscription anymore. Passing a paused subscription is an | |
| 277 * error. | |
| 278 * | |
| 279 * If some of the data of the TLS handshake has already been read | |
| 280 * from the socket this data can be passed in the [bufferedData] | |
| 281 * parameter. This data will be processed before any other data | |
| 282 * available on the socket. | |
| 283 * | |
| 284 * See [RawSecureServerSocket.bind] for more information on the | |
| 285 * arguments. | |
| 286 * | |
| 287 */ | |
| 288 static Future<RawSecureSocket> secureServer( | |
| 289 RawSocket socket, | |
| 290 SecurityContext context, | |
| 291 {StreamSubscription subscription, | |
| 292 List<int> bufferedData, | |
| 293 bool requestClientCertificate: false, | |
| 294 bool requireClientCertificate: false, | |
| 295 List<String> supportedProtocols}) { | |
| 296 socket.readEventsEnabled = false; | |
| 297 socket.writeEventsEnabled = false; | |
| 298 return _RawSecureSocket.connect( | |
| 299 socket.address, | |
| 300 socket.remotePort, | |
| 301 context: context, | |
| 302 is_server: true, | |
| 303 socket: socket, | |
| 304 subscription: subscription, | |
| 305 bufferedData: bufferedData, | |
| 306 requestClientCertificate: requestClientCertificate, | |
| 307 requireClientCertificate: requireClientCertificate, | |
| 308 supportedProtocols: supportedProtocols); | |
| 309 } | |
| 310 | |
| 311 /** | |
| 312 * Renegotiate an existing secure connection, renewing the session keys | |
| 313 * and possibly changing the connection properties. | |
| 314 * | |
| 315 * This repeats the SSL or TLS handshake, with options that allow clearing | |
| 316 * the session cache and requesting a client certificate. | |
| 317 */ | |
| 318 void renegotiate({bool useSessionCache: true, | |
| 319 bool requestClientCertificate: false, | |
| 320 bool requireClientCertificate: false}); | |
| 321 | |
| 322 /** | |
| 323 * Get the peer certificate for a connected RawSecureSocket. If this | |
| 324 * RawSecureSocket is the server end of a secure socket connection, | |
| 325 * [peerCertificate] will return the client certificate, or null, if no | |
| 326 * client certificate was received. If it is the client end, | |
| 327 * [peerCertificate] will return the server's certificate. | |
| 328 */ | |
| 329 X509Certificate get peerCertificate; | |
| 330 | |
| 331 /** | |
| 332 * Get the protocol which was selected during protocol negotiation. | |
| 333 */ | |
| 334 String get selectedProtocol; | |
| 335 } | |
| 336 | |
| 337 | |
| 338 /** | |
| 339 * X509Certificate represents an SSL certificate, with accessors to | |
| 340 * get the fields of the certificate. | |
| 341 */ | |
| 342 abstract class X509Certificate { | |
| 343 external factory X509Certificate._(); | |
| 344 | |
| 345 String get subject; | |
| 346 String get issuer; | |
| 347 DateTime get startValidity; | |
| 348 DateTime get endValidity; | |
| 349 } | |
| 350 | |
| 351 | |
| 352 class _FilterStatus { | |
| 353 bool progress = false; // The filter read or wrote data to the buffers. | |
| 354 bool readEmpty = true; // The read buffers and decryption filter are empty. | |
| 355 bool writeEmpty = true; // The write buffers and encryption filter are empty. | |
| 356 // These are set if a buffer changes state from empty or full. | |
| 357 bool readPlaintextNoLongerEmpty = false; | |
| 358 bool writePlaintextNoLongerFull = false; | |
| 359 bool readEncryptedNoLongerFull = false; | |
| 360 bool writeEncryptedNoLongerEmpty = false; | |
| 361 | |
| 362 _FilterStatus(); | |
| 363 } | |
| 364 | |
| 365 | |
| 366 class _RawSecureSocket extends Stream<RawSocketEvent> | |
| 367 implements RawSecureSocket { | |
| 368 // Status states | |
| 369 static final int HANDSHAKE = 201; | |
| 370 static final int CONNECTED = 202; | |
| 371 static final int CLOSED = 203; | |
| 372 | |
| 373 // Buffer identifiers. | |
| 374 // These must agree with those in the native C++ implementation. | |
| 375 static final int READ_PLAINTEXT = 0; | |
| 376 static final int WRITE_PLAINTEXT = 1; | |
| 377 static final int READ_ENCRYPTED = 2; | |
| 378 static final int WRITE_ENCRYPTED = 3; | |
| 379 static final int NUM_BUFFERS = 4; | |
| 380 | |
| 381 // Is a buffer identifier for an encrypted buffer? | |
| 382 static bool _isBufferEncrypted(int identifier) => identifier >= READ_ENCRYPTED
; | |
| 383 | |
| 384 RawSocket _socket; | |
| 385 final Completer<_RawSecureSocket> _handshakeComplete = | |
| 386 new Completer<_RawSecureSocket>(); | |
| 387 StreamController<RawSocketEvent> _controller; | |
| 388 Stream<RawSocketEvent> _stream; | |
| 389 StreamSubscription<RawSocketEvent> _socketSubscription; | |
| 390 List<int> _bufferedData; | |
| 391 int _bufferedDataIndex = 0; | |
| 392 final InternetAddress address; | |
| 393 final bool is_server; | |
| 394 SecurityContext context; | |
| 395 final bool requestClientCertificate; | |
| 396 final bool requireClientCertificate; | |
| 397 final Function onBadCertificate; | |
| 398 | |
| 399 var _status = HANDSHAKE; | |
| 400 bool _writeEventsEnabled = true; | |
| 401 bool _readEventsEnabled = true; | |
| 402 int _pauseCount = 0; | |
| 403 bool _pendingReadEvent = false; | |
| 404 bool _socketClosedRead = false; // The network socket is closed for reading. | |
| 405 bool _socketClosedWrite = false; // The network socket is closed for writing. | |
| 406 bool _closedRead = false; // The secure socket has fired an onClosed event. | |
| 407 bool _closedWrite = false; // The secure socket has been closed for writing. | |
| 408 Completer _closeCompleter = new Completer(); // The network socket is gone. | |
| 409 _FilterStatus _filterStatus = new _FilterStatus(); | |
| 410 bool _connectPending = true; | |
| 411 bool _filterPending = false; | |
| 412 bool _filterActive = false; | |
| 413 | |
| 414 _SecureFilter _secureFilter = new _SecureFilter(); | |
| 415 String _selectedProtocol; | |
| 416 | |
| 417 static Future<_RawSecureSocket> connect( | |
| 418 dynamic/*String|InternetAddress*/ host, | |
| 419 int requestedPort, | |
| 420 {bool is_server, | |
| 421 SecurityContext context, | |
| 422 RawSocket socket, | |
| 423 StreamSubscription subscription, | |
| 424 List<int> bufferedData, | |
| 425 bool requestClientCertificate: false, | |
| 426 bool requireClientCertificate: false, | |
| 427 bool onBadCertificate(X509Certificate certificate), | |
| 428 List<String> supportedProtocols}) { | |
| 429 _verifyFields(host, requestedPort, is_server, | |
| 430 requestClientCertificate, requireClientCertificate, | |
| 431 onBadCertificate); | |
| 432 if (host is InternetAddress) host = host.host; | |
| 433 InternetAddress address = socket.address; | |
| 434 if (host != null) { | |
| 435 address = InternetAddress._cloneWithNewHost(address, host); | |
| 436 } | |
| 437 return new _RawSecureSocket(address, | |
| 438 requestedPort, | |
| 439 is_server, | |
| 440 context, | |
| 441 socket, | |
| 442 subscription, | |
| 443 bufferedData, | |
| 444 requestClientCertificate, | |
| 445 requireClientCertificate, | |
| 446 onBadCertificate, | |
| 447 supportedProtocols) | |
| 448 ._handshakeComplete.future; | |
| 449 } | |
| 450 | |
| 451 _RawSecureSocket( | |
| 452 this.address, | |
| 453 int requestedPort, | |
| 454 this.is_server, | |
| 455 this.context, | |
| 456 RawSocket this._socket, | |
| 457 this._socketSubscription, | |
| 458 this._bufferedData, | |
| 459 this.requestClientCertificate, | |
| 460 this.requireClientCertificate, | |
| 461 this.onBadCertificate(X509Certificate certificate), | |
| 462 List<String> supportedProtocols) { | |
| 463 if (context == null) { | |
| 464 context = SecurityContext.defaultContext; | |
| 465 } | |
| 466 _controller = new StreamController<RawSocketEvent>( | |
| 467 sync: true, | |
| 468 onListen: _onSubscriptionStateChange, | |
| 469 onPause: _onPauseStateChange, | |
| 470 onResume: _onPauseStateChange, | |
| 471 onCancel: _onSubscriptionStateChange); | |
| 472 _stream = _controller.stream; | |
| 473 // Throw an ArgumentError if any field is invalid. After this, all | |
| 474 // errors will be reported through the future or the stream. | |
| 475 _secureFilter.init(); | |
| 476 _secureFilter.registerHandshakeCompleteCallback( | |
| 477 _secureHandshakeCompleteHandler); | |
| 478 if (onBadCertificate != null) { | |
| 479 _secureFilter.registerBadCertificateCallback(_onBadCertificateWrapper); | |
| 480 } | |
| 481 _socket.readEventsEnabled = true; | |
| 482 _socket.writeEventsEnabled = false; | |
| 483 if (_socketSubscription == null) { | |
| 484 // If a current subscription is provided use this otherwise | |
| 485 // create a new one. | |
| 486 _socketSubscription = _socket.listen(_eventDispatcher, | |
| 487 onError: _reportError, | |
| 488 onDone: _doneHandler); | |
| 489 } else { | |
| 490 if (_socketSubscription.isPaused) { | |
| 491 _socket.close(); | |
| 492 throw new ArgumentError( | |
| 493 "Subscription passed to TLS upgrade is paused"); | |
| 494 } | |
| 495 // If we are upgrading a socket that is already closed for read, | |
| 496 // report an error as if we received READ_CLOSED during the handshake. | |
| 497 dynamic s = _socket; // Cast to dynamic to avoid warning. | |
| 498 if (s._socket.closedReadEventSent) { | |
| 499 _eventDispatcher(RawSocketEvent.READ_CLOSED); | |
| 500 } | |
| 501 _socketSubscription | |
| 502 ..onData(_eventDispatcher) | |
| 503 ..onError(_reportError) | |
| 504 ..onDone(_doneHandler); | |
| 505 } | |
| 506 try { | |
| 507 var encodedProtocols = | |
| 508 SecurityContext._protocolsToLengthEncoding(supportedProtocols); | |
| 509 _secureFilter.connect(address.host, | |
| 510 context, | |
| 511 is_server, | |
| 512 requestClientCertificate || | |
| 513 requireClientCertificate, | |
| 514 requireClientCertificate, | |
| 515 encodedProtocols); | |
| 516 _secureHandshake(); | |
| 517 } catch (e, s) { | |
| 518 _reportError(e, s); | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 StreamSubscription<RawSocketEvent> listen(void onData(RawSocketEvent data), | |
| 523 {Function onError, | |
| 524 void onDone(), | |
| 525 bool cancelOnError}) { | |
| 526 _sendWriteEvent(); | |
| 527 return _stream.listen(onData, | |
| 528 onError: onError, | |
| 529 onDone: onDone, | |
| 530 cancelOnError: cancelOnError); | |
| 531 } | |
| 532 | |
| 533 static void _verifyFields(host, | |
| 534 int requestedPort, | |
| 535 bool is_server, | |
| 536 bool requestClientCertificate, | |
| 537 bool requireClientCertificate, | |
| 538 Function onBadCertificate) { | |
| 539 if (host is! String && host is! InternetAddress) { | |
| 540 throw new ArgumentError("host is not a String or an InternetAddress"); | |
| 541 } | |
| 542 if (requestedPort is! int) { | |
| 543 throw new ArgumentError("requestedPort is not an int"); | |
| 544 } | |
| 545 if (requestedPort < 0 || requestedPort > 65535) { | |
| 546 throw new ArgumentError("requestedPort is not in the range 0..65535"); | |
| 547 } | |
| 548 if (requestClientCertificate is! bool) { | |
| 549 throw new ArgumentError("requestClientCertificate is not a bool"); | |
| 550 } | |
| 551 if (requireClientCertificate is! bool) { | |
| 552 throw new ArgumentError("requireClientCertificate is not a bool"); | |
| 553 } | |
| 554 if (onBadCertificate != null && onBadCertificate is! Function) { | |
| 555 throw new ArgumentError("onBadCertificate is not null or a Function"); | |
| 556 } | |
| 557 } | |
| 558 | |
| 559 int get port => _socket.port; | |
| 560 | |
| 561 InternetAddress get remoteAddress => _socket.remoteAddress; | |
| 562 | |
| 563 int get remotePort => _socket.remotePort; | |
| 564 | |
| 565 void set _owner(owner) { | |
| 566 (_socket as dynamic)._owner = owner; | |
| 567 } | |
| 568 | |
| 569 int available() { | |
| 570 return _status != CONNECTED ? 0 | |
| 571 : _secureFilter.buffers[READ_PLAINTEXT].length; | |
| 572 } | |
| 573 | |
| 574 Future<RawSecureSocket> close() { | |
| 575 shutdown(SocketDirection.BOTH); | |
| 576 return _closeCompleter.future; | |
| 577 } | |
| 578 | |
| 579 void _completeCloseCompleter([dummy]) { | |
| 580 if (!_closeCompleter.isCompleted) _closeCompleter.complete(this); | |
| 581 } | |
| 582 | |
| 583 void _close() { | |
| 584 _closedWrite = true; | |
| 585 _closedRead = true; | |
| 586 if (_socket != null) { | |
| 587 _socket.close().then(_completeCloseCompleter); | |
| 588 } else { | |
| 589 _completeCloseCompleter(); | |
| 590 } | |
| 591 _socketClosedWrite = true; | |
| 592 _socketClosedRead = true; | |
| 593 if (!_filterActive && _secureFilter != null) { | |
| 594 _secureFilter.destroy(); | |
| 595 _secureFilter = null; | |
| 596 } | |
| 597 if (_socketSubscription != null) { | |
| 598 _socketSubscription.cancel(); | |
| 599 } | |
| 600 _controller.close(); | |
| 601 _status = CLOSED; | |
| 602 } | |
| 603 | |
| 604 void shutdown(SocketDirection direction) { | |
| 605 if (direction == SocketDirection.SEND || | |
| 606 direction == SocketDirection.BOTH) { | |
| 607 _closedWrite = true; | |
| 608 if (_filterStatus.writeEmpty) { | |
| 609 _socket.shutdown(SocketDirection.SEND); | |
| 610 _socketClosedWrite = true; | |
| 611 if (_closedRead) { | |
| 612 _close(); | |
| 613 } | |
| 614 } | |
| 615 } | |
| 616 if (direction == SocketDirection.RECEIVE || | |
| 617 direction == SocketDirection.BOTH) { | |
| 618 _closedRead = true; | |
| 619 _socketClosedRead = true; | |
| 620 _socket.shutdown(SocketDirection.RECEIVE); | |
| 621 if (_socketClosedWrite) { | |
| 622 _close(); | |
| 623 } | |
| 624 } | |
| 625 } | |
| 626 | |
| 627 bool get writeEventsEnabled => _writeEventsEnabled; | |
| 628 | |
| 629 void set writeEventsEnabled(bool value) { | |
| 630 _writeEventsEnabled = value; | |
| 631 if (value) { | |
| 632 Timer.run(() => _sendWriteEvent()); | |
| 633 } | |
| 634 } | |
| 635 | |
| 636 bool get readEventsEnabled => _readEventsEnabled; | |
| 637 | |
| 638 void set readEventsEnabled(bool value) { | |
| 639 _readEventsEnabled = value; | |
| 640 _scheduleReadEvent(); | |
| 641 } | |
| 642 | |
| 643 List<int> read([int length]) { | |
| 644 if (length != null && (length is! int || length < 0)) { | |
| 645 throw new ArgumentError( | |
| 646 "Invalid length parameter in SecureSocket.read (length: $length)"); | |
| 647 } | |
| 648 if (_closedRead) { | |
| 649 throw new SocketException("Reading from a closed socket"); | |
| 650 } | |
| 651 if (_status != CONNECTED) { | |
| 652 return null; | |
| 653 } | |
| 654 var result = _secureFilter.buffers[READ_PLAINTEXT].read(length); | |
| 655 _scheduleFilter(); | |
| 656 return result; | |
| 657 } | |
| 658 | |
| 659 // Write the data to the socket, and schedule the filter to encrypt it. | |
| 660 int write(List<int> data, [int offset, int bytes]) { | |
| 661 if (bytes != null && (bytes is! int || bytes < 0)) { | |
| 662 throw new ArgumentError( | |
| 663 "Invalid bytes parameter in SecureSocket.read (bytes: $bytes)"); | |
| 664 } | |
| 665 if (offset != null && (offset is! int || offset < 0)) { | |
| 666 throw new ArgumentError( | |
| 667 "Invalid offset parameter in SecureSocket.read (offset: $offset)"); | |
| 668 } | |
| 669 if (_closedWrite) { | |
| 670 _controller.addError(new SocketException("Writing to a closed socket")); | |
| 671 return 0; | |
| 672 } | |
| 673 if (_status != CONNECTED) return 0; | |
| 674 if (offset == null) offset = 0; | |
| 675 if (bytes == null) bytes = data.length - offset; | |
| 676 | |
| 677 int written = | |
| 678 _secureFilter.buffers[WRITE_PLAINTEXT].write(data, offset, bytes); | |
| 679 if (written > 0) { | |
| 680 _filterStatus.writeEmpty = false; | |
| 681 } | |
| 682 _scheduleFilter(); | |
| 683 return written; | |
| 684 } | |
| 685 | |
| 686 X509Certificate get peerCertificate => _secureFilter.peerCertificate; | |
| 687 | |
| 688 String get selectedProtocol => _selectedProtocol; | |
| 689 | |
| 690 bool _onBadCertificateWrapper(X509Certificate certificate) { | |
| 691 if (onBadCertificate == null) return false; | |
| 692 var result = onBadCertificate(certificate); | |
| 693 if (result is bool) return result; | |
| 694 throw new HandshakeException( | |
| 695 "onBadCertificate callback returned non-boolean $result"); | |
| 696 } | |
| 697 | |
| 698 bool setOption(SocketOption option, bool enabled) { | |
| 699 if (_socket == null) return false; | |
| 700 return _socket.setOption(option, enabled); | |
| 701 } | |
| 702 | |
| 703 void _eventDispatcher(RawSocketEvent event) { | |
| 704 try { | |
| 705 if (event == RawSocketEvent.READ) { | |
| 706 _readHandler(); | |
| 707 } else if (event == RawSocketEvent.WRITE) { | |
| 708 _writeHandler(); | |
| 709 } else if (event == RawSocketEvent.READ_CLOSED) { | |
| 710 _closeHandler(); | |
| 711 } | |
| 712 } catch (e, stackTrace) { | |
| 713 _reportError(e, stackTrace); | |
| 714 } | |
| 715 } | |
| 716 | |
| 717 void _readHandler() { | |
| 718 _readSocket(); | |
| 719 _scheduleFilter(); | |
| 720 } | |
| 721 | |
| 722 void _writeHandler() { | |
| 723 _writeSocket(); | |
| 724 _scheduleFilter(); | |
| 725 } | |
| 726 | |
| 727 void _doneHandler() { | |
| 728 if (_filterStatus.readEmpty) { | |
| 729 _close(); | |
| 730 } | |
| 731 } | |
| 732 | |
| 733 void _reportError(e, [StackTrace stackTrace]) { | |
| 734 if (_status == CLOSED) { | |
| 735 return; | |
| 736 } else if (_connectPending) { | |
| 737 // _connectPending is true until the handshake has completed, and the | |
| 738 // _handshakeComplete future returned from SecureSocket.connect has | |
| 739 // completed. Before this point, we must complete it with an error. | |
| 740 _handshakeComplete.completeError(e, stackTrace); | |
| 741 } else { | |
| 742 _controller.addError(e, stackTrace); | |
| 743 } | |
| 744 _close(); | |
| 745 } | |
| 746 | |
| 747 void _closeHandler() { | |
| 748 if (_status == CONNECTED) { | |
| 749 if (_closedRead) return; | |
| 750 _socketClosedRead = true; | |
| 751 if (_filterStatus.readEmpty) { | |
| 752 _closedRead = true; | |
| 753 _controller.add(RawSocketEvent.READ_CLOSED); | |
| 754 if (_socketClosedWrite) { | |
| 755 _close(); | |
| 756 } | |
| 757 } else { | |
| 758 _scheduleFilter(); | |
| 759 } | |
| 760 } else if (_status == HANDSHAKE) { | |
| 761 _socketClosedRead = true; | |
| 762 if (_filterStatus.readEmpty) { | |
| 763 _reportError( | |
| 764 new HandshakeException('Connection terminated during handshake'), | |
| 765 null); | |
| 766 } else { | |
| 767 _secureHandshake(); | |
| 768 } | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 void _secureHandshake() { | |
| 773 try { | |
| 774 _secureFilter.handshake(); | |
| 775 _filterStatus.writeEmpty = false; | |
| 776 _readSocket(); | |
| 777 _writeSocket(); | |
| 778 _scheduleFilter(); | |
| 779 } catch (e, stackTrace) { | |
| 780 _reportError(e, stackTrace); | |
| 781 } | |
| 782 } | |
| 783 | |
| 784 void renegotiate({bool useSessionCache: true, | |
| 785 bool requestClientCertificate: false, | |
| 786 bool requireClientCertificate: false}) { | |
| 787 if (_status != CONNECTED) { | |
| 788 throw new HandshakeException( | |
| 789 "Called renegotiate on a non-connected socket"); | |
| 790 } | |
| 791 _secureFilter.renegotiate(useSessionCache, | |
| 792 requestClientCertificate, | |
| 793 requireClientCertificate); | |
| 794 _status = HANDSHAKE; | |
| 795 _filterStatus.writeEmpty = false; | |
| 796 _scheduleFilter(); | |
| 797 } | |
| 798 | |
| 799 void _secureHandshakeCompleteHandler() { | |
| 800 _status = CONNECTED; | |
| 801 if (_connectPending) { | |
| 802 _connectPending = false; | |
| 803 try { | |
| 804 _selectedProtocol = _secureFilter.selectedProtocol(); | |
| 805 // We don't want user code to run synchronously in this callback. | |
| 806 Timer.run(() => _handshakeComplete.complete(this)); | |
| 807 } catch (error, stack) { | |
| 808 _handshakeComplete.completeError(error, stack); | |
| 809 } | |
| 810 } | |
| 811 } | |
| 812 | |
| 813 void _onPauseStateChange() { | |
| 814 if (_controller.isPaused) { | |
| 815 _pauseCount++; | |
| 816 } else { | |
| 817 _pauseCount--; | |
| 818 if (_pauseCount == 0) { | |
| 819 _scheduleReadEvent(); | |
| 820 _sendWriteEvent(); // Can send event synchronously. | |
| 821 } | |
| 822 } | |
| 823 | |
| 824 if (!_socketClosedRead || !_socketClosedWrite) { | |
| 825 if (_controller.isPaused) { | |
| 826 _socketSubscription.pause(); | |
| 827 } else { | |
| 828 _socketSubscription.resume(); | |
| 829 } | |
| 830 } | |
| 831 } | |
| 832 | |
| 833 void _onSubscriptionStateChange() { | |
| 834 if (_controller.hasListener) { | |
| 835 // TODO(ajohnsen): Do something here? | |
| 836 } | |
| 837 } | |
| 838 | |
| 839 void _scheduleFilter() { | |
| 840 _filterPending = true; | |
| 841 _tryFilter(); | |
| 842 } | |
| 843 | |
| 844 void _tryFilter() { | |
| 845 if (_status == CLOSED) { | |
| 846 return; | |
| 847 } | |
| 848 if (_filterPending && !_filterActive) { | |
| 849 _filterActive = true; | |
| 850 _filterPending = false; | |
| 851 _pushAllFilterStages().then((status) { | |
| 852 _filterStatus = status; | |
| 853 _filterActive = false; | |
| 854 if (_status == CLOSED) { | |
| 855 _secureFilter.destroy(); | |
| 856 _secureFilter = null; | |
| 857 return; | |
| 858 } | |
| 859 _socket.readEventsEnabled = true; | |
| 860 if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) { | |
| 861 // Checks for and handles all cases of partially closed sockets. | |
| 862 shutdown(SocketDirection.SEND); | |
| 863 if (_status == CLOSED) { | |
| 864 return; | |
| 865 } | |
| 866 } | |
| 867 if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) { | |
| 868 if (_status == HANDSHAKE) { | |
| 869 _secureFilter.handshake(); | |
| 870 if (_status == HANDSHAKE) { | |
| 871 throw new HandshakeException( | |
| 872 'Connection terminated during handshake'); | |
| 873 } | |
| 874 } | |
| 875 _closeHandler(); | |
| 876 } | |
| 877 if (_status == CLOSED) { | |
| 878 return; | |
| 879 } | |
| 880 if (_filterStatus.progress) { | |
| 881 _filterPending = true; | |
| 882 if (_filterStatus.writeEncryptedNoLongerEmpty) { | |
| 883 _writeSocket(); | |
| 884 } | |
| 885 if (_filterStatus.writePlaintextNoLongerFull) { | |
| 886 _sendWriteEvent(); | |
| 887 } | |
| 888 if (_filterStatus.readEncryptedNoLongerFull) { | |
| 889 _readSocket(); | |
| 890 } | |
| 891 if (_filterStatus.readPlaintextNoLongerEmpty) { | |
| 892 _scheduleReadEvent(); | |
| 893 } | |
| 894 if (_status == HANDSHAKE) { | |
| 895 _secureHandshake(); | |
| 896 } | |
| 897 } | |
| 898 _tryFilter(); | |
| 899 }).catchError(_reportError); | |
| 900 } | |
| 901 } | |
| 902 | |
| 903 List<int> _readSocketOrBufferedData(int bytes) { | |
| 904 if (_bufferedData != null) { | |
| 905 if (bytes > _bufferedData.length - _bufferedDataIndex) { | |
| 906 bytes = _bufferedData.length - _bufferedDataIndex; | |
| 907 } | |
| 908 var result = _bufferedData.sublist(_bufferedDataIndex, | |
| 909 _bufferedDataIndex + bytes); | |
| 910 _bufferedDataIndex += bytes; | |
| 911 if (_bufferedData.length == _bufferedDataIndex) { | |
| 912 _bufferedData = null; | |
| 913 } | |
| 914 return result; | |
| 915 } else if (!_socketClosedRead) { | |
| 916 return _socket.read(bytes); | |
| 917 } else { | |
| 918 return null; | |
| 919 } | |
| 920 } | |
| 921 | |
| 922 void _readSocket() { | |
| 923 if (_status == CLOSED) return; | |
| 924 var buffer = _secureFilter.buffers[READ_ENCRYPTED]; | |
| 925 if (buffer.writeFromSource(_readSocketOrBufferedData) > 0) { | |
| 926 _filterStatus.readEmpty = false; | |
| 927 } else { | |
| 928 _socket.readEventsEnabled = false; | |
| 929 } | |
| 930 } | |
| 931 | |
| 932 void _writeSocket() { | |
| 933 if (_socketClosedWrite) return; | |
| 934 var buffer = _secureFilter.buffers[WRITE_ENCRYPTED]; | |
| 935 if (buffer.readToSocket(_socket)) { // Returns true if blocked | |
| 936 _socket.writeEventsEnabled = true; | |
| 937 } | |
| 938 } | |
| 939 | |
| 940 // If a read event should be sent, add it to the controller. | |
| 941 _scheduleReadEvent() { | |
| 942 if (!_pendingReadEvent && | |
| 943 _readEventsEnabled && | |
| 944 _pauseCount == 0 && | |
| 945 _secureFilter != null && | |
| 946 !_secureFilter.buffers[READ_PLAINTEXT].isEmpty) { | |
| 947 _pendingReadEvent = true; | |
| 948 Timer.run(_sendReadEvent); | |
| 949 } | |
| 950 } | |
| 951 | |
| 952 _sendReadEvent() { | |
| 953 _pendingReadEvent = false; | |
| 954 if (_status != CLOSED && | |
| 955 _readEventsEnabled && | |
| 956 _pauseCount == 0 && | |
| 957 _secureFilter != null && | |
| 958 !_secureFilter.buffers[READ_PLAINTEXT].isEmpty) { | |
| 959 _controller.add(RawSocketEvent.READ); | |
| 960 _scheduleReadEvent(); | |
| 961 } | |
| 962 } | |
| 963 | |
| 964 // If a write event should be sent, add it to the controller. | |
| 965 _sendWriteEvent() { | |
| 966 if (!_closedWrite && | |
| 967 _writeEventsEnabled && | |
| 968 _pauseCount == 0 && | |
| 969 _secureFilter != null && | |
| 970 _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) { | |
| 971 _writeEventsEnabled = false; | |
| 972 _controller.add(RawSocketEvent.WRITE); | |
| 973 } | |
| 974 } | |
| 975 | |
| 976 Future<_FilterStatus> _pushAllFilterStages() { | |
| 977 bool wasInHandshake = _status != CONNECTED; | |
| 978 List args = new List(2 + NUM_BUFFERS * 2); | |
| 979 args[0] = _secureFilter._pointer(); | |
| 980 args[1] = wasInHandshake; | |
| 981 var bufs = _secureFilter.buffers; | |
| 982 for (var i = 0; i < NUM_BUFFERS; ++i) { | |
| 983 args[2 * i + 2] = bufs[i].start; | |
| 984 args[2 * i + 3] = bufs[i].end; | |
| 985 } | |
| 986 | |
| 987 return _IOService._dispatch(_SSL_PROCESS_FILTER, args).then((response) { | |
| 988 if (response.length == 2) { | |
| 989 if (wasInHandshake) { | |
| 990 // If we're in handshake, throw a handshake error. | |
| 991 _reportError( | |
| 992 new HandshakeException('${response[1]} error ${response[0]}'), | |
| 993 null); | |
| 994 } else { | |
| 995 // If we're connected, throw a TLS error. | |
| 996 _reportError(new TlsException('${response[1]} error ${response[0]}'), | |
| 997 null); | |
| 998 } | |
| 999 } | |
| 1000 int start(int index) => response[2 * index]; | |
| 1001 int end(int index) => response[2 * index + 1]; | |
| 1002 | |
| 1003 _FilterStatus status = new _FilterStatus(); | |
| 1004 // Compute writeEmpty as "write plaintext buffer and write encrypted | |
| 1005 // buffer were empty when we started and are empty now". | |
| 1006 status.writeEmpty = bufs[WRITE_PLAINTEXT].isEmpty && | |
| 1007 start(WRITE_ENCRYPTED) == end(WRITE_ENCRYPTED); | |
| 1008 // If we were in handshake when this started, _writeEmpty may be false | |
| 1009 // because the handshake wrote data after we checked. | |
| 1010 if (wasInHandshake) status.writeEmpty = false; | |
| 1011 | |
| 1012 // Compute readEmpty as "both read buffers were empty when we started | |
| 1013 // and are empty now". | |
| 1014 status.readEmpty = bufs[READ_ENCRYPTED].isEmpty && | |
| 1015 start(READ_PLAINTEXT) == end(READ_PLAINTEXT); | |
| 1016 | |
| 1017 _ExternalBuffer buffer = bufs[WRITE_PLAINTEXT]; | |
| 1018 int new_start = start(WRITE_PLAINTEXT); | |
| 1019 if (new_start != buffer.start) { | |
| 1020 status.progress = true; | |
| 1021 if (buffer.free == 0) { | |
| 1022 status.writePlaintextNoLongerFull = true; | |
| 1023 } | |
| 1024 buffer.start = new_start; | |
| 1025 } | |
| 1026 buffer = bufs[READ_ENCRYPTED]; | |
| 1027 new_start = start(READ_ENCRYPTED); | |
| 1028 if (new_start != buffer.start) { | |
| 1029 status.progress = true; | |
| 1030 if (buffer.free == 0) { | |
| 1031 status.readEncryptedNoLongerFull = true; | |
| 1032 } | |
| 1033 buffer.start = new_start; | |
| 1034 } | |
| 1035 buffer = bufs[WRITE_ENCRYPTED]; | |
| 1036 int new_end = end(WRITE_ENCRYPTED); | |
| 1037 if (new_end != buffer.end) { | |
| 1038 status.progress = true; | |
| 1039 if (buffer.length == 0) { | |
| 1040 status.writeEncryptedNoLongerEmpty = true; | |
| 1041 } | |
| 1042 buffer.end = new_end; | |
| 1043 } | |
| 1044 buffer = bufs[READ_PLAINTEXT]; | |
| 1045 new_end = end(READ_PLAINTEXT); | |
| 1046 if (new_end != buffer.end) { | |
| 1047 status.progress = true; | |
| 1048 if (buffer.length == 0) { | |
| 1049 status.readPlaintextNoLongerEmpty = true; | |
| 1050 } | |
| 1051 buffer.end = new_end; | |
| 1052 } | |
| 1053 return status; | |
| 1054 }); | |
| 1055 } | |
| 1056 } | |
| 1057 | |
| 1058 | |
| 1059 /** | |
| 1060 * A circular buffer backed by an external byte array. Accessed from | |
| 1061 * both C++ and Dart code in an unsynchronized way, with one reading | |
| 1062 * and one writing. All updates to start and end are done by Dart code. | |
| 1063 */ | |
| 1064 class _ExternalBuffer { | |
| 1065 List data; // This will be a ExternalByteArray, backed by C allocated data. | |
| 1066 int start; | |
| 1067 int end; | |
| 1068 final size; | |
| 1069 | |
| 1070 _ExternalBuffer(this.size) { | |
| 1071 start = end = size ~/ 2; | |
| 1072 } | |
| 1073 | |
| 1074 void advanceStart(int bytes) { | |
| 1075 assert(start > end || start + bytes <= end); | |
| 1076 start += bytes; | |
| 1077 if (start >= size) { | |
| 1078 start -= size; | |
| 1079 assert(start <= end); | |
| 1080 assert(start < size); | |
| 1081 } | |
| 1082 } | |
| 1083 | |
| 1084 void advanceEnd(int bytes) { | |
| 1085 assert(start <= end || start > end + bytes); | |
| 1086 end += bytes; | |
| 1087 if (end >= size) { | |
| 1088 end -= size; | |
| 1089 assert(end < start); | |
| 1090 assert(end < size); | |
| 1091 } | |
| 1092 } | |
| 1093 | |
| 1094 bool get isEmpty => end == start; | |
| 1095 | |
| 1096 int get length => | |
| 1097 start > end ? size + end - start : end - start; | |
| 1098 | |
| 1099 int get linearLength => | |
| 1100 start > end ? size - start : end - start; | |
| 1101 | |
| 1102 int get free => | |
| 1103 start > end ? start - end - 1 : size + start - end - 1; | |
| 1104 | |
| 1105 int get linearFree { | |
| 1106 if (start > end) return start - end - 1; | |
| 1107 if (start == 0) return size - end - 1; | |
| 1108 return size - end; | |
| 1109 } | |
| 1110 | |
| 1111 List<int> read(int bytes) { | |
| 1112 if (bytes == null) { | |
| 1113 bytes = length; | |
| 1114 } else { | |
| 1115 bytes = min(bytes, length); | |
| 1116 } | |
| 1117 if (bytes == 0) return null; | |
| 1118 List<int> result = new Uint8List(bytes); | |
| 1119 int bytesRead = 0; | |
| 1120 // Loop over zero, one, or two linear data ranges. | |
| 1121 while (bytesRead < bytes) { | |
| 1122 int toRead = min(bytes - bytesRead, linearLength); | |
| 1123 result.setRange(bytesRead, | |
| 1124 bytesRead + toRead, | |
| 1125 data, | |
| 1126 start); | |
| 1127 advanceStart(toRead); | |
| 1128 bytesRead += toRead; | |
| 1129 } | |
| 1130 return result; | |
| 1131 } | |
| 1132 | |
| 1133 int write(List<int> inputData, int offset, int bytes) { | |
| 1134 if (bytes > free) { | |
| 1135 bytes = free; | |
| 1136 } | |
| 1137 int written = 0; | |
| 1138 int toWrite = min(bytes, linearFree); | |
| 1139 // Loop over zero, one, or two linear data ranges. | |
| 1140 while (toWrite > 0) { | |
| 1141 data.setRange(end, end + toWrite, inputData, offset); | |
| 1142 advanceEnd(toWrite); | |
| 1143 offset += toWrite; | |
| 1144 written += toWrite; | |
| 1145 toWrite = min(bytes - written, linearFree); | |
| 1146 } | |
| 1147 return written; | |
| 1148 } | |
| 1149 | |
| 1150 int writeFromSource(List<int> getData(int requested)) { | |
| 1151 int written = 0; | |
| 1152 int toWrite = linearFree; | |
| 1153 // Loop over zero, one, or two linear data ranges. | |
| 1154 while (toWrite > 0) { | |
| 1155 // Source returns at most toWrite bytes, and it returns null when empty. | |
| 1156 var inputData = getData(toWrite); | |
| 1157 if (inputData == null || inputData.length == 0) break; | |
| 1158 var len = inputData.length; | |
| 1159 data.setRange(end, end + len, inputData); | |
| 1160 advanceEnd(len); | |
| 1161 written += len; | |
| 1162 toWrite = linearFree; | |
| 1163 } | |
| 1164 return written; | |
| 1165 } | |
| 1166 | |
| 1167 bool readToSocket(RawSocket socket) { | |
| 1168 // Loop over zero, one, or two linear data ranges. | |
| 1169 while (true) { | |
| 1170 var toWrite = linearLength; | |
| 1171 if (toWrite == 0) return false; | |
| 1172 int bytes = socket.write(data, start, toWrite); | |
| 1173 advanceStart(bytes); | |
| 1174 if (bytes < toWrite) { | |
| 1175 // The socket has blocked while we have data to write. | |
| 1176 return true; | |
| 1177 } | |
| 1178 } | |
| 1179 } | |
| 1180 } | |
| 1181 | |
| 1182 | |
| 1183 abstract class _SecureFilter { | |
| 1184 external factory _SecureFilter(); | |
| 1185 | |
| 1186 void connect(String hostName, | |
| 1187 SecurityContext context, | |
| 1188 bool is_server, | |
| 1189 bool requestClientCertificate, | |
| 1190 bool requireClientCertificate, | |
| 1191 Uint8List protocols); | |
| 1192 void destroy(); | |
| 1193 void handshake(); | |
| 1194 String selectedProtocol(); | |
| 1195 void rehandshake(); | |
| 1196 void renegotiate(bool useSessionCache, | |
| 1197 bool requestClientCertificate, | |
| 1198 bool requireClientCertificate); | |
| 1199 void init(); | |
| 1200 X509Certificate get peerCertificate; | |
| 1201 int processBuffer(int bufferIndex); | |
| 1202 void registerBadCertificateCallback(Function callback); | |
| 1203 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); | |
| 1204 | |
| 1205 // This call may cause a reference counted pointer in the native | |
| 1206 // implementation to be retained. It should only be called when the resulting | |
| 1207 // value is passed to the IO service through a call to dispatch(). | |
| 1208 int _pointer(); | |
| 1209 | |
| 1210 List<_ExternalBuffer> get buffers; | |
| 1211 } | |
| 1212 | |
| 1213 /** A secure networking exception caused by a failure in the | |
| 1214 * TLS/SSL protocol. | |
| 1215 */ | |
| 1216 class TlsException implements IOException { | |
| 1217 final String type; | |
| 1218 final String message; | |
| 1219 final OSError osError; | |
| 1220 | |
| 1221 const TlsException([String message = "", | |
| 1222 OSError osError = null]) | |
| 1223 : this._("TlsException", message, osError); | |
| 1224 | |
| 1225 const TlsException._(this.type, this.message, this.osError); | |
| 1226 | |
| 1227 String toString() { | |
| 1228 StringBuffer sb = new StringBuffer(); | |
| 1229 sb.write(type); | |
| 1230 if (!message.isEmpty) { | |
| 1231 sb.write(": $message"); | |
| 1232 if (osError != null) { | |
| 1233 sb.write(" ($osError)"); | |
| 1234 } | |
| 1235 } else if (osError != null) { | |
| 1236 sb.write(": $osError"); | |
| 1237 } | |
| 1238 return sb.toString(); | |
| 1239 } | |
| 1240 } | |
| 1241 | |
| 1242 | |
| 1243 /** | |
| 1244 * An exception that happens in the handshake phase of establishing | |
| 1245 * a secure network connection. | |
| 1246 */ | |
| 1247 class HandshakeException extends TlsException { | |
| 1248 const HandshakeException([String message = "", | |
| 1249 OSError osError = null]) | |
| 1250 : super._("HandshakeException", message, osError); | |
| 1251 } | |
| 1252 | |
| 1253 | |
| 1254 /** | |
| 1255 * An exception that happens in the handshake phase of establishing | |
| 1256 * a secure network connection, when looking up or verifying a | |
| 1257 * certificate. | |
| 1258 */ | |
| 1259 class CertificateException extends TlsException { | |
| 1260 const CertificateException([String message = "", | |
| 1261 OSError osError = null]) | |
| 1262 : super._("CertificateException", message, osError); | |
| 1263 } | |
| OLD | NEW |