Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(164)

Side by Side Diff: pkg/dev_compiler/tool/input_sdk/lib/io/secure_socket.dart

Issue 2698353003: unfork DDC's copy of most SDK libraries (Closed)
Patch Set: revert core_patch Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698