OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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 /** | 5 /** |
6 * SecureSocket provides a secure (SSL or TLS) client connection to a server. | 6 * SecureSocket provides a secure (SSL or TLS) client connection to a server. |
7 * The certificate provided by the server is checked | 7 * The certificate provided by the server is checked |
8 * using the certificate database provided in setCertificateDatabase. | 8 * using the certificate database provided in setCertificateDatabase. |
9 */ | 9 */ |
10 abstract class SecureSocket implements Socket { | 10 abstract class SecureSocket implements Socket { |
11 /** | 11 /** |
12 * Constructs a new secure client socket and connect it to the given | 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 | 13 * host on the given port. The returned socket is not yet connected |
14 * but ready for registration of callbacks. | 14 * but ready for registration of callbacks. |
15 */ | 15 */ |
16 factory SecureSocket(String host, int port) => new _SecureSocket(host, port); | 16 factory SecureSocket(String host, int port) => new _SecureSocket(host, port); |
17 | 17 |
18 /** | 18 /** |
19 * Initializes the NSS library with the path to a certificate database | 19 * Initializes the NSS library with the path to a certificate database |
20 * containing root certificates for verifying certificate paths on | 20 * containing root certificates for verifying certificate paths on |
21 * client connections, and server certificates to provide on server | 21 * client connections, and server certificates to provide on server |
22 * connections. The password argument should be used when creating | 22 * connections. The password argument should be used when creating |
23 * secure server sockets, to allow the private key of the server | 23 * secure server sockets, to allow the private key of the server |
24 * certificate to be fetched. | 24 * certificate to be fetched. If useBuiltinRoots is true (the default), |
| 25 * then a built-in set of root certificates for trusted certificate |
| 26 * authorities is merged with the certificates in the database. |
| 27 * |
| 28 * Examples: |
| 29 * 1) Use only the builtin root certificates: |
| 30 * SecureSocket.initialize(); or |
| 31 * |
| 32 * 2) Use a specified database and the builtin roots: |
| 33 * SecureSocket.initialize(database: 'path/to/my/database', |
| 34 * password: 'my_password'); |
| 35 * |
| 36 * 3) Use a specified database, without builtin roots: |
| 37 * SecureSocket.initialize(database: 'path/to/my/database', |
| 38 * password: 'my_password'. |
| 39 * useBuiltinRoots: false); |
25 * | 40 * |
26 * The database should be an NSS certificate database directory | 41 * The database should be an NSS certificate database directory |
27 * containing a cert9.db file, not a cert8.db file. This version of | 42 * 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 | 43 * 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 | 44 * front of the absolute path of the database directory, or setting the |
30 * environment variable NSS_DEFAULT_DB_TYPE to "sql". | 45 * environment variable NSS_DEFAULT_DB_TYPE to "sql". |
31 */ | 46 */ |
32 external static void setCertificateDatabase(String certificateDatabase, | 47 external static void initialize({String database, |
33 [String password]); | 48 String password, |
| 49 bool useBuiltinRoots: true}); |
34 } | 50 } |
35 | 51 |
36 | 52 |
37 class _SecureSocket implements SecureSocket { | 53 class _SecureSocket implements SecureSocket { |
38 // Status states | 54 // Status states |
39 static final int NOT_CONNECTED = 200; | 55 static final int NOT_CONNECTED = 200; |
40 static final int HANDSHAKE = 201; | 56 static final int HANDSHAKE = 201; |
41 static final int CONNECTED = 202; | 57 static final int CONNECTED = 202; |
42 static final int CLOSED = 203; | 58 static final int CLOSED = 203; |
43 | 59 |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 scheduledDataEvent.cancel(); | 205 scheduledDataEvent.cancel(); |
190 } | 206 } |
191 _status = CLOSED; | 207 _status = CLOSED; |
192 } | 208 } |
193 } | 209 } |
194 | 210 |
195 void _closeWrite() => close(true); | 211 void _closeWrite() => close(true); |
196 | 212 |
197 List<int> read([int len]) { | 213 List<int> read([int len]) { |
198 if (_closedRead) { | 214 if (_closedRead) { |
199 throw new SocketException("Reading from a closed socket"); | 215 throw new SocketIOException("Reading from a closed socket"); |
200 } | 216 } |
201 if (_status != CONNECTED) { | 217 if (_status != CONNECTED) { |
202 return new List<int>(0); | 218 return new List<int>(0); |
203 } | 219 } |
204 var buffer = _secureFilter.buffers[READ_PLAINTEXT]; | 220 var buffer = _secureFilter.buffers[READ_PLAINTEXT]; |
205 _readEncryptedData(); | 221 _readEncryptedData(); |
206 int toRead = buffer.length; | 222 int toRead = buffer.length; |
207 if (len != null) { | 223 if (len != null) { |
208 if (len is! int || len < 0) { | 224 if (len is! int || len < 0) { |
209 throw new ArgumentError( | 225 throw new ArgumentError( |
210 "Invalid len parameter in SecureSocket.read (len: $len)"); | 226 "Invalid len parameter in SecureSocket.read (len: $len)"); |
211 } | 227 } |
212 if (len < toRead) { | 228 if (len < toRead) { |
213 toRead = len; | 229 toRead = len; |
214 } | 230 } |
215 } | 231 } |
216 List<int> result = buffer.data.getRange(buffer.start, toRead); | 232 List<int> result = buffer.data.getRange(buffer.start, toRead); |
217 buffer.advanceStart(toRead); | 233 buffer.advanceStart(toRead); |
218 _setHandlersAfterRead(); | 234 _setHandlersAfterRead(); |
219 return result; | 235 return result; |
220 } | 236 } |
221 | 237 |
222 int readList(List<int> data, int offset, int bytes) { | 238 int readList(List<int> data, int offset, int bytes) { |
223 if (_closedRead) { | 239 if (_closedRead) { |
224 throw new SocketException("Reading from a closed socket"); | 240 throw new SocketIOException("Reading from a closed socket"); |
225 } | 241 } |
226 if (offset < 0 || bytes < 0 || offset + bytes > data.length) { | 242 if (offset < 0 || bytes < 0 || offset + bytes > data.length) { |
227 throw new ArgumentError( | 243 throw new ArgumentError( |
228 "Invalid offset or bytes in SecureSocket.readList"); | 244 "Invalid offset or bytes in SecureSocket.readList"); |
229 } | 245 } |
230 if (_status != CONNECTED && _status != CLOSED) { | 246 if (_status != CONNECTED && _status != CLOSED) { |
231 return 0; | 247 return 0; |
232 } | 248 } |
233 | 249 |
234 int bytesRead = 0; | 250 int bytesRead = 0; |
(...skipping 13 matching lines...) Expand all Loading... |
248 | 264 |
249 _setHandlersAfterRead(); | 265 _setHandlersAfterRead(); |
250 return bytesRead; | 266 return bytesRead; |
251 } | 267 } |
252 | 268 |
253 // Write the data to the socket, and flush it as much as possible | 269 // Write the data to the socket, and flush it as much as possible |
254 // until it would block. If the write would block, _writeEncryptedData sets | 270 // until it would block. If the write would block, _writeEncryptedData sets |
255 // up handlers to flush the pipeline when possible. | 271 // up handlers to flush the pipeline when possible. |
256 int writeList(List<int> data, int offset, int bytes) { | 272 int writeList(List<int> data, int offset, int bytes) { |
257 if (_closedWrite) { | 273 if (_closedWrite) { |
258 throw new SocketException("Writing to a closed socket"); | 274 throw new SocketIOException("Writing to a closed socket"); |
259 } | 275 } |
260 if (_status != CONNECTED) return 0; | 276 if (_status != CONNECTED) return 0; |
261 var buffer = _secureFilter.buffers[WRITE_PLAINTEXT]; | 277 var buffer = _secureFilter.buffers[WRITE_PLAINTEXT]; |
262 if (bytes > buffer.free) { | 278 if (bytes > buffer.free) { |
263 bytes = buffer.free; | 279 bytes = buffer.free; |
264 } | 280 } |
265 if (bytes > 0) { | 281 if (bytes > 0) { |
266 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); | 282 buffer.data.setRange(buffer.start + buffer.length, bytes, data, offset); |
267 buffer.length += bytes; | 283 buffer.length += bytes; |
268 } | 284 } |
(...skipping 22 matching lines...) Expand all Loading... |
291 // We must be able to set onWrite from the onWrite callback. | 307 // We must be able to set onWrite from the onWrite callback. |
292 var handler = _socketWriteHandler; | 308 var handler = _socketWriteHandler; |
293 // Reset the one-shot handler. | 309 // Reset the one-shot handler. |
294 _socketWriteHandler = null; | 310 _socketWriteHandler = null; |
295 handler(); | 311 handler(); |
296 } | 312 } |
297 } | 313 } |
298 | 314 |
299 void _secureDataHandler() { | 315 void _secureDataHandler() { |
300 if (_status == HANDSHAKE) { | 316 if (_status == HANDSHAKE) { |
301 _secureHandshake(); | 317 try { |
| 318 _secureHandshake(); |
| 319 } catch (e) { _reportError(e, "SecureSocket error"); } |
302 } else { | 320 } else { |
303 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure. | 321 try { |
304 _readEncryptedData(); | 322 _writeEncryptedData(); // TODO(whesse): Removing this causes a failure. |
| 323 _readEncryptedData(); |
| 324 } catch (e) { _reportError(e, "SecureSocket error"); } |
305 if (!_filterReadEmpty) { | 325 if (!_filterReadEmpty) { |
306 // Call the onData event. | 326 // Call the onData event. |
307 if (scheduledDataEvent != null) { | 327 if (scheduledDataEvent != null) { |
308 scheduledDataEvent.cancel(); | 328 scheduledDataEvent.cancel(); |
309 scheduledDataEvent = null; | 329 scheduledDataEvent = null; |
310 } | 330 } |
311 if (_socketDataHandler != null) { | 331 if (_socketDataHandler != null) { |
312 _socketDataHandler(); | 332 _socketDataHandler(); |
313 } | 333 } |
314 } | 334 } |
315 } | 335 } |
316 } | 336 } |
317 | 337 |
318 void _secureErrorHandler(e) { | 338 void _secureErrorHandler(e) { |
319 _reportError(e, 'Error on underlying Socket'); | 339 _reportError(e, 'Error on underlying Socket'); |
320 } | 340 } |
321 | 341 |
322 void _reportError(error, String message) { | 342 void _reportError(error, String message) { |
323 // TODO(whesse): Call _reportError from all internal functions that throw. | 343 // TODO(whesse): Call _reportError from all internal functions that throw. |
324 var e; | 344 var e; |
325 if (error is SocketIOException) { | 345 if (error is SocketIOException) { |
326 e = new SocketIOException('$message (${error.message})', error.osError); | 346 e = new SocketIOException('$message (${error.message})', error.osError); |
327 } else if (error is OSError) { | 347 } else if (error is OSError) { |
328 e = new SocketIOException(message, error); | 348 e = new SocketIOException(message, error); |
329 } else { | 349 } else { |
330 e = new SocketIOException('$message (${error.toString()})', null); | 350 e = new SocketIOException('$message (${error.toString()})', null); |
331 } | 351 } |
| 352 close(false); |
332 bool reported = false; | 353 bool reported = false; |
333 if (_socketErrorHandler != null) { | 354 if (_socketErrorHandler != null) { |
334 reported = true; | 355 reported = true; |
335 _socketErrorHandler(e); | 356 _socketErrorHandler(e); |
336 } | 357 } |
337 if (_inputStream != null) { | 358 if (_inputStream != null) { |
338 reported = reported || _inputStream._onSocketError(e); | 359 reported = reported || _inputStream._onSocketError(e); |
339 } | 360 } |
340 if (_outputStream != null) { | 361 if (_outputStream != null) { |
341 reported = reported || _outputStream._onSocketError(e); | 362 reported = reported || _outputStream._onSocketError(e); |
342 } | 363 } |
343 | |
344 if (!reported) throw e; | 364 if (!reported) throw e; |
345 } | 365 } |
346 | 366 |
347 void _secureCloseHandler() { | 367 void _secureCloseHandler() { |
348 _socketClosedRead = true; | 368 _socketClosedRead = true; |
349 if (_filterReadEmpty) { | 369 if (_filterReadEmpty) { |
350 _closedRead = true; | 370 _closedRead = true; |
351 _fireCloseEvent(); | 371 _fireCloseEvent(); |
352 if (_socketClosedWrite) { | 372 if (_socketClosedWrite) { |
353 _secureFilter.destroy(); | 373 _secureFilter.destroy(); |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
561 bool is_server, | 581 bool is_server, |
562 String certificateName); | 582 String certificateName); |
563 void destroy(); | 583 void destroy(); |
564 void handshake(); | 584 void handshake(); |
565 void init(); | 585 void init(); |
566 int processBuffer(int bufferIndex); | 586 int processBuffer(int bufferIndex); |
567 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); | 587 void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); |
568 | 588 |
569 List<_ExternalBuffer> get buffers; | 589 List<_ExternalBuffer> get buffers; |
570 } | 590 } |
OLD | NEW |