Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/extensions/api/socket/ssl_socket.h" | |
| 6 | |
| 7 #include "chrome/browser/extensions/api/api_resource.h" | |
| 8 #include "chrome/browser/extensions/api/api_resource_event_notifier.h" | |
| 9 #include "net/base/address_list.h" | |
| 10 #include "net/base/cert_verifier.h" | |
| 11 #include "net/base/host_port_pair.h" | |
| 12 #include "net/base/ip_endpoint.h" | |
| 13 #include "net/base/net_errors.h" | |
| 14 #include "net/base/rand_callback.h" | |
| 15 #include "net/base/ssl_config_service.h" | |
| 16 #include "net/socket/client_socket_factory.h" | |
| 17 #include "net/socket/client_socket_handle.h" | |
| 18 #include "net/socket/tcp_client_socket.h" | |
| 19 | |
| 20 // SSLSocket starts out as a TCP socket in SSLSTATE_NONE. When a handshake | |
| 21 // request is made, it transitions to SSLSTATE_WAIT. If there are no pending | |
| 22 // read or write requests on the TCP socket, an SSL client socket is then | |
| 23 // created and asked to perform the handshake. Otherwise, SSLSocket | |
| 24 // continues to attempt to start the handshake after every pending read | |
| 25 // and write request completes. While in SSLSTATE_WAIT, no new read or | |
| 26 // write requests may be issued (and are replied to with an immediate | |
| 27 // net::ERR_IO_PENDING). | |
| 28 // | |
| 29 // If the SSL handshake succeeds, SSLSocket enters SSLSTATE_CONNECTED | |
| 30 // and all read and write requests go over the secure channel. If | |
| 31 // the SSL handshake fails, SSLSocket enters SSLSTATE_ERROR and no | |
| 32 // further read or write requests may be made until the socket is | |
| 33 // closed. | |
| 34 | |
| 35 namespace extensions { | |
| 36 | |
| 37 SSLSocket::SSLSocket(extensions::APIResourceEventNotifier* event_notifier) | |
| 38 : Socket(event_notifier), | |
| 39 ssl_state_(SSLSTATE_NONE) { | |
| 40 } | |
| 41 | |
| 42 // For testing. | |
| 43 SSLSocket::SSLSocket(net::TCPClientSocket* tcp_client_socket, | |
| 44 APIResourceEventNotifier* event_notifier) | |
| 45 : Socket(event_notifier), | |
| 46 socket_(tcp_client_socket), | |
| 47 ssl_state_(SSLSTATE_NONE) { | |
| 48 } | |
| 49 | |
| 50 // static | |
| 51 SSLSocket* SSLSocket::CreateSocketForTesting( | |
| 52 net::TCPClientSocket* tcp_client_socket, | |
| 53 APIResourceEventNotifier* event_notifier) { | |
| 54 return new SSLSocket(tcp_client_socket, event_notifier); | |
| 55 } | |
| 56 | |
| 57 SSLSocket::~SSLSocket() { | |
| 58 if (is_connected_) { | |
| 59 Disconnect(); | |
| 60 } | |
| 61 } | |
| 62 | |
| 63 void SSLSocket::Connect(const std::string& address, | |
| 64 int port, | |
| 65 const CompletionCallback& callback) { | |
| 66 DCHECK(!callback.is_null()); | |
| 67 | |
| 68 if (!connect_callback_.is_null()) { | |
| 69 callback.Run(net::ERR_CONNECTION_FAILED); | |
| 70 return; | |
| 71 } | |
| 72 connect_callback_ = callback; | |
| 73 | |
| 74 int result = net::ERR_CONNECTION_FAILED; | |
| 75 do { | |
| 76 if (is_connected_) | |
| 77 break; | |
| 78 | |
| 79 net::AddressList address_list; | |
| 80 if (!StringAndPortToAddressList(address, port, &address_list)) { | |
| 81 result = net::ERR_ADDRESS_INVALID; | |
| 82 break; | |
| 83 } | |
| 84 | |
| 85 socket_.reset(new net::TCPClientSocket(address_list, NULL, | |
| 86 net::NetLog::Source())); | |
| 87 connect_callback_ = callback; | |
| 88 result = socket_->Connect(base::Bind( | |
| 89 &SSLSocket::OnConnectComplete, base::Unretained(this))); | |
| 90 } while (false); | |
| 91 | |
| 92 if (result != net::ERR_IO_PENDING) | |
| 93 OnConnectComplete(result); | |
| 94 } | |
| 95 | |
| 96 void SSLSocket::PerformSSLHandshake(const std::string& ssl_hostname, | |
| 97 int ssl_port, | |
| 98 const CompletionCallback& callback) { | |
| 99 DCHECK(!callback.is_null()); | |
| 100 | |
| 101 if (ssl_state_ == SSLSTATE_WAIT) { | |
| 102 callback.Run(net::ERR_IO_PENDING); | |
| 103 return; | |
| 104 } else if (ssl_state_ == SSLSTATE_ERROR) { | |
| 105 callback.Run(net::ERR_FAILED); | |
| 106 return; | |
| 107 } else if (ssl_state_ == SSLSTATE_CONNECTED) { | |
| 108 callback.Run(net::ERR_FAILED); | |
| 109 return; | |
| 110 } | |
| 111 DCHECK(ssl_state_ == SSLSTATE_NONE); | |
| 112 | |
| 113 handshake_callback_ = callback; | |
| 114 ssl_state_ = SSLSTATE_WAIT; | |
| 115 ssl_port_ = ssl_port; | |
| 116 ssl_hostname_ = ssl_hostname; | |
| 117 | |
| 118 AttemptSSLHandshake(); | |
| 119 } | |
| 120 | |
| 121 void SSLSocket::AttemptSSLHandshake() { | |
| 122 DCHECK(ssl_state_ == SSLSTATE_WAIT); | |
| 123 | |
| 124 // We have to wait until outstanding I/O requests are | |
| 125 // complete before we perform the SSL handshake. | |
| 126 if (!read_callback_.is_null()) { | |
| 127 VLOG(1) << "AttemptSSLHandshake aborting (pending callback)."; | |
| 128 return; | |
| 129 } | |
| 130 | |
| 131 if (!write_queue_empty()) { | |
| 132 VLOG(1) << "AttemptSSLHandshake aborting (pending writes)."; | |
| 133 return; | |
| 134 } | |
| 135 | |
| 136 if (!socket_.get() || !socket_->IsConnected()) { | |
| 137 OnHandshakeComplete(net::ERR_SOCKET_NOT_CONNECTED); | |
| 138 return; | |
| 139 } | |
| 140 | |
| 141 VLOG(2) << "AttemptSSLHandshake running handshake."; | |
| 142 | |
| 143 // see remoting/jingle_glue/ssl_socket_adapter.cc | |
| 144 ssl_socket_.reset(); | |
| 145 cert_verifier_.reset(net::CertVerifier::CreateDefault()); | |
| 146 | |
| 147 // We'll use the default configuration to avoid issues wrt | |
| 148 // thread-safety and SSLConfigService. | |
| 149 net::SSLConfig ssl_config; | |
|
Ryan Sleevi
2012/07/09 20:55:45
This is not a good pattern. You should use the Pro
| |
| 150 net::SSLClientSocketContext context; | |
| 151 context.cert_verifier = cert_verifier_.get(); | |
| 152 | |
| 153 // ClientSocketHandle::set_socket takes ownership of the socket... | |
| 154 net::ClientSocketHandle* socket_handle = new net::ClientSocketHandle(); | |
| 155 socket_handle->set_socket(socket_.release()); | |
| 156 | |
| 157 // ... and the SSL socket takes ownership of the socket handle. | |
| 158 ssl_socket_.reset( | |
| 159 net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( | |
| 160 socket_handle, | |
| 161 net::HostPortPair(ssl_hostname_, ssl_port_), | |
| 162 ssl_config, | |
| 163 context)); | |
| 164 | |
| 165 int result = ssl_socket_->Connect(base::Bind( | |
| 166 &SSLSocket::OnHandshakeComplete, base::Unretained(this))); | |
| 167 | |
| 168 if (result != net::ERR_IO_PENDING) { | |
| 169 OnHandshakeComplete(result); | |
| 170 } | |
| 171 } | |
| 172 | |
| 173 void SSLSocket::Disconnect() { | |
| 174 if (ssl_socket_.get()) { | |
| 175 ssl_socket_->Disconnect(); | |
| 176 } else if (socket_.get()) { | |
| 177 socket_->Disconnect(); | |
| 178 } | |
| 179 | |
| 180 ssl_state_ = SSLSTATE_NONE; | |
| 181 is_connected_ = false; | |
| 182 } | |
| 183 | |
| 184 int SSLSocket::Bind(const std::string& address, int port) { | |
| 185 // TODO(penghuang): Supports bind for tcp? | |
| 186 return net::ERR_FAILED; | |
| 187 } | |
| 188 | |
| 189 void SSLSocket::Read(int count, | |
| 190 const ReadCompletionCallback& callback) { | |
| 191 DCHECK(!callback.is_null()); | |
| 192 | |
| 193 if (!read_callback_.is_null() || ssl_state_ == SSLSTATE_WAIT) { | |
| 194 callback.Run(net::ERR_IO_PENDING, NULL); | |
| 195 return; | |
| 196 } else if (ssl_state_ == SSLSTATE_ERROR) { | |
| 197 callback.Run(net::ERR_FAILED, NULL); | |
| 198 return; | |
| 199 } else { | |
| 200 read_callback_ = callback; | |
| 201 } | |
| 202 | |
| 203 int result = net::ERR_FAILED; | |
| 204 scoped_refptr<net::IOBuffer> io_buffer; | |
| 205 do { | |
| 206 if (count < 0) { | |
| 207 result = net::ERR_INVALID_ARGUMENT; | |
| 208 break; | |
| 209 } | |
| 210 | |
| 211 if (ssl_state_ == SSLSTATE_CONNECTED) { | |
| 212 if (!ssl_socket_.get() || !ssl_socket_->IsConnected()) { | |
| 213 result = net::ERR_SOCKET_NOT_CONNECTED; | |
| 214 break; | |
| 215 } | |
| 216 DVLOG(2) << "Reading from SSL socket."; | |
| 217 io_buffer = new net::IOBuffer(count); | |
| 218 result = ssl_socket_->Read(io_buffer.get(), count, | |
| 219 base::Bind(&SSLSocket::OnReadComplete, base::Unretained(this), | |
| 220 io_buffer)); | |
| 221 } else { | |
| 222 if (!socket_.get() || !socket_->IsConnected()) { | |
| 223 result = net::ERR_SOCKET_NOT_CONNECTED; | |
| 224 break; | |
| 225 } | |
| 226 DVLOG(2) << "Reading from normal socket."; | |
| 227 io_buffer = new net::IOBuffer(count); | |
| 228 result = socket_->Read(io_buffer.get(), count, | |
| 229 base::Bind(&SSLSocket::OnReadComplete, base::Unretained(this), | |
| 230 io_buffer)); | |
| 231 } | |
| 232 } while (false); | |
| 233 | |
| 234 if (result != net::ERR_IO_PENDING) | |
| 235 OnReadComplete(io_buffer, result); | |
| 236 } | |
| 237 | |
| 238 void SSLSocket::RecvFrom(int count, | |
| 239 const RecvFromCompletionCallback& callback) { | |
| 240 callback.Run(net::ERR_FAILED, NULL, NULL, 0); | |
| 241 } | |
| 242 | |
| 243 void SSLSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer, | |
| 244 int byte_count, | |
| 245 const std::string& address, | |
| 246 int port, | |
| 247 const CompletionCallback& callback) { | |
| 248 callback.Run(net::ERR_FAILED); | |
| 249 } | |
| 250 | |
| 251 bool SSLSocket::SetKeepAlive(bool enable, int delay) { | |
| 252 if (!socket_.get()) | |
| 253 return false; | |
| 254 return socket_->SetKeepAlive(enable, delay); | |
| 255 } | |
| 256 | |
| 257 bool SSLSocket::SetNoDelay(bool no_delay) { | |
| 258 if (!socket_.get()) | |
| 259 return false; | |
| 260 return socket_->SetNoDelay(no_delay); | |
| 261 } | |
| 262 | |
| 263 int SSLSocket::WriteImpl(net::IOBuffer* io_buffer, | |
| 264 int io_buffer_size, | |
| 265 const net::CompletionCallback& callback) { | |
| 266 if (ssl_state_ == SSLSTATE_CONNECTED) { | |
| 267 if (!ssl_socket_.get() || !ssl_socket_->IsConnected()) | |
| 268 return net::ERR_SOCKET_NOT_CONNECTED; | |
| 269 DVLOG(2) << "Writing " << io_buffer_size << "B to SSL socket."; | |
| 270 return ssl_socket_->Write(io_buffer, io_buffer_size, callback); | |
| 271 } else if (ssl_state_ == SSLSTATE_NONE) { | |
| 272 if (!socket_.get() || !socket_->IsConnected()) | |
| 273 return net::ERR_SOCKET_NOT_CONNECTED; | |
| 274 DVLOG(2) << "Writing " << io_buffer_size << "B to normal socket."; | |
| 275 return socket_->Write(io_buffer, io_buffer_size, callback); | |
| 276 } else if (ssl_state_ == SSLSTATE_WAIT && socket_.get()) { | |
| 277 // Write dregs out to the insecure socket. We'll complete the handshake | |
| 278 // once the write queue empties. | |
| 279 if (!socket_.get() || !socket_->IsConnected()) | |
| 280 return net::ERR_SOCKET_NOT_CONNECTED; | |
| 281 DVLOG(2) << "Writing " << io_buffer_size | |
| 282 << "B to normal socket (in SSLSTATE_WAIT)."; | |
| 283 return socket_->Write(io_buffer, io_buffer_size, callback); | |
| 284 } else { | |
| 285 VLOG(1) << "Bad state for writing."; | |
| 286 return net::ERR_FAILED; | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 void SSLSocket::OnConnectComplete(int result) { | |
| 291 DCHECK(!connect_callback_.is_null()); | |
| 292 DCHECK(!is_connected_); | |
| 293 is_connected_ = result == net::OK; | |
| 294 connect_callback_.Run(result); | |
| 295 connect_callback_.Reset(); | |
| 296 } | |
| 297 | |
| 298 void SSLSocket::OnHandshakeComplete(int result) { | |
| 299 DCHECK(!handshake_callback_.is_null()); | |
| 300 DCHECK(ssl_state_ == SSLSTATE_WAIT); | |
| 301 ssl_state_ = result == net::OK ? SSLSTATE_CONNECTED : SSLSTATE_ERROR; | |
| 302 handshake_callback_.Run(result); | |
| 303 handshake_callback_.Reset(); | |
| 304 } | |
| 305 | |
| 306 void SSLSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, | |
| 307 int result) { | |
| 308 DCHECK(!read_callback_.is_null()); | |
| 309 read_callback_.Run(result, io_buffer); | |
| 310 read_callback_.Reset(); | |
| 311 DVLOG(2) << "OnReadComplete in state " << ssl_state_; | |
| 312 if (ssl_state_ == SSLSTATE_WAIT) { | |
| 313 AttemptSSLHandshake(); | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 void SSLSocket::OnWriteComplete(int result) { | |
| 318 Socket::OnWriteComplete(result); | |
| 319 DVLOG(2) << "OnWriteComplete in state " << ssl_state_; | |
| 320 if (ssl_state_ == SSLSTATE_WAIT) { | |
| 321 AttemptSSLHandshake(); | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 } // namespace extensions | |
| OLD | NEW |