Chromium Code Reviews| Index: sdk/lib/io/secure_socket.dart |
| diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart |
| index 272123796102cb9a5bff980766e2b169456f1443..0ee4fa45b45b31c9016bbff4c4d69f1b9caa2439 100644 |
| --- a/sdk/lib/io/secure_socket.dart |
| +++ b/sdk/lib/io/secure_socket.dart |
| @@ -11,9 +11,24 @@ abstract class SecureSocket implements Socket { |
| /** |
| * Constructs a new secure client socket and connect it to the given |
| * host on the given port. The returned socket is not yet connected |
| - * but ready for registration of callbacks. |
| + * but ready for registration of callbacks. If sendClientCertificate is |
| + * set to true, the socket will send a client certificate if one is |
| + * requested by the server. If clientCertificate is the nickname of |
| + * a certificate in the certificate database, that certificate will be sent. |
| + * If clientCertificate is null, which is the usual use case, an |
| + * appropriate certificate will be searched for in the database and |
| + * sent automatically, based on what the server says it will accept. |
| */ |
| - factory SecureSocket(String host, int port) => new _SecureSocket(host, port); |
| + factory SecureSocket(String host, |
| + int port, |
| + {bool sendClientCertificate: false, |
| + String certificateName}) { |
| + return new _SecureSocket(host, |
| + port, |
| + certificateName, |
| + is_server: false, |
| + sendClientCertificate: sendClientCertificate); |
| + } |
| /** |
| * Install a handler for unverifiable certificates. The handler can inspect |
| @@ -23,6 +38,14 @@ abstract class SecureSocket implements Socket { |
| */ |
| void set onBadCertificate(bool callback(X509Certificate certificate)); |
| + /** |
| + * Get the peerCertificate for a connected secure socket. For a server |
| + * socket, this will return the client certificate, or null, if no |
| + * client certificate was received. For a client socket, this |
| + * will return the server's certificate. |
| + */ |
| + X509Certificate get peerCertificate; |
| + |
| /** |
| * Initializes the NSS library with the path to a certificate database |
| * containing root certificates for verifying certificate paths on |
| @@ -89,45 +112,55 @@ class _SecureSocket implements SecureSocket { |
| static final int WRITE_ENCRYPTED = 3; |
| static final int NUM_BUFFERS = 4; |
| - int _count = 0; |
| - // Constructs a new secure client socket. |
| - factory _SecureSocket(String host, int port) => |
| - new _SecureSocket.internal(host, port, false); |
| - |
| - // Constructs a new secure server socket, with the named server certificate. |
| - factory _SecureSocket.server(String host, |
| - int port, |
| - Socket socket, |
| - String certificateName) => |
| - new _SecureSocket.internal(host, port, true, socket, certificateName); |
| - |
| - _SecureSocket.internal(String host, |
| - int port, |
| - bool is_server, |
| - [Socket socket, |
| - String certificateName]) |
| - : _host = host, |
| - _port = port, |
| - _socket = socket, |
| - _certificateName = certificateName, |
| - _is_server = is_server, |
| - _secureFilter = new _SecureFilter() { |
| - if (_socket == null) { |
| - _socket = new Socket(host, port); |
| - } |
| - _socket.onConnect = _secureConnectHandler; |
| - _socket.onData = _secureDataHandler; |
| - _socket.onClosed = _secureCloseHandler; |
| - _socket.onError = _secureErrorHandler; |
| - _secureFilter.init(); |
| - _secureFilter.registerHandshakeCompleteCallback(_secureHandshakeCompleteHandler); |
| - } |
| - |
| - int get port => _socket.port; |
| - |
| - String get remoteHost => _socket.remoteHost; |
| - |
| - int get remotePort => _socket.remotePort; |
| + _SecureSocket(String this.host, |
| + int requestedPort, |
| + String this.certificateName, |
| + {bool this.is_server, |
| + Socket this.socket, |
| + bool this.requestClientCertificate: false, |
| + bool this.requireClientCertificate: false, |
| + bool this.sendClientCertificate: false}) |
| + : secureFilter = new _SecureFilter() { |
| + // Throw an ArgumentError if any field is invalid. |
| + _verifyFields(); |
| + if (socket == null) { |
| + socket = new Socket(host, requestedPort); |
| + } |
| + socket.onConnect = _secureConnectHandler; |
| + socket.onData = _secureDataHandler; |
| + socket.onClosed = _secureCloseHandler; |
| + socket.onError = _secureErrorHandler; |
| + secureFilter.init(); |
| + secureFilter.registerHandshakeCompleteCallback( |
| + _secureHandshakeCompleteHandler); |
| + } |
| + |
| + void _verifyFields() { |
| + if (host is! String) throw new ArgumentError( |
| + "SecureSocket constructor: host is not a String"); |
| + assert(is_server is bool); |
| + assert(socket == null || socket is Socket); |
| + if (certificateName != null && certificateName is! String) { |
| + throw new ArgumentError( |
| + "SecureSocket constructor: certificateName is not null or a String"); |
| + } |
| + if (certificateName == null && is_server) { |
| + throw new ArgumentError( |
| + "SecureSocket constructor: certificateName is null on a server"); |
| + } |
| + if (requestClientCertificate is! bool) throw new ArgumentError( |
|
Mads Ager (google)
2012/12/11 07:45:27
Please use curly braces when using multiple lines
Bill Hesse
2012/12/11 09:20:54
Done.
|
| + "SecureSocket constructor: requestClientCertificate is not a bool"); |
| + if (requireClientCertificate is! bool) throw new ArgumentError( |
| + "SecureSocket constructor: requireClientCertificate is not a bool"); |
| + if (sendClientCertificate is! bool) throw new ArgumentError( |
| + "SecureSocket constructor: sendClientCertificate is not a bool"); |
| + } |
| + |
| + int get port => socket.port; |
| + |
| + String get remoteHost => socket.remoteHost; |
| + |
| + int get remotePort => socket.remotePort; |
| void set onClosed(void callback()) { |
| if (_inputStream != null && callback != null) { |
| @@ -180,7 +213,7 @@ class _SecureSocket implements SecureSocket { |
| void set _onWrite(void callback()) { |
| _socketWriteHandler = callback; |
| // Reset the one-shot onWrite handler. |
| - _socket.onWrite = _secureWriteHandler; |
| + socket.onWrite = _secureWriteHandler; |
| } |
| void set onBadCertificate(bool callback(X509Certificate certificate)) { |
| @@ -188,7 +221,7 @@ class _SecureSocket implements SecureSocket { |
| throw new SocketIOException( |
| "Callback provided to onBadCertificate is not a function or null"); |
| } |
| - _secureFilter.registerBadCertificateCallback(callback); |
| + secureFilter.registerBadCertificateCallback(callback); |
| } |
| InputStream get inputStream { |
| @@ -223,7 +256,7 @@ class _SecureSocket implements SecureSocket { |
| _closedWrite = true; |
| _writeEncryptedData(); |
| if (_filterWriteEmpty) { |
| - _socket.close(true); |
| + socket.close(true); |
| _socketClosedWrite = true; |
| if (_closedRead) { |
| close(false); |
| @@ -232,11 +265,11 @@ class _SecureSocket implements SecureSocket { |
| } else { |
| _closedWrite = true; |
| _closedRead = true; |
| - _socket.close(false); |
| + socket.close(false); |
| _socketClosedWrite = true; |
| _socketClosedRead = true; |
| - _secureFilter.destroy(); |
| - _secureFilter = null; |
| + secureFilter.destroy(); |
| + secureFilter = null; |
| if (scheduledDataEvent != null) { |
| scheduledDataEvent.cancel(); |
| } |
| @@ -253,7 +286,7 @@ class _SecureSocket implements SecureSocket { |
| if (_status != CONNECTED) { |
| return new List<int>(0); |
| } |
| - var buffer = _secureFilter.buffers[READ_PLAINTEXT]; |
| + var buffer = secureFilter.buffers[READ_PLAINTEXT]; |
| _readEncryptedData(); |
| int toRead = buffer.length; |
| if (len != null) { |
| @@ -284,7 +317,7 @@ class _SecureSocket implements SecureSocket { |
| } |
| int bytesRead = 0; |
| - var buffer = _secureFilter.buffers[READ_PLAINTEXT]; |
| + var buffer = secureFilter.buffers[READ_PLAINTEXT]; |
| // TODO(whesse): Currently this fails if the if is turned into a while loop. |
| // Fix it so that it can loop and read more than one buffer's worth of data. |
| if (bytes > bytesRead) { |
| @@ -310,7 +343,7 @@ class _SecureSocket implements SecureSocket { |
| throw new SocketIOException("Writing to a closed socket"); |
| } |
| if (_status != CONNECTED) return 0; |
| - var buffer = _secureFilter.buffers[WRITE_PLAINTEXT]; |
| + var buffer = secureFilter.buffers[WRITE_PLAINTEXT]; |
| if (bytes > buffer.free) { |
| bytes = buffer.free; |
| } |
| @@ -322,9 +355,17 @@ class _SecureSocket implements SecureSocket { |
| return bytes; |
| } |
| + X509Certificate get peerCertificate => secureFilter.peerCertificate; |
| + |
| void _secureConnectHandler() { |
| _connectPending = true; |
| - _secureFilter.connect(_host, _port, _is_server, _certificateName); |
| + secureFilter.connect(host, |
| + port, |
| + is_server, |
| + certificateName, |
| + requestClientCertificate || requireClientCertificate, |
| + requireClientCertificate, |
| + sendClientCertificate); |
| _status = HANDSHAKE; |
| _secureHandshake(); |
| } |
| @@ -338,7 +379,7 @@ class _SecureSocket implements SecureSocket { |
| _secureHandshake(); |
| } else if (_status == CONNECTED && |
| _socketWriteHandler != null && |
| - _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) { |
| + secureFilter.buffers[WRITE_PLAINTEXT].free > 0) { |
| // We must be able to set onWrite from the onWrite callback. |
| var handler = _socketWriteHandler; |
| // Reset the one-shot handler. |
| @@ -420,10 +461,10 @@ class _SecureSocket implements SecureSocket { |
| void _secureHandshake() { |
| _readEncryptedData(); |
| - _secureFilter.handshake(); |
| + secureFilter.handshake(); |
| _writeEncryptedData(); |
| - if (_secureFilter.buffers[WRITE_ENCRYPTED].length > 0) { |
| - _socket.onWrite = _secureWriteHandler; |
| + if (secureFilter.buffers[WRITE_ENCRYPTED].length > 0) { |
| + socket.onWrite = _secureWriteHandler; |
| } |
| } |
| @@ -434,7 +475,7 @@ class _SecureSocket implements SecureSocket { |
| _socketConnectHandler(); |
| } |
| if (_socketWriteHandler != null) { |
| - _socket.onWrite = _secureWriteHandler; |
| + socket.onWrite = _secureWriteHandler; |
| } |
| } |
| @@ -445,30 +486,30 @@ class _SecureSocket implements SecureSocket { |
| void _readEncryptedData() { |
| // Read from the socket, and push it through the filter as far as |
| // possible. |
| - var encrypted = _secureFilter.buffers[READ_ENCRYPTED]; |
| - var plaintext = _secureFilter.buffers[READ_PLAINTEXT]; |
| + var encrypted = secureFilter.buffers[READ_ENCRYPTED]; |
| + var plaintext = secureFilter.buffers[READ_PLAINTEXT]; |
| bool progress = true; |
| while (progress) { |
| progress = false; |
| // Do not try to read plaintext from the filter while handshaking. |
| if ((_status == CONNECTED) && plaintext.free > 0) { |
| - int bytes = _secureFilter.processBuffer(READ_PLAINTEXT); |
| + int bytes = secureFilter.processBuffer(READ_PLAINTEXT); |
| if (bytes > 0) { |
| plaintext.length += bytes; |
| progress = true; |
| } |
| } |
| if (encrypted.length > 0) { |
| - int bytes = _secureFilter.processBuffer(READ_ENCRYPTED); |
| + int bytes = secureFilter.processBuffer(READ_ENCRYPTED); |
| if (bytes > 0) { |
| encrypted.advanceStart(bytes); |
| progress = true; |
| } |
| } |
| if (!_socketClosedRead) { |
| - int bytes = _socket.readList(encrypted.data, |
| - encrypted.start + encrypted.length, |
| - encrypted.free); |
| + int bytes = socket.readList(encrypted.data, |
| + encrypted.start + encrypted.length, |
| + encrypted.free); |
| if (bytes > 0) { |
| encrypted.length += bytes; |
| progress = true; |
| @@ -484,29 +525,29 @@ class _SecureSocket implements SecureSocket { |
| void _writeEncryptedData() { |
| if (_socketClosedWrite) return; |
| - var encrypted = _secureFilter.buffers[WRITE_ENCRYPTED]; |
| - var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT]; |
| + var encrypted = secureFilter.buffers[WRITE_ENCRYPTED]; |
| + var plaintext = secureFilter.buffers[WRITE_PLAINTEXT]; |
| while (true) { |
| if (encrypted.length > 0) { |
| // Write from the filter to the socket. |
| - int bytes = _socket.writeList(encrypted.data, |
| - encrypted.start, |
| - encrypted.length); |
| + int bytes = socket.writeList(encrypted.data, |
| + encrypted.start, |
| + encrypted.length); |
| if (bytes == 0) { |
| // The socket has blocked while we have data to write. |
| // We must be notified when it becomes unblocked. |
| - _socket.onWrite = _secureWriteHandler; |
| + socket.onWrite = _secureWriteHandler; |
| _filterWriteEmpty = false; |
| break; |
| } |
| encrypted.advanceStart(bytes); |
| } else { |
| - var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT]; |
| + var plaintext = secureFilter.buffers[WRITE_PLAINTEXT]; |
| if (plaintext.length > 0) { |
| - int plaintext_bytes = _secureFilter.processBuffer(WRITE_PLAINTEXT); |
| + int plaintext_bytes = secureFilter.processBuffer(WRITE_PLAINTEXT); |
| plaintext.advanceStart(plaintext_bytes); |
| } |
| - int bytes = _secureFilter.processBuffer(WRITE_ENCRYPTED); |
| + int bytes = secureFilter.processBuffer(WRITE_ENCRYPTED); |
| if (bytes <= 0) { |
| // We know the WRITE_ENCRYPTED buffer is empty, and the |
| // filter wrote zero bytes to it, so the filter must be empty. |
| @@ -557,11 +598,13 @@ class _SecureSocket implements SecureSocket { |
| bool get _socketClosed => _closedRead; |
| // _SecureSocket cannot extend _Socket and use _Socket's factory constructor. |
| - Socket _socket; |
| - String _host; |
| - int _port; |
| - bool _is_server; |
| - String _certificateName; |
| + Socket socket; |
| + final String host; |
| + final bool is_server; |
| + final String certificateName; |
| + final bool requestClientCertificate; |
| + final bool requireClientCertificate; |
| + final bool sendClientCertificate; |
| var _status = NOT_CONNECTED; |
| bool _socketClosedRead = false; // The network socket is closed for reading. |
| @@ -580,7 +623,7 @@ class _SecureSocket implements SecureSocket { |
| Function _socketCloseHandler; |
| Timer scheduledDataEvent; |
| - _SecureFilter _secureFilter; |
| + _SecureFilter secureFilter; |
| } |
| @@ -611,10 +654,14 @@ abstract class _SecureFilter { |
| void connect(String hostName, |
| int port, |
| bool is_server, |
| - String certificateName); |
| + String certificateName, |
| + bool requestClientCertificate, |
| + bool requireClientCertificate, |
| + bool sendClientCertificate); |
| void destroy(); |
| void handshake(); |
| void init(); |
| + X509Certificate get peerCertificate; |
| int processBuffer(int bufferIndex); |
| void registerBadCertificateCallback(Function callback); |
| void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); |