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

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

Issue 11419138: Rename TlsSocket to SecureSocket, and all other Tls... items to Secure.... (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rename C++ class from Filter to SSLFilter. Created 8 years 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/tls_server_socket.dart ('k') | tests/standalone/io/secure_server_stream_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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 /**
6 * TlsSocket provides a secure (SSL or TLS) client connection to a server.
7 * The certificate provided by the server is checked
8 * using the certificate database provided in setCertificateDatabase.
9 */
10 abstract class TlsSocket implements Socket {
11 /**
12 * Constructs a new secure client socket and connect it to the given
13 * host on the given port. The returned socket is not yet connected
14 * but ready for registration of callbacks.
15 */
16 factory TlsSocket(String host, int port) => new _TlsSocket(host, port);
17
18 /**
19 * Initializes the TLS library with the path to a certificate database
20 * containing root certificates for verifying certificate paths on
21 * client connections, and server certificates to provide on server
22 * connections. The password argument should be used when creating
23 * secure server sockets, to allow the private key of the server
24 * certificate to be fetched.
25 *
26 * The database should be an NSS certificate database directory
27 * containing a cert9.db file, not a cert8.db file. This version of
28 * the database can be created using the NSS certutil tool with "sql:" in
29 * front of the absolute path of the database directory, or setting the
30 * environment variable NSS_DEFAULT_DB_TYPE to "sql".
31 */
32 external static void setCertificateDatabase(String certificateDatabase,
33 [String password]);
34 }
35
36
37 class _TlsSocket implements TlsSocket {
38 // Status states
39 static final int NOT_CONNECTED = 200;
40 static final int HANDSHAKE = 201;
41 static final int CONNECTED = 202;
42 static final int CLOSED = 203;
43
44 // Buffer identifiers.
45 // These must agree with those in the native C++ implementation.
46 static final int READ_PLAINTEXT = 0;
47 static final int WRITE_PLAINTEXT = 1;
48 static final int READ_ENCRYPTED = 2;
49 static final int WRITE_ENCRYPTED = 3;
50 static final int NUM_BUFFERS = 4;
51
52 int _count = 0;
53 // Constructs a new secure client socket.
54 factory _TlsSocket(String host, int port) =>
55 new _TlsSocket.internal(host, port, false);
56
57 // Constructs a new secure server socket, with the named server certificate.
58 factory _TlsSocket.server(String host,
59 int port,
60 Socket socket,
61 String certificateName) =>
62 new _TlsSocket.internal(host, port, true, socket, certificateName);
63
64 _TlsSocket.internal(String host,
65 int port,
66 bool is_server,
67 [Socket socket,
68 String certificateName])
69 : _host = host,
70 _port = port,
71 _socket = socket,
72 _certificateName = certificateName,
73 _is_server = is_server,
74 _tlsFilter = new _TlsFilter() {
75 if (_socket == null) {
76 _socket = new Socket(host, port);
77 }
78 _socket.onConnect = _tlsConnectHandler;
79 _socket.onData = _tlsDataHandler;
80 _socket.onClosed = _tlsCloseHandler;
81 _tlsFilter.init();
82 _tlsFilter.registerHandshakeCompleteCallback(_tlsHandshakeCompleteHandler);
83 }
84
85 int get port => _socket.port;
86
87 String get remoteHost => _socket.remoteHost;
88
89 int get remotePort => _socket.remotePort;
90
91 void set onClosed(void callback()) {
92 if (_inputStream != null) {
93 throw new StreamException(
94 "Cannot set close handler when input stream is used");
95 }
96 _onClosed = callback;
97 }
98
99 void set _onClosed(void callback()) {
100 _socketCloseHandler = callback;
101 }
102
103 void set onConnect(void callback()) {
104 if (_outputStream != null) {
105 throw new StreamException(
106 "Cannot set connect handler when output stream is used");
107 }
108 if (_status == CONNECTED || _status == CLOSED) {
109 throw new StreamException(
110 "Cannot set connect handler when already connected");
111 }
112 _onConnect = callback;
113 }
114
115 void set _onConnect(void callback()) {
116 _socketConnectHandler = callback;
117 }
118
119 void set onData(void callback()) {
120 if (_outputStream != null) {
121 throw new StreamException(
122 "Cannot set data handler when input stream is used");
123 }
124 _onData = callback;
125 }
126
127 void set _onData(void callback()) {
128 _socketDataHandler = callback;
129 }
130
131 void set onWrite(void callback()) {
132 if (_outputStream != null) {
133 throw new StreamException(
134 "Cannot set write handler when output stream is used");
135 }
136 _onWrite = callback;
137 }
138
139 void set _onWrite(void callback()) {
140 _socketWriteHandler = callback;
141 // Reset the one-shot onWrite handler.
142 _socket.onWrite = _tlsWriteHandler;
143 }
144
145 InputStream get inputStream {
146 if (_inputStream == null) {
147 if (_socketDataHandler != null || _socketCloseHandler != null) {
148 throw new StreamException(
149 "Cannot get input stream when socket handlers are used");
150 }
151 _inputStream = new _SocketInputStream(this);
152 }
153 return _inputStream;
154 }
155
156 OutputStream get outputStream {
157 if (_outputStream == null) {
158 if (_socketConnectHandler != null || _socketWriteHandler != null) {
159 throw new StreamException(
160 "Cannot get output stream when socket handlers are used");
161 }
162 _outputStream = new _SocketOutputStream(this);
163 }
164 return _outputStream;
165 }
166
167 int available() {
168 throw new UnimplementedError("TlsSocket.available not implemented yet");
169 }
170
171 void close([bool halfClose]) {
172 if (halfClose) {
173 _closedWrite = true;
174 _writeEncryptedData();
175 if (_filterWriteEmpty) {
176 _socket.close(true);
177 _socketClosedWrite = true;
178 }
179 } else {
180 _closedWrite = true;
181 _closedRead = true;
182 _socket.close(false);
183 _socketClosedWrite = true;
184 _socketClosedRead = true;
185 _tlsFilter.destroy();
186 _tlsFilter = null;
187 if (scheduledDataEvent != null) {
188 scheduledDataEvent.cancel();
189 }
190 _status = CLOSED;
191 }
192 }
193
194 void _closeWrite() => close(true);
195
196 List<int> read([int len]) {
197 if (_closedRead) {
198 throw new SocketException("Reading from a closed socket");
199 }
200 if (_status != CONNECTED) {
201 return new List<int>(0);
202 }
203 var buffer = _tlsFilter.buffers[READ_PLAINTEXT];
204 _readEncryptedData();
205 int toRead = buffer.length;
206 if (len != null) {
207 if (len is! int || len < 0) {
208 throw new ArgumentError(
209 "Invalid len parameter in TlsSocket.read (len: $len)");
210 }
211 if (len < toRead) {
212 toRead = len;
213 }
214 }
215 List<int> result = buffer.data.getRange(buffer.start, toRead);
216 buffer.advanceStart(toRead);
217 _setHandlersAfterRead();
218 return result;
219 }
220
221 int readList(List<int> data, int offset, int bytes) {
222 if (_closedRead) {
223 throw new SocketException("Reading from a closed socket");
224 }
225 if (offset < 0 || bytes < 0 || offset + bytes > data.length) {
226 throw new ArgumentError(
227 "Invalid offset or bytes in TlsSocket.readList");
228 }
229 if (_status != CONNECTED && _status != CLOSED) {
230 return 0;
231 }
232
233 int bytesRead = 0;
234 var buffer = _tlsFilter.buffers[READ_PLAINTEXT];
235 // TODO(whesse): Currently this fails if the if is turned into a while loop.
236 // Fix it so that it can loop and read more than one buffer's worth of data.
237 if (bytes > bytesRead) {
238 _readEncryptedData();
239 if (buffer.length > 0) {
240 int toRead = min(bytes - bytesRead, buffer.length);
241 data.setRange(offset, toRead, buffer.data, buffer.start);
242 buffer.advanceStart(toRead);
243 bytesRead += toRead;
244 offset += toRead;
245 }
246 }
247
248 _setHandlersAfterRead();
249 return bytesRead;
250 }
251
252 // Write the data to the socket, and flush it as much as possible
253 // until it would block. If the write would block, _writeEncryptedData sets
254 // up handlers to flush the pipeline when possible.
255 int writeList(List<int> data, int offset, int bytes) {
256 if (_closedWrite) {
257 throw new SocketException("Writing to a closed socket");
258 }
259 if (_status != CONNECTED) return 0;
260 var buffer = _tlsFilter.buffers[WRITE_PLAINTEXT];
261 if (bytes > buffer.free) {
262 bytes = buffer.free;
263 }
264 if (bytes > 0) {
265 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset);
266 buffer.length += bytes;
267 }
268 _writeEncryptedData(); // Tries to flush all pipeline stages.
269 return bytes;
270 }
271
272 void _tlsConnectHandler() {
273 _connectPending = true;
274 _tlsFilter.connect(_host, _port, _is_server, _certificateName);
275 _status = HANDSHAKE;
276 _tlsHandshake();
277 }
278
279 void _tlsWriteHandler() {
280 _writeEncryptedData();
281 if (_filterWriteEmpty && _closedWrite && !_socketClosedWrite) {
282 _socket.close(true);
283 _sockedClosedWrite = true;
284 }
285 if (_status == HANDSHAKE) {
286 _tlsHandshake();
287 } else if (_status == CONNECTED &&
288 _socketWriteHandler != null &&
289 _tlsFilter.buffers[WRITE_PLAINTEXT].free > 0) {
290 // We must be able to set onWrite from the onWrite callback.
291 var handler = _socketWriteHandler;
292 // Reset the one-shot handler.
293 _socketWriteHandler = null;
294 handler();
295 }
296 }
297
298 void _tlsDataHandler() {
299 if (_status == HANDSHAKE) {
300 _tlsHandshake();
301 } else {
302 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure.
303 _readEncryptedData();
304 if (!_filterReadEmpty) {
305 // Call the onData event.
306 if (scheduledDataEvent != null) {
307 scheduledDataEvent.cancel();
308 scheduledDataEvent = null;
309 }
310 if (_socketDataHandler != null) {
311 _socketDataHandler();
312 }
313 }
314 }
315 }
316
317 void _tlsCloseHandler() {
318 _socketClosedRead = true;
319 if (_filterReadEmpty) {
320 _closedRead = true;
321 _fireCloseEvent();
322 if (_socketClosedWrite) {
323 _tlsFilter.destroy();
324 _tlsFilter = null;
325 _status = CLOSED;
326 }
327 }
328 }
329
330 void _tlsHandshake() {
331 _readEncryptedData();
332 _tlsFilter.handshake();
333 _writeEncryptedData();
334 if (_tlsFilter.buffers[WRITE_ENCRYPTED].length > 0) {
335 _socket.onWrite = _tlsWriteHandler;
336 }
337 }
338
339 void _tlsHandshakeCompleteHandler() {
340 _status = CONNECTED;
341 if (_connectPending && _socketConnectHandler != null) {
342 _connectPending = false;
343 _socketConnectHandler();
344 }
345 if (_socketWriteHandler != null) {
346 _socket.onWrite = _tlsWriteHandler;
347 }
348 }
349
350 // True if the underlying socket is closed, the filter has been emptied of
351 // all data, and the close event has been fired.
352 get _closed => _socketClosed && !_fireCloseEventPending;
353
354 void _fireCloseEvent() {
355 if (scheduledDataEvent != null) {
356 scheduledDataEvent.cancel();
357 }
358 if (_socketCloseHandler != null) {
359 _socketCloseHandler();
360 }
361 }
362
363 void _readEncryptedData() {
364 // Read from the socket, and push it through the filter as far as
365 // possible.
366 var encrypted = _tlsFilter.buffers[READ_ENCRYPTED];
367 var plaintext = _tlsFilter.buffers[READ_PLAINTEXT];
368 bool progress = true;
369 while (progress) {
370 progress = false;
371 // Do not try to read plaintext from the filter while handshaking.
372 if ((_status == CONNECTED) && plaintext.free > 0) {
373 int bytes = _tlsFilter.processBuffer(READ_PLAINTEXT);
374 if (bytes > 0) {
375 plaintext.length += bytes;
376 progress = true;
377 }
378 }
379 if (encrypted.length > 0) {
380 int bytes = _tlsFilter.processBuffer(READ_ENCRYPTED);
381 if (bytes > 0) {
382 encrypted.advanceStart(bytes);
383 progress = true;
384 }
385 }
386 if (!_socketClosedRead) {
387 int bytes = _socket.readList(encrypted.data,
388 encrypted.start + encrypted.length,
389 encrypted.free);
390 if (bytes > 0) {
391 encrypted.length += bytes;
392 progress = true;
393 }
394 }
395 }
396 // If there is any data in any stages of the filter, there should
397 // be data in the plaintext buffer after this process.
398 // TODO(whesse): Verify that this is true, and there can be no
399 // partial encrypted block stuck in the tlsFilter.
400 _filterReadEmpty = (plaintext.length == 0);
401 }
402
403 void _writeEncryptedData() {
404 if (_socketClosedWrite) return;
405 var encrypted = _tlsFilter.buffers[WRITE_ENCRYPTED];
406 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT];
407 while (true) {
408 if (encrypted.length > 0) {
409 // Write from the filter to the socket.
410 int bytes = _socket.writeList(encrypted.data,
411 encrypted.start,
412 encrypted.length);
413 if (bytes == 0) {
414 // The socket has blocked while we have data to write.
415 // We must be notified when it becomes unblocked.
416 _socket.onWrite = _tlsWriteHandler;
417 _filterWriteEmpty = false;
418 break;
419 }
420 encrypted.advanceStart(bytes);
421 } else {
422 var plaintext = _tlsFilter.buffers[WRITE_PLAINTEXT];
423 if (plaintext.length > 0) {
424 int plaintext_bytes = _tlsFilter.processBuffer(WRITE_PLAINTEXT);
425 plaintext.advanceStart(plaintext_bytes);
426 }
427 int bytes = _tlsFilter.processBuffer(WRITE_ENCRYPTED);
428 if (bytes <= 0) {
429 // We know the WRITE_ENCRYPTED buffer is empty, and the
430 // filter wrote zero bytes to it, so the filter must be empty.
431 // Also, the WRITE_PLAINTEXT buffer must have been empty, or
432 // it would have written to the filter.
433 // TODO(whesse): Verify that the filter works this way.
434 _filterWriteEmpty = true;
435 break;
436 }
437 encrypted.length += bytes;
438 }
439 }
440 }
441
442 /* After a read, the onData handler is enabled to fire again.
443 * We may also have a close event waiting for the TlsFilter to empty.
444 */
445 void _setHandlersAfterRead() {
446 // If the filter is empty, then we are guaranteed an event when it
447 // becomes unblocked. Cancel any _tlsDataHandler call.
448 // Otherwise, schedule a _tlsDataHandler call since there may data
449 // available, and this read call enables the data event.
450 if (_filterReadEmpty) {
451 if (scheduledDataEvent != null) {
452 scheduledDataEvent.cancel();
453 scheduledDataEvent = null;
454 }
455 } else if (scheduledDataEvent == null) {
456 scheduledDataEvent = new Timer(0, (_) => _tlsDataHandler());
457 }
458
459 if (_socketClosedRead) { // An onClose event is pending.
460 // _closedRead is false, since we are in a read or readList call.
461 if (!_filterReadEmpty) {
462 // _filterReadEmpty may be out of date since read and readList empty
463 // the plaintext buffer after calling _readEncryptedData.
464 // TODO(whesse): Fix this as part of fixing read and readList.
465 _readEncryptedData();
466 }
467 if (_filterReadEmpty) {
468 // This can't be an else clause: the value of _filterReadEmpty changes.
469 // This must be asynchronous, because we are in a read or readList call.
470 new Timer(0, (_) => _fireCloseEvent());
471 }
472 }
473 }
474
475 bool get _socketClosed => _closedRead;
476
477 // _TlsSocket cannot extend _Socket and use _Socket's factory constructor.
478 Socket _socket;
479 String _host;
480 int _port;
481 bool _is_server;
482 String _certificateName;
483
484 var _status = NOT_CONNECTED;
485 bool _socketClosedRead = false; // The network socket is closed for reading.
486 bool _socketClosedWrite = false; // The network socket is closed for writing.
487 bool _closedRead = false; // The secure socket has fired an onClosed event.
488 bool _closedWrite = false; // The secure socket has been closed for writing.
489 bool _filterReadEmpty = true; // There is no buffered data to read.
490 bool _filterWriteEmpty = true; // There is no buffered data to be written.
491 _SocketInputStream _inputStream;
492 _SocketOutputStream _outputStream;
493 bool _connectPending = false;
494 Function _socketConnectHandler;
495 Function _socketWriteHandler;
496 Function _socketDataHandler;
497 Function _socketCloseHandler;
498 Timer scheduledDataEvent;
499
500 _TlsFilter _tlsFilter;
501 }
502
503
504 class _TlsExternalBuffer {
505 static final int SIZE = 8 * 1024;
506 _TlsExternalBuffer() : start = 0, length = 0;
507
508 // TODO(whesse): Consider making this a circular buffer. Only if it helps.
509 void advanceStart(int numBytes) {
510 start += numBytes;
511 length -= numBytes;
512 if (length == 0) {
513 start = 0;
514 }
515 }
516
517 int get free => SIZE - (start + length);
518
519 List data; // This will be a ExternalByteArray, backed by C allocated data.
520 int start;
521 int length;
522 }
523
524
525 abstract class _TlsFilter {
526 external factory _TlsFilter();
527
528 void connect(String hostName,
529 int port,
530 bool is_server,
531 String certificateName);
532 void destroy();
533 void handshake();
534 void init();
535 int processBuffer(int bufferIndex);
536 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler);
537
538 List<_TlsExternalBuffer> get buffers;
539 }
OLDNEW
« no previous file with comments | « sdk/lib/io/tls_server_socket.dart ('k') | tests/standalone/io/secure_server_stream_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698