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

Side by Side Diff: sdk/lib/io/secure_socket.dart

Issue 12316036: Merge IO v2 branch to bleeding edge (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebased to r18818 Created 7 years, 10 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 | Annotate | Revision Log
« no previous file with comments | « sdk/lib/io/secure_server_socket.dart ('k') | sdk/lib/io/socket.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.io; 5 part of dart.io;
6 6
7 /** 7 /**
8 * SecureSocket provides a secure (SSL or TLS) client connection to a server. 8 * A high-level class for communicating securely over a TCP socket, using
9 * The certificate provided by the server is checked 9 * TLS and SSL. The [SecureSocket] exposes both a [Stream] and an
10 * using the certificate database (optionally) provided in initialize(). 10 * [IOSink] interface, making it ideal for using together with
11 * other [Stream]s.
11 */ 12 */
12 abstract class SecureSocket implements Socket { 13 abstract class SecureSocket implements Socket {
14 external factory SecureSocket._(RawSecureSocket rawSocket);
15
13 /** 16 /**
14 * Constructs a new secure client socket and connect it to the given 17 * Constructs a new secure client socket and connect it to the given
15 * host on the given port. The returned socket is not yet connected 18 * [host] on port [port]. The returned Future will complete with a
16 * but ready for registration of callbacks. If sendClientCertificate is 19 * [SecureSocket] that is connected and ready for subscription.
17 * set to true, the socket will send a client certificate if one is 20 *
18 * requested by the server. If clientCertificate is the nickname of 21 * If [sendClientCertificate] is set to true, the socket will send a client
19 * a certificate in the certificate database, that certificate will be sent. 22 * certificate if one is requested by the server.
20 * If clientCertificate is null, which is the usual use case, an 23 *
24 * If [certificateName] is the nickname of a certificate in the certificate
25 * database, that certificate will be sent.
26 *
27 * If [certificateName] is null, which is the usual use case, an
21 * appropriate certificate will be searched for in the database and 28 * appropriate certificate will be searched for in the database and
22 * sent automatically, based on what the server says it will accept. 29 * sent automatically, based on what the server says it will accept.
30 *
31 * [onBadCertificate] is an optional handler for unverifiable certificates.
32 * The handler receives the [X509Certificate], and can inspect it and
33 * decide (or let the user decide) whether to accept
34 * the connection or not. The handler should return true
35 * to continue the [SecureSocket] connection.
23 */ 36 */
24 factory SecureSocket(String host, 37 static Future<SecureSocket> connect(
25 int port, 38 String host,
26 {bool sendClientCertificate: false, 39 int port,
27 String certificateName}) { 40 {bool sendClientCertificate: false,
28 return new _SecureSocket(host, 41 String certificateName,
29 port, 42 bool onBadCertificate(X509Certificate certificate)}) {
30 certificateName, 43 return RawSecureSocket.connect(host,
31 is_server: false, 44 port,
32 sendClientCertificate: sendClientCertificate); 45 sendClientCertificate: sendClientCertificate,
46 certificateName: certificateName,
47 onBadCertificate: onBadCertificate)
48 .then((rawSocket) => new SecureSocket._(rawSocket));
33 } 49 }
34 50
35 /** 51 /**
36 * Install a handler for unverifiable certificates. The handler can inspect 52 * Get the peer certificate for a connected SecureSocket. If this
37 * the certificate, and decide (or let the user decide) whether to accept 53 * SecureSocket is the server end of a secure socket connection,
38 * the connection or not. The callback should return true 54 * [peerCertificate] will return the client certificate, or null, if no
39 * to continue the SecureSocket connection. 55 * client certificate was received. If it is the client end,
40 */ 56 * [peerCertificate] will return the server's certificate.
41 void set onBadCertificate(bool callback(X509Certificate certificate));
42
43 /**
44 * Get the peerCertificate for a connected secure socket. For a server
45 * socket, this will return the client certificate, or null, if no
46 * client certificate was received. For a client socket, this
47 * will return the server's certificate.
48 */ 57 */
49 X509Certificate get peerCertificate; 58 X509Certificate get peerCertificate;
50 59
51 /** 60 /**
52 * Initializes the NSS library. If [initialize] is not called, the library 61 * Initializes the NSS library. If [initialize] is not called, the library
53 * is automatically initialized as if [initialize] were called with no 62 * is automatically initialized as if [initialize] were called with no
54 * arguments. 63 * arguments.
55 * 64 *
56 * The optional argument [database] is the path to a certificate database 65 * The optional argument [database] is the path to a certificate database
57 * containing root certificates for verifying certificate paths on 66 * containing root certificates for verifying certificate paths on
58 * client connections, and server certificates to provide on server 67 * client connections, and server certificates to provide on server
59 * connections. The argument [password] should be used when creating 68 * connections. The argument [password] should be used when creating
60 * secure server sockets, to allow the private key of the server 69 * secure server sockets, to allow the private key of the server
61 * certificate to be fetched. If [useBuiltinRoots] is true (the default), 70 * certificate to be fetched. If [useBuiltinRoots] is true (the default),
(...skipping 19 matching lines...) Expand all
81 * front of the absolute path of the database directory, or setting the 90 * front of the absolute path of the database directory, or setting the
82 * environment variable [[NSS_DEFAULT_DB_TYPE]] to "sql". 91 * environment variable [[NSS_DEFAULT_DB_TYPE]] to "sql".
83 */ 92 */
84 external static void initialize({String database, 93 external static void initialize({String database,
85 String password, 94 String password,
86 bool useBuiltinRoots: true}); 95 bool useBuiltinRoots: true});
87 } 96 }
88 97
89 98
90 /** 99 /**
100 * RawSecureSocket provides a secure (SSL or TLS) network connection.
101 * Client connections to a server are provided by calling
102 * RawSecureSocket.connect. A secure server, created with
103 * RawSecureServerSocket, also returns RawSecureSocket objects representing
104 * the server end of a secure connection.
105 * The certificate provided by the server is checked
106 * using the certificate database provided in SecureSocket.initialize, and/or
107 * the default built-in root certificates.
108 */
109 abstract class RawSecureSocket implements RawSocket {
110 /**
111 * Constructs a new secure client socket and connect it to the given
112 * host on the given port. The returned Future is completed with the
113 * RawSecureSocket when it is connected and ready for subscription.
114 *
115 * The certificate provided by the server is checked using the certificate
116 * database provided in [SecureSocket.initialize], and/or the default built-in
117 * root certificates. If [sendClientCertificate] is
118 * set to true, the socket will send a client certificate if one is
119 * requested by the server. If [certificateName] is the nickname of
120 * a certificate in the certificate database, that certificate will be sent.
121 * If [certificateName] is null, which is the usual use case, an
122 * appropriate certificate will be searched for in the database and
123 * sent automatically, based on what the server says it will accept.
124 *
125 * [onBadCertificate] is an optional handler for unverifiable certificates.
126 * The handler receives the [X509Certificate], and can inspect it and
127 * decide (or let the user decide) whether to accept
128 * the connection or not. The handler should return true
129 * to continue the [RawSecureSocket] connection.
130 */
131 static Future<RawSecureSocket> connect(
132 String host,
133 int port,
134 {bool sendClientCertificate: false,
135 String certificateName,
136 bool onBadCertificate(X509Certificate certificate)}) {
137 return _RawSecureSocket.connect(
138 host,
139 port,
140 certificateName,
141 is_server: false,
142 sendClientCertificate: sendClientCertificate,
143 onBadCertificate: onBadCertificate);
144 }
145
146 /**
147 * Get the peer certificate for a connected RawSecureSocket. If this
148 * RawSecureSocket is the server end of a secure socket connection,
149 * [peerCertificate] will return the client certificate, or null, if no
150 * client certificate was received. If it is the client end,
151 * [peerCertificate] will return the server's certificate.
152 */
153 X509Certificate get peerCertificate;
154 }
155
156
157 /**
91 * X509Certificate represents an SSL certificate, with accessors to 158 * X509Certificate represents an SSL certificate, with accessors to
92 * get the fields of the certificate. 159 * get the fields of the certificate.
93 */ 160 */
94 class X509Certificate { 161 class X509Certificate {
95 X509Certificate(this.subject, 162 X509Certificate(this.subject,
96 this.issuer, 163 this.issuer,
97 this.startValidity, 164 this.startValidity,
98 this.endValidity); 165 this.endValidity);
99 final String subject; 166 final String subject;
100 final String issuer; 167 final String issuer;
101 final DateTime startValidity; 168 final DateTime startValidity;
102 final DateTime endValidity; 169 final DateTime endValidity;
103 } 170 }
104 171
105 172
106 class _SecureSocket implements SecureSocket { 173 class _RawSecureSocket extends Stream<RawSocketEvent>
174 implements RawSecureSocket {
107 // Status states 175 // Status states
108 static final int NOT_CONNECTED = 200; 176 static final int NOT_CONNECTED = 200;
109 static final int HANDSHAKE = 201; 177 static final int HANDSHAKE = 201;
110 static final int CONNECTED = 202; 178 static final int CONNECTED = 202;
111 static final int CLOSED = 203; 179 static final int CLOSED = 203;
112 180
113 // Buffer identifiers. 181 // Buffer identifiers.
114 // These must agree with those in the native C++ implementation. 182 // These must agree with those in the native C++ implementation.
115 static final int READ_PLAINTEXT = 0; 183 static final int READ_PLAINTEXT = 0;
116 static final int WRITE_PLAINTEXT = 1; 184 static final int WRITE_PLAINTEXT = 1;
117 static final int READ_ENCRYPTED = 2; 185 static final int READ_ENCRYPTED = 2;
118 static final int WRITE_ENCRYPTED = 3; 186 static final int WRITE_ENCRYPTED = 3;
119 static final int NUM_BUFFERS = 4; 187 static final int NUM_BUFFERS = 4;
120 188
121 _SecureSocket(String this.host, 189 RawSocket _socket;
122 int requestedPort, 190 final Completer<_RawSecureSocket> _handshakeComplete =
123 String this.certificateName, 191 new Completer<_RawSecureSocket>();
124 {bool this.is_server, 192 StreamController<RawSocketEvent> _controller;
125 Socket this.socket, 193 Stream<RawSocketEvent> _stream;
126 bool this.requestClientCertificate: false, 194 StreamSubscription<RawSocketEvent> _socketSubscription;
127 bool this.requireClientCertificate: false, 195 final String host;
128 bool this.sendClientCertificate: false}) 196 final bool is_server;
129 : secureFilter = new _SecureFilter() { 197 final String certificateName;
130 // Throw an ArgumentError if any field is invalid. 198 final bool requestClientCertificate;
199 final bool requireClientCertificate;
200 final bool sendClientCertificate;
201 final Function onBadCertificate;
202
203 var _status = NOT_CONNECTED;
204 bool _writeEventsEnabled = true;
205 bool _readEventsEnabled = true;
206 bool _socketClosedRead = false; // The network socket is closed for reading.
207 bool _socketClosedWrite = false; // The network socket is closed for writing.
208 bool _closedRead = false; // The secure socket has fired an onClosed event.
209 bool _closedWrite = false; // The secure socket has been closed for writing.
210 bool _filterReadEmpty = true; // There is no buffered data to read.
211 bool _filterWriteEmpty = true; // There is no buffered data to be written.
212 bool _connectPending = false;
213 _SecureFilter _secureFilter = new _SecureFilter();
214
215 static Future<_RawSecureSocket> connect(
216 String host,
217 int requestedPort,
218 String certificateName,
219 {bool is_server,
220 RawSocket socket,
221 bool requestClientCertificate: false,
222 bool requireClientCertificate: false,
223 bool sendClientCertificate: false,
224 bool onBadCertificate(X509Certificate certificate)}){
225 return new _RawSecureSocket(host,
226 requestedPort,
227 certificateName,
228 is_server,
229 socket,
230 requestClientCertificate,
231 requireClientCertificate,
232 sendClientCertificate,
233 onBadCertificate)
234 ._handshakeComplete.future;
235 }
236
237 _RawSecureSocket(
238 String this.host,
239 int requestedPort,
240 String this.certificateName,
241 bool this.is_server,
242 RawSocket socket,
243 bool this.requestClientCertificate,
244 bool this.requireClientCertificate,
245 bool this.sendClientCertificate,
246 bool this.onBadCertificate(X509Certificate certificate)) {
247 _controller = new StreamController<RawSocketEvent>(
248 onPauseStateChange: _onPauseStateChange,
249 onSubscriptionStateChange: _onSubscriptionStateChange);
250 _stream = _controller.stream;
251 // Throw an ArgumentError if any field is invalid. After this, all
252 // errors will be reported through the future or the stream.
131 _verifyFields(); 253 _verifyFields();
254 _secureFilter.init();
255 _secureFilter.registerHandshakeCompleteCallback(
256 _secureHandshakeCompleteHandler);
257 if (onBadCertificate != null) {
258 _secureFilter.registerBadCertificateCallback(onBadCertificate);
259 }
260 var futureSocket;
132 if (socket == null) { 261 if (socket == null) {
133 socket = new Socket(host, requestedPort); 262 futureSocket = RawSocket.connect(host, requestedPort);
134 } 263 } else {
135 socket.onConnect = _secureConnectHandler; 264 futureSocket = new Future.immediate(socket);
136 socket.onData = _secureDataHandler; 265 }
137 socket.onClosed = _secureCloseHandler; 266 futureSocket.then((rawSocket) {
138 socket.onError = _secureErrorHandler; 267 rawSocket.writeEventsEnabled = false;
139 secureFilter.init(); 268 _socket = rawSocket;
140 secureFilter.registerHandshakeCompleteCallback( 269 _socketSubscription = _socket.listen(_eventDispatcher,
141 _secureHandshakeCompleteHandler); 270 onError: _errorHandler,
271 onDone: _doneHandler);
272 _connectPending = true;
273 _secureFilter.connect(host,
274 port,
275 is_server,
276 certificateName,
277 requestClientCertificate ||
278 requireClientCertificate,
279 requireClientCertificate,
280 sendClientCertificate);
281 _status = HANDSHAKE;
282 _secureHandshake();
283 })
284 .catchError((error) {
285 _handshakeComplete.completeError(error);
286 close();
287 });
288 }
289
290 StreamSubscription listen(void onData(RawSocketEvent data),
291 {void onError(AsyncError error),
292 void onDone(),
293 bool unsubscribeOnError}) {
294 if (_writeEventsEnabled) {
295 _writeEventsEnabled = false;
296 _controller.add(RawSocketEvent.WRITE);
297 }
298 return _stream.listen(onData,
299 onError: onError,
300 onDone: onDone,
301 unsubscribeOnError: unsubscribeOnError);
142 } 302 }
143 303
144 void _verifyFields() { 304 void _verifyFields() {
145 if (host is! String) throw new ArgumentError(
146 "SecureSocket constructor: host is not a String");
147 assert(is_server is bool); 305 assert(is_server is bool);
148 assert(socket == null || socket is Socket); 306 assert(_socket == null || _socket is RawSocket);
307 if (host is! String) {
308 throw new ArgumentError(
309 "RawSecureSocket constructor: host is not a String");
310 }
149 if (certificateName != null && certificateName is! String) { 311 if (certificateName != null && certificateName is! String) {
150 throw new ArgumentError( 312 throw new ArgumentError("certificateName is not null or a String");
151 "SecureSocket constructor: certificateName is not null or a String");
152 } 313 }
153 if (certificateName == null && is_server) { 314 if (certificateName == null && is_server) {
154 throw new ArgumentError( 315 throw new ArgumentError("certificateName is null on a server");
155 "SecureSocket constructor: certificateName is null on a server");
156 } 316 }
157 if (requestClientCertificate is! bool) { 317 if (requestClientCertificate is! bool) {
158 throw new ArgumentError( 318 throw new ArgumentError("requestClientCertificate is not a bool");
159 "SecureSocket constructor: requestClientCertificate is not a bool");
160 } 319 }
161 if (requireClientCertificate is! bool) { 320 if (requireClientCertificate is! bool) {
162 throw new ArgumentError( 321 throw new ArgumentError("requireClientCertificate is not a bool");
163 "SecureSocket constructor: requireClientCertificate is not a bool");
164 } 322 }
165 if (sendClientCertificate is! bool) { 323 if (sendClientCertificate is! bool) {
166 throw new ArgumentError( 324 throw new ArgumentError("sendClientCertificate is not a bool");
167 "SecureSocket constructor: sendClientCertificate is not a bool"); 325 }
168 } 326 if (onBadCertificate != null && onBadCertificate is! Function) {
169 } 327 throw new ArgumentError("onBadCertificate is not null or a Function");
170 328 }
171 int get port => socket.port; 329 }
172 330
173 String get remoteHost => socket.remoteHost; 331 int get port => _socket.port;
174 332
175 int get remotePort => socket.remotePort; 333 String get remoteHost => _socket.remoteHost;
176 334
177 void set onClosed(void callback()) { 335 int get remotePort => _socket.remotePort;
178 if (_inputStream != null && callback != null) {
179 throw new StreamException(
180 "Cannot set close handler when input stream is used");
181 }
182 _onClosed = callback;
183 }
184
185 void set _onClosed(void callback()) {
186 _socketCloseHandler = callback;
187 }
188
189 void set onConnect(void callback()) {
190 if (_status == CONNECTED || _status == CLOSED) {
191 throw new StreamException(
192 "Cannot set connect handler when already connected");
193 }
194 _onConnect = callback;
195 }
196
197 void set _onConnect(void callback()) {
198 _socketConnectHandler = callback;
199 }
200
201 void set onData(void callback()) {
202 if (_inputStream != null && callback != null) {
203 throw new StreamException(
204 "Cannot set data handler when input stream is used");
205 }
206 _onData = callback;
207 }
208
209 void set _onData(void callback()) {
210 _socketDataHandler = callback;
211 }
212
213 void set onError(void callback(e)) {
214 _socketErrorHandler = callback;
215 }
216
217 void set onWrite(void callback()) {
218 if (_outputStream != null && callback != null) {
219 throw new StreamException(
220 "Cannot set write handler when output stream is used");
221 }
222 _onWrite = callback;
223 }
224
225 void set _onWrite(void callback()) {
226 _socketWriteHandler = callback;
227 // Reset the one-shot onWrite handler.
228 socket.onWrite = _secureWriteHandler;
229 }
230
231 void set onBadCertificate(bool callback(X509Certificate certificate)) {
232 if (callback is! Function && callback != null) {
233 throw new SocketIOException(
234 "Callback provided to onBadCertificate is not a function or null");
235 }
236 secureFilter.registerBadCertificateCallback(callback);
237 }
238
239 InputStream get inputStream {
240 if (_inputStream == null) {
241 if (_socketDataHandler != null || _socketCloseHandler != null) {
242 throw new StreamException(
243 "Cannot get input stream when socket handlers are used");
244 }
245 _inputStream = new _SocketInputStream(this);
246 }
247 return _inputStream;
248 }
249
250 OutputStream get outputStream {
251 if (_outputStream == null) {
252 if (_socketWriteHandler != null) {
253 throw new StreamException(
254 "Cannot get output stream when socket write handler is used");
255 }
256 _outputStream = new _SocketOutputStream(this);
257 }
258 return _outputStream;
259 }
260 336
261 int available() { 337 int available() {
262 throw new UnimplementedError("SecureSocket.available not implemented yet"); 338 if (_status != CONNECTED) return 0;
263 } 339 _readEncryptedData();
264 340 return _secureFilter.buffers[READ_PLAINTEXT].length;
265 void close([bool halfClose = false]) { 341 }
266 if (_status == CLOSED) return; 342
267 if (halfClose) { 343 void close() {
344 _closedWrite = true;
345 _closedRead = true;
346 if (_socket != null) {
347 _socket.close();
348 }
349 _socketClosedWrite = true;
350 _socketClosedRead = true;
351 if (_secureFilter != null) {
352 _secureFilter.destroy();
353 _secureFilter = null;
354 }
355 if (_socketSubscription != null) {
356 _socketSubscription.cancel();
357 }
358 _controller.close();
359 _status = CLOSED;
360 }
361
362 void shutdown(SocketDirection direction) {
363 if (direction == SocketDirection.BOTH) {
364 close();
365 } else if (direction == SocketDirection.SEND) {
268 _closedWrite = true; 366 _closedWrite = true;
269 _writeEncryptedData(); 367 _writeEncryptedData();
270 if (_filterWriteEmpty) { 368 if (_filterWriteEmpty) {
271 socket.close(true); 369 _socket.shutdown(SocketDirection.SEND);
272 _socketClosedWrite = true; 370 _socketClosedWrite = true;
273 if (_closedRead) { 371 if (_closedRead) {
274 close(false); 372 close();
275 } 373 }
276 } 374 }
375 } else if (direction == SocketDirection.RECEIVE) {
376 _closedRead = true;
377 _socketClosedRead = true;
378 _socket.shutdown(SocketDirection.RECEIVE);
379 if (_socketClosedWrite) {
380 close();
381 }
382 }
383 }
384
385 bool get writeEventsEnabled => _writeEventsEnabled;
386
387 void set writeEventsEnabled(bool value) {
388 if (value &&
389 _secureFilter != null &&
390 _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) {
391 new Timer(0, (_) => _controller.add(RawSocketEvent.WRITE));
277 } else { 392 } else {
278 _closedWrite = true; 393 _writeEventsEnabled = value;
279 _closedRead = true; 394 }
280 socket.close(false); 395 }
281 _socketClosedWrite = true; 396
282 _socketClosedRead = true; 397 bool get readEventsEnabled => _readEventsEnabled;
283 secureFilter.destroy(); 398
284 secureFilter = null; 399 void set readEventsEnabled(bool value) {
285 if (scheduledDataEvent != null) { 400 _readEventsEnabled = value;
286 scheduledDataEvent.cancel(); 401 if (_socketClosedRead) {
402 if (value) {
403 // We have no underlying socket to set off read events.
404 new Timer(0, (_) => _readHandler());
287 } 405 }
288 _status = CLOSED; 406 }
289 } 407 }
290 }
291
292 void _closeWrite() => close(true);
293 408
294 List<int> read([int len]) { 409 List<int> read([int len]) {
295 if (_closedRead) { 410 if (_closedRead) {
296 throw new SocketIOException("Reading from a closed socket"); 411 throw new SocketIOException("Reading from a closed socket");
297 } 412 }
298 if (_status != CONNECTED) { 413 if (_status != CONNECTED) {
299 return new List<int>(0); 414 return new List<int>(0);
300 } 415 }
301 var buffer = secureFilter.buffers[READ_PLAINTEXT]; 416 var buffer = _secureFilter.buffers[READ_PLAINTEXT];
302 _readEncryptedData(); 417 _readEncryptedData();
303 int toRead = buffer.length; 418 int toRead = buffer.length;
304 if (len != null) { 419 if (len != null) {
305 if (len is! int || len < 0) { 420 if (len is! int || len < 0) {
306 throw new ArgumentError( 421 throw new ArgumentError(
307 "Invalid len parameter in SecureSocket.read (len: $len)"); 422 "Invalid len parameter in SecureSocket.read (len: $len)");
308 } 423 }
309 if (len < toRead) { 424 if (len < toRead) {
310 toRead = len; 425 toRead = len;
311 } 426 }
312 } 427 }
313 List<int> result = (toRead == 0) ? null : 428 List<int> result = (toRead == 0) ? null :
314 buffer.data.getRange(buffer.start, toRead); 429 buffer.data.getRange(buffer.start, toRead);
315 buffer.advanceStart(toRead); 430 buffer.advanceStart(toRead);
316 _setHandlersAfterRead();
317 return result;
318 }
319 431
320 int readList(List<int> data, int offset, int bytes) { 432 // Set up a read event if the filter still has data.
321 if (_closedRead) { 433 if (!_filterReadEmpty) {
322 throw new SocketIOException("Reading from a closed socket"); 434 new Timer(0, (_) => _readHandler());
323 }
324 if (offset < 0 || bytes < 0 || offset + bytes > data.length) {
325 throw new ArgumentError(
326 "Invalid offset or bytes in SecureSocket.readList");
327 }
328 if (_status != CONNECTED && _status != CLOSED) {
329 return 0;
330 } 435 }
331 436
332 int bytesRead = 0; 437 if (_socketClosedRead) { // An onClose event is pending.
333 var buffer = secureFilter.buffers[READ_PLAINTEXT]; 438 // _closedRead is false, since we are in a read call.
334 // TODO(whesse): Currently this fails if the if is turned into a while loop. 439 if (!_filterReadEmpty) {
335 // Fix it so that it can loop and read more than one buffer's worth of data. 440 // _filterReadEmpty may be out of date since read empties
336 if (bytes > bytesRead) { 441 // the plaintext buffer after calling _readEncryptedData.
337 _readEncryptedData(); 442 // TODO(whesse): Fix this as part of fixing read.
338 if (buffer.length > 0) { 443 _readEncryptedData();
339 int toRead = min(bytes - bytesRead, buffer.length); 444 }
340 data.setRange(offset, toRead, buffer.data, buffer.start); 445 if (_filterReadEmpty) {
341 buffer.advanceStart(toRead); 446 // This can't be an else clause: the value of _filterReadEmpty changes.
342 bytesRead += toRead; 447 // This must be asynchronous, because we are in a read call.
343 offset += toRead; 448 new Timer(0, (_) => _closeHandler());
344 } 449 }
345 } 450 }
346 451
347 _setHandlersAfterRead(); 452 return result;
348 return bytesRead;
349 } 453 }
350 454
351 // Write the data to the socket, and flush it as much as possible 455 // Write the data to the socket, and flush it as much as possible
352 // until it would block. If the write would block, _writeEncryptedData sets 456 // until it would block. If the write would block, _writeEncryptedData sets
353 // up handlers to flush the pipeline when possible. 457 // up handlers to flush the pipeline when possible.
354 int writeList(List<int> data, int offset, int bytes) { 458 int write(List<int> data, [int offset, int bytes]) {
355 if (_closedWrite) { 459 if (_closedWrite) {
356 throw new SocketIOException("Writing to a closed socket"); 460 _controller.signalError(new AsyncError(new SocketIOException(
461 "Writing to a closed socket")));
462 return 0;
357 } 463 }
358 if (_status != CONNECTED) return 0; 464 if (_status != CONNECTED) return 0;
359 var buffer = secureFilter.buffers[WRITE_PLAINTEXT]; 465 var buffer = _secureFilter.buffers[WRITE_PLAINTEXT];
360 if (bytes > buffer.free) { 466 if (bytes > buffer.free) {
361 bytes = buffer.free; 467 bytes = buffer.free;
362 } 468 }
363 if (bytes > 0) { 469 if (bytes > 0) {
364 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); 470 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset);
365 buffer.length += bytes; 471 buffer.length += bytes;
366 } 472 }
367 _writeEncryptedData(); // Tries to flush all pipeline stages. 473 _writeEncryptedData(); // Tries to flush all pipeline stages.
368 return bytes; 474 return bytes;
369 } 475 }
370 476
371 X509Certificate get peerCertificate => secureFilter.peerCertificate; 477 X509Certificate get peerCertificate => _secureFilter.peerCertificate;
372 478
373 void _secureConnectHandler() { 479 void _writeHandler() {
374 _connectPending = true; 480 if (_status == CLOSED) return;
375 secureFilter.connect(host,
376 port,
377 is_server,
378 certificateName,
379 requestClientCertificate || requireClientCertificate,
380 requireClientCertificate,
381 sendClientCertificate);
382 _status = HANDSHAKE;
383 _secureHandshake();
384 }
385
386 void _secureWriteHandler() {
387 _writeEncryptedData(); 481 _writeEncryptedData();
388 if (_filterWriteEmpty && _closedWrite && !_socketClosedWrite) { 482 if (_filterWriteEmpty && _closedWrite && !_socketClosedWrite) {
389 close(true); 483 // Close _socket for write, by calling shutdown(), to avoid cloning the
484 // socket closing code in shutdown().
485 shutdown(SocketDirection.SEND);
390 } 486 }
391 if (_status == HANDSHAKE) { 487 if (_status == HANDSHAKE) {
392 _secureHandshake(); 488 try {
489 _secureHandshake();
490 } catch (e) { _reportError(e, "RawSecureSocket error"); }
393 } else if (_status == CONNECTED && 491 } else if (_status == CONNECTED &&
394 _socketWriteHandler != null && 492 _writeEventsEnabled &&
395 secureFilter.buffers[WRITE_PLAINTEXT].free > 0) { 493 _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) {
396 // We must be able to set onWrite from the onWrite callback.
397 var handler = _socketWriteHandler;
398 // Reset the one-shot handler. 494 // Reset the one-shot handler.
399 _socketWriteHandler = null; 495 _writeEventsEnabled = false;
400 handler(); 496 _controller.add(RawSocketEvent.WRITE);
401 } 497 }
402 } 498 }
403 499
404 void _secureDataHandler() { 500 void _eventDispatcher(RawSocketEvent event) {
405 if (_status == HANDSHAKE) { 501 if (event == RawSocketEvent.READ) {
502 _readHandler();
503 } else if (event == RawSocketEvent.WRITE) {
504 _writeHandler();
505 } else if (event == RawSocketEvent.READ_CLOSED) {
506 _closeHandler();
507 }
508 }
509
510 void _readHandler() {
511 if (_status == CLOSED) {
512 return;
513 } else if (_status == HANDSHAKE) {
406 try { 514 try {
407 _secureHandshake(); 515 _secureHandshake();
408 } catch (e) { _reportError(e, "SecureSocket error"); } 516 if (_status != HANDSHAKE) _readHandler();
517 } catch (e) { _reportError(e, "RawSecureSocket error"); }
409 } else { 518 } else {
519 if (_status != CONNECTED) {
520 // Cannot happen.
521 throw new SocketIOException("Internal SocketIO Error");
522 }
410 try { 523 try {
411 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure.
412 _readEncryptedData(); 524 _readEncryptedData();
413 } catch (e) { _reportError(e, "SecureSocket error"); } 525 } catch (e) { _reportError(e, "RawSecureSocket error"); }
414 if (!_filterReadEmpty) { 526 if (!_filterReadEmpty) {
415 // Call the onData event. 527 if (_readEventsEnabled) {
416 if (scheduledDataEvent != null) { 528 _controller.add(RawSocketEvent.READ);
417 scheduledDataEvent.cancel(); 529 if (_socketClosedRead) {
418 scheduledDataEvent = null; 530 // Keep firing read events until we are paused or buffer is empty.
419 } 531 new Timer(0, (_) => _readHandler());
420 if (_socketDataHandler != null) { 532 }
421 _socketDataHandler();
422 } 533 }
423 } else if (_socketClosedRead) { 534 } else if (_socketClosedRead) {
424 _secureCloseHandler(); 535 _closeHandler();
425 } 536 }
426 } 537 }
427 } 538 }
428 539
429 void _secureErrorHandler(e) { 540 void _doneHandler() {
430 _reportError(e, 'Error on underlying Socket'); 541 if (_filterReadEmpty) {
542 close();
543 }
544 }
545
546 void _errorHandler(e) {
547 _reportError(e, 'Error on underlying RawSocket');
431 } 548 }
432 549
433 void _reportError(error, String message) { 550 void _reportError(error, String message) {
434 // TODO(whesse): Call _reportError from all internal functions that throw. 551 // TODO(whesse): Call _reportError from all internal functions that throw.
435 var e; 552 var e;
436 if (error is SocketIOException) { 553 if (error is AsyncError) {
554 e = error;
555 } else if (error is SocketIOException) {
437 e = new SocketIOException('$message (${error.message})', error.osError); 556 e = new SocketIOException('$message (${error.message})', error.osError);
438 } else if (error is OSError) { 557 } else if (error is OSError) {
439 e = new SocketIOException(message, error); 558 e = new SocketIOException(message, error);
440 } else { 559 } else {
441 e = new SocketIOException('$message (${error.toString()})', null); 560 e = new SocketIOException('$message (${error.toString()})', null);
442 } 561 }
443 close(false); 562 if (_connectPending) {
444 bool reported = false; 563 _handshakeComplete.completeError(e);
445 if (_socketErrorHandler != null) { 564 } else {
446 reported = true; 565 _controller.signalError(e);
447 _socketErrorHandler(e);
448 } 566 }
449 if (_inputStream != null) { 567 close();
450 reported = reported || _inputStream._onSocketError(e);
451 }
452 if (_outputStream != null) {
453 reported = reported || _outputStream._onSocketError(e);
454 }
455 if (!reported) throw e;
456 } 568 }
457 569
458 void _secureCloseHandler() { 570 void _closeHandler() {
459 if (_closedRead) return; 571 if (_status == CONNECTED) {
460 _socketClosedRead = true; 572 if (_closedRead) return;
461 if (_filterReadEmpty) { 573 _socketClosedRead = true;
462 _closedRead = true; 574 if (_filterReadEmpty) {
463 if (scheduledDataEvent != null) { 575 _closedRead = true;
464 scheduledDataEvent.cancel(); 576 _controller.add(RawSocketEvent.READ_CLOSED);
577 if (_socketClosedWrite) {
578 close();
579 }
465 } 580 }
466 if (_socketCloseHandler != null) { 581 } else if (_status == HANDSHAKE) {
467 _socketCloseHandler(); 582 _reportError(
468 } 583 new SocketIOException('Connection terminated during handshake'),
469 if (_socketClosedWrite) { 584 'handshake error');
470 close(false);
471 }
472 } 585 }
473 } 586 }
474 587
475 void _secureHandshake() { 588 void _secureHandshake() {
476 _readEncryptedData(); 589 _readEncryptedData();
477 secureFilter.handshake(); 590 _secureFilter.handshake();
478 _writeEncryptedData(); 591 _writeEncryptedData();
479 if (secureFilter.buffers[WRITE_ENCRYPTED].length > 0) {
480 socket.onWrite = _secureWriteHandler;
481 }
482 } 592 }
483 593
484 void _secureHandshakeCompleteHandler() { 594 void _secureHandshakeCompleteHandler() {
485 _status = CONNECTED; 595 _status = CONNECTED;
486 if (_connectPending && _socketConnectHandler != null) { 596 if (_connectPending) {
487 _connectPending = false; 597 _connectPending = false;
488 _socketConnectHandler(); 598 // If we complete the future synchronously, user code will run here,
489 } 599 // and modify the state of the RawSecureSocket. For example, it
490 if (_socketWriteHandler != null) { 600 // could close the socket, and set _filter to null.
491 socket.onWrite = _secureWriteHandler; 601 new Timer(0, (_) => _handshakeComplete.complete(this));
492 } 602 }
493 } 603 }
494 604
495 // True if the underlying socket is closed, the filter has been emptied of 605 void _onPauseStateChange() {
496 // all data, and the close event has been fired. 606 if (!_socketClosedRead || !_socketClosedWrite) {
497 get _closed => _socketClosed; 607 if (_controller.isPaused) {
608 _socketSubscription.pause();
609 } else {
610 _socketSubscription.resume();
611 }
612 }
613 }
614
615 void _onSubscriptionStateChange() {
616 if (_controller.hasSubscribers) {
617 // TODO(ajohnsen): Do something here?
618 }
619 }
498 620
499 void _readEncryptedData() { 621 void _readEncryptedData() {
500 // Read from the socket, and push it through the filter as far as 622 // Read from the socket, and push it through the filter as far as
501 // possible. 623 // possible.
502 var encrypted = secureFilter.buffers[READ_ENCRYPTED]; 624 var encrypted = _secureFilter.buffers[READ_ENCRYPTED];
503 var plaintext = secureFilter.buffers[READ_PLAINTEXT]; 625 var plaintext = _secureFilter.buffers[READ_PLAINTEXT];
504 bool progress = true; 626 bool progress = true;
505 while (progress) { 627 while (progress) {
506 progress = false; 628 progress = false;
507 // Do not try to read plaintext from the filter while handshaking. 629 // Do not try to read plaintext from the filter while handshaking.
508 if ((_status == CONNECTED) && plaintext.free > 0) { 630 if ((_status == CONNECTED) && plaintext.free > 0) {
509 int bytes = secureFilter.processBuffer(READ_PLAINTEXT); 631 int bytes = _secureFilter.processBuffer(READ_PLAINTEXT);
510 if (bytes > 0) { 632 if (bytes > 0) {
511 plaintext.length += bytes; 633 plaintext.length += bytes;
512 progress = true; 634 progress = true;
513 } 635 }
514 } 636 }
515 if (encrypted.length > 0) { 637 if (encrypted.length > 0) {
516 int bytes = secureFilter.processBuffer(READ_ENCRYPTED); 638 int bytes = _secureFilter.processBuffer(READ_ENCRYPTED);
517 if (bytes > 0) { 639 if (bytes > 0) {
518 encrypted.advanceStart(bytes); 640 encrypted.advanceStart(bytes);
519 progress = true; 641 progress = true;
520 } 642 }
521 } 643 }
522 if (!_socketClosedRead) { 644 if (!_socketClosedRead && encrypted.free > 0) {
523 int bytes = socket.readList(encrypted.data, 645 List<int> data = _socket.read(encrypted.free);
524 encrypted.start + encrypted.length, 646 if (data != null) {
525 encrypted.free); 647 int bytes = data.length;
526 if (bytes > 0) { 648 encrypted.data.setRange(encrypted.start + encrypted.length,
649 bytes,
650 data);
527 encrypted.length += bytes; 651 encrypted.length += bytes;
528 progress = true; 652 progress = true;
529 } 653 }
530 } 654 }
531 } 655 }
532 // If there is any data in any stages of the filter, there should 656 // If there is any data in any stages of the filter, there should
533 // be data in the plaintext buffer after this process. 657 // be data in the plaintext buffer after this process.
534 // TODO(whesse): Verify that this is true, and there can be no 658 // TODO(whesse): Verify that this is true, and there can be no
535 // partial encrypted block stuck in the secureFilter. 659 // partial encrypted block stuck in the secureFilter.
536 _filterReadEmpty = (plaintext.length == 0); 660 _filterReadEmpty = (plaintext.length == 0);
537 } 661 }
538 662
539 void _writeEncryptedData() { 663 void _writeEncryptedData() {
540 if (_socketClosedWrite) return; 664 if (_socketClosedWrite) return;
541 var encrypted = secureFilter.buffers[WRITE_ENCRYPTED]; 665 var encrypted = _secureFilter.buffers[WRITE_ENCRYPTED];
542 var plaintext = secureFilter.buffers[WRITE_PLAINTEXT]; 666 var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT];
543 while (true) { 667 while (true) {
544 if (encrypted.length > 0) { 668 if (encrypted.length > 0) {
545 // Write from the filter to the socket. 669 // Write from the filter to the socket.
546 int bytes = socket.writeList(encrypted.data, 670 int bytes = _socket.write(encrypted.data,
547 encrypted.start, 671 encrypted.start,
548 encrypted.length); 672 encrypted.length);
549 if (bytes == 0) { 673 encrypted.advanceStart(bytes);
674 if (encrypted.start < encrypted.length) {
550 // The socket has blocked while we have data to write. 675 // The socket has blocked while we have data to write.
551 // We must be notified when it becomes unblocked. 676 // We must be notified when it becomes unblocked.
552 socket.onWrite = _secureWriteHandler; 677 _socket.writeEventsEnabled = true;
553 _filterWriteEmpty = false; 678 _filterWriteEmpty = false;
554 break; 679 break;
555 } 680 }
556 encrypted.advanceStart(bytes);
557 } else { 681 } else {
558 var plaintext = secureFilter.buffers[WRITE_PLAINTEXT]; 682 var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT];
559 if (plaintext.length > 0) { 683 if (plaintext.length > 0) {
560 int plaintext_bytes = secureFilter.processBuffer(WRITE_PLAINTEXT); 684 int plaintext_bytes = _secureFilter.processBuffer(WRITE_PLAINTEXT);
561 plaintext.advanceStart(plaintext_bytes); 685 plaintext.advanceStart(plaintext_bytes);
562 } 686 }
563 int bytes = secureFilter.processBuffer(WRITE_ENCRYPTED); 687 int bytes = _secureFilter.processBuffer(WRITE_ENCRYPTED);
564 if (bytes <= 0) { 688 if (bytes <= 0) {
565 // We know the WRITE_ENCRYPTED buffer is empty, and the 689 // We know the WRITE_ENCRYPTED buffer is empty, and the
566 // filter wrote zero bytes to it, so the filter must be empty. 690 // filter wrote zero bytes to it, so the filter must be empty.
567 // Also, the WRITE_PLAINTEXT buffer must have been empty, or 691 // Also, the WRITE_PLAINTEXT buffer must have been empty, or
568 // it would have written to the filter. 692 // it would have written to the filter.
569 // TODO(whesse): Verify that the filter works this way. 693 // TODO(whesse): Verify that the filter works this way.
570 _filterWriteEmpty = true; 694 _filterWriteEmpty = true;
571 break; 695 break;
572 } 696 }
573 encrypted.length += bytes; 697 encrypted.length += bytes;
574 } 698 }
575 } 699 }
576 } 700 }
577
578 /* After a read, the onData handler is enabled to fire again.
579 * We may also have a close event waiting for the SecureFilter to empty.
580 */
581 void _setHandlersAfterRead() {
582 // If the filter is empty, then we are guaranteed an event when it
583 // becomes unblocked. Cancel any _secureDataHandler call.
584 // Otherwise, schedule a _secureDataHandler call since there may data
585 // available, and this read call enables the data event.
586 if (_filterReadEmpty) {
587 if (scheduledDataEvent != null) {
588 scheduledDataEvent.cancel();
589 scheduledDataEvent = null;
590 }
591 } else if (scheduledDataEvent == null) {
592 scheduledDataEvent = Timer.run(_secureDataHandler);
593 }
594
595 if (_socketClosedRead) { // An onClose event is pending.
596 // _closedRead is false, since we are in a read or readList call.
597 if (!_filterReadEmpty) {
598 // _filterReadEmpty may be out of date since read and readList empty
599 // the plaintext buffer after calling _readEncryptedData.
600 // TODO(whesse): Fix this as part of fixing read and readList.
601 _readEncryptedData();
602 }
603 if (_filterReadEmpty) {
604 // This can't be an else clause: the value of _filterReadEmpty changes.
605 // This must be asynchronous, because we are in a read or readList call.
606 Timer.run(_secureCloseHandler);
607 }
608 }
609 }
610
611 bool get _socketClosed => _closedRead;
612
613 // _SecureSocket cannot extend _Socket and use _Socket's factory constructor.
614 Socket socket;
615 final String host;
616 final bool is_server;
617 final String certificateName;
618 final bool requestClientCertificate;
619 final bool requireClientCertificate;
620 final bool sendClientCertificate;
621
622 var _status = NOT_CONNECTED;
623 bool _socketClosedRead = false; // The network socket is closed for reading.
624 bool _socketClosedWrite = false; // The network socket is closed for writing.
625 bool _closedRead = false; // The secure socket has fired an onClosed event.
626 bool _closedWrite = false; // The secure socket has been closed for writing.
627 bool _filterReadEmpty = true; // There is no buffered data to read.
628 bool _filterWriteEmpty = true; // There is no buffered data to be written.
629 _SocketInputStream _inputStream;
630 _SocketOutputStream _outputStream;
631 bool _connectPending = false;
632 Function _socketConnectHandler;
633 Function _socketWriteHandler;
634 Function _socketDataHandler;
635 Function _socketErrorHandler;
636 Function _socketCloseHandler;
637 Timer scheduledDataEvent;
638
639 _SecureFilter secureFilter;
640 } 701 }
641 702
642 703
643 class _ExternalBuffer { 704 class _ExternalBuffer {
644 // Performance is improved if a full buffer of plaintext fits 705 // Performance is improved if a full buffer of plaintext fits
645 // in the encrypted buffer, when encrypted. 706 // in the encrypted buffer, when encrypted.
646 static final int SIZE = 8 * 1024; 707 static final int SIZE = 8 * 1024;
647 static final int ENCRYPTED_SIZE = 10 * 1024; 708 static final int ENCRYPTED_SIZE = 10 * 1024;
648 _ExternalBuffer() : start = 0, length = 0; 709 _ExternalBuffer() : start = 0, length = 0;
649 710
(...skipping 27 matching lines...) Expand all
677 void destroy(); 738 void destroy();
678 void handshake(); 739 void handshake();
679 void init(); 740 void init();
680 X509Certificate get peerCertificate; 741 X509Certificate get peerCertificate;
681 int processBuffer(int bufferIndex); 742 int processBuffer(int bufferIndex);
682 void registerBadCertificateCallback(Function callback); 743 void registerBadCertificateCallback(Function callback);
683 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); 744 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler);
684 745
685 List<_ExternalBuffer> get buffers; 746 List<_ExternalBuffer> get buffers;
686 } 747 }
OLDNEW
« no previous file with comments | « sdk/lib/io/secure_server_socket.dart ('k') | sdk/lib/io/socket.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698