Update {virtual,override,final} to follow C++11 style.
[chromium-blink-merge.git] / extensions / browser / api / cast_channel / cast_socket.cc
blob1b4baef7a9b743b1674bc63d95d5a86719472773
1 // Copyright 2014 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.
5 #include "extensions/browser/api/cast_channel/cast_socket.h"
7 #include <stdlib.h>
8 #include <string.h>
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/format_macros.h"
13 #include "base/lazy_instance.h"
14 #include "base/numerics/safe_conversions.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/sys_byteorder.h"
18 #include "base/time/time.h"
19 #include "extensions/browser/api/cast_channel/cast_auth_util.h"
20 #include "extensions/browser/api/cast_channel/cast_framer.h"
21 #include "extensions/browser/api/cast_channel/cast_message_util.h"
22 #include "extensions/browser/api/cast_channel/cast_transport.h"
23 #include "extensions/browser/api/cast_channel/logger.h"
24 #include "extensions/browser/api/cast_channel/logger_util.h"
25 #include "extensions/common/api/cast_channel/cast_channel.pb.h"
26 #include "net/base/address_list.h"
27 #include "net/base/host_port_pair.h"
28 #include "net/base/net_errors.h"
29 #include "net/base/net_util.h"
30 #include "net/cert/cert_verifier.h"
31 #include "net/cert/x509_certificate.h"
32 #include "net/http/transport_security_state.h"
33 #include "net/socket/client_socket_factory.h"
34 #include "net/socket/client_socket_handle.h"
35 #include "net/socket/ssl_client_socket.h"
36 #include "net/socket/stream_socket.h"
37 #include "net/socket/tcp_client_socket.h"
38 #include "net/ssl/ssl_config_service.h"
39 #include "net/ssl/ssl_info.h"
41 // Assumes |ip_endpoint_| of type net::IPEndPoint and |channel_auth_| of enum
42 // type ChannelAuthType are available in the current scope.
43 #define VLOG_WITH_CONNECTION(level) VLOG(level) << "[" << \
44 ip_endpoint_.ToString() << ", auth=" << channel_auth_ << "] "
46 namespace {
48 const int kMaxSelfSignedCertLifetimeInDays = 2;
50 std::string FormatTimeForLogging(base::Time time) {
51 base::Time::Exploded exploded_time;
52 time.UTCExplode(&exploded_time);
53 return base::StringPrintf(
54 "%04d-%02d-%02d %02d:%02d:%02d.%03d UTC", exploded_time.year,
55 exploded_time.month, exploded_time.day_of_month, exploded_time.hour,
56 exploded_time.minute, exploded_time.second, exploded_time.millisecond);
59 } // namespace
61 namespace extensions {
62 static base::LazyInstance<BrowserContextKeyedAPIFactory<
63 ApiResourceManager<core_api::cast_channel::CastSocket> > > g_factory =
64 LAZY_INSTANCE_INITIALIZER;
66 // static
67 template <>
68 BrowserContextKeyedAPIFactory<
69 ApiResourceManager<core_api::cast_channel::CastSocket> >*
70 ApiResourceManager<core_api::cast_channel::CastSocket>::GetFactoryInstance() {
71 return g_factory.Pointer();
74 namespace core_api {
75 namespace cast_channel {
77 CastSocket::CastSocket(const std::string& owner_extension_id)
78 : ApiResource(owner_extension_id) {
81 CastSocketImpl::CastSocketImpl(const std::string& owner_extension_id,
82 const net::IPEndPoint& ip_endpoint,
83 ChannelAuthType channel_auth,
84 net::NetLog* net_log,
85 const base::TimeDelta& timeout,
86 bool keep_alive,
87 const scoped_refptr<Logger>& logger,
88 long device_capabilities)
89 : CastSocket(owner_extension_id),
90 auth_delegate_(this),
91 owner_extension_id_(owner_extension_id),
92 channel_id_(0),
93 ip_endpoint_(ip_endpoint),
94 channel_auth_(channel_auth),
95 net_log_(net_log),
96 keep_alive_(keep_alive),
97 logger_(logger),
98 connect_timeout_(timeout),
99 connect_timeout_timer_(new base::OneShotTimer<CastSocketImpl>),
100 is_canceled_(false),
101 device_capabilities_(device_capabilities),
102 connect_state_(proto::CONN_STATE_NONE),
103 error_state_(CHANNEL_ERROR_NONE),
104 ready_state_(READY_STATE_NONE) {
105 DCHECK(net_log_);
106 DCHECK(channel_auth_ == CHANNEL_AUTH_TYPE_SSL ||
107 channel_auth_ == CHANNEL_AUTH_TYPE_SSL_VERIFIED);
108 net_log_source_.type = net::NetLog::SOURCE_SOCKET;
109 net_log_source_.id = net_log_->NextID();
112 CastSocketImpl::~CastSocketImpl() {
113 // Ensure that resources are freed but do not run pending callbacks to avoid
114 // any re-entrancy.
115 CloseInternal();
118 ReadyState CastSocketImpl::ready_state() const {
119 return ready_state_;
122 ChannelError CastSocketImpl::error_state() const {
123 return error_state_;
126 const net::IPEndPoint& CastSocketImpl::ip_endpoint() const {
127 return ip_endpoint_;
130 int CastSocketImpl::id() const {
131 return channel_id_;
134 void CastSocketImpl::set_id(int id) {
135 channel_id_ = id;
138 ChannelAuthType CastSocketImpl::channel_auth() const {
139 return channel_auth_;
142 bool CastSocketImpl::keep_alive() const {
143 return keep_alive_;
146 scoped_ptr<net::TCPClientSocket> CastSocketImpl::CreateTcpSocket() {
147 net::AddressList addresses(ip_endpoint_);
148 return scoped_ptr<net::TCPClientSocket>(
149 new net::TCPClientSocket(addresses, net_log_, net_log_source_));
150 // Options cannot be set on the TCPClientSocket yet, because the
151 // underlying platform socket will not be created until Bind()
152 // or Connect() is called.
155 scoped_ptr<net::SSLClientSocket> CastSocketImpl::CreateSslSocket(
156 scoped_ptr<net::StreamSocket> socket) {
157 net::SSLConfig ssl_config;
158 // If a peer cert was extracted in a previous attempt to connect, then
159 // whitelist that cert.
160 if (!peer_cert_.empty()) {
161 net::SSLConfig::CertAndStatus cert_and_status;
162 cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID;
163 cert_and_status.der_cert = peer_cert_;
164 ssl_config.allowed_bad_certs.push_back(cert_and_status);
165 logger_->LogSocketEvent(channel_id_, proto::SSL_CERT_WHITELISTED);
168 cert_verifier_.reset(net::CertVerifier::CreateDefault());
169 transport_security_state_.reset(new net::TransportSecurityState);
170 net::SSLClientSocketContext context;
171 // CertVerifier and TransportSecurityState are owned by us, not the
172 // context object.
173 context.cert_verifier = cert_verifier_.get();
174 context.transport_security_state = transport_security_state_.get();
176 scoped_ptr<net::ClientSocketHandle> connection(new net::ClientSocketHandle);
177 connection->SetSocket(socket.Pass());
178 net::HostPortPair host_and_port = net::HostPortPair::FromIPEndPoint(
179 ip_endpoint_);
181 return net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
182 connection.Pass(), host_and_port, ssl_config, context);
185 bool CastSocketImpl::ExtractPeerCert(std::string* cert) {
186 DCHECK(cert);
187 DCHECK(peer_cert_.empty());
188 net::SSLInfo ssl_info;
189 if (!socket_->GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) {
190 return false;
193 logger_->LogSocketEvent(channel_id_, proto::SSL_INFO_OBTAINED);
195 // Ensure that the peer cert (which is self-signed) doesn't have an excessive
196 // life-time (i.e. no more than 2 days).
197 base::Time expiry = ssl_info.cert->valid_expiry();
198 base::Time lifetimeLimit =
199 base::Time::Now() +
200 base::TimeDelta::FromDays(kMaxSelfSignedCertLifetimeInDays);
201 if (expiry.is_null() || expiry > lifetimeLimit) {
202 std::string details = FormatTimeForLogging(expiry);
203 details += " " + ip_endpoint().ToString();
204 LOG(ERROR) << "Peer cert has excessive lifetime. details=" << details;
205 logger_->LogSocketEventWithDetails(
206 channel_id_, proto::SSL_CERT_EXCESSIVE_LIFETIME, details);
207 return false;
210 bool result = net::X509Certificate::GetDEREncoded(
211 ssl_info.cert->os_cert_handle(), cert);
212 if (result) {
213 VLOG_WITH_CONNECTION(1) << "Successfully extracted peer certificate";
216 logger_->LogSocketEventWithRv(
217 channel_id_, proto::DER_ENCODED_CERT_OBTAIN, result ? 1 : 0);
218 return result;
221 bool CastSocketImpl::VerifyChannelPolicy(const AuthResult& result) {
222 if ((device_capabilities_ & CastDeviceCapability::VIDEO_OUT) != 0 &&
223 (result.channel_policies & AuthResult::POLICY_AUDIO_ONLY) != 0) {
224 LOG(ERROR) << "Audio only policy enforced";
225 logger_->LogSocketEventWithDetails(
226 channel_id_, proto::CHANNEL_POLICY_ENFORCED, std::string());
227 return false;
229 return true;
232 bool CastSocketImpl::VerifyChallengeReply() {
233 AuthResult result = AuthenticateChallengeReply(*challenge_reply_, peer_cert_);
234 if (result.success()) {
235 VLOG(1) << result.error_message;
236 if (!VerifyChannelPolicy(result)) {
237 return false;
240 logger_->LogSocketChallengeReplyEvent(channel_id_, result);
241 return result.success();
244 void CastSocketImpl::SetTransportForTesting(
245 scoped_ptr<CastTransport> transport) {
246 transport_ = transport.Pass();
249 void CastSocketImpl::Connect(scoped_ptr<CastTransport::Delegate> delegate,
250 base::Callback<void(ChannelError)> callback) {
251 DCHECK(CalledOnValidThread());
252 VLOG_WITH_CONNECTION(1) << "Connect readyState = " << ready_state_;
254 read_delegate_ = delegate.Pass();
256 if (ready_state_ != READY_STATE_NONE) {
257 logger_->LogSocketEventWithDetails(
258 channel_id_, proto::CONNECT_FAILED, "ReadyState not NONE");
259 callback.Run(CHANNEL_ERROR_CONNECT_ERROR);
260 return;
263 connect_callback_ = callback;
264 SetReadyState(READY_STATE_CONNECTING);
265 SetConnectState(proto::CONN_STATE_TCP_CONNECT);
267 // Set up connection timeout.
268 if (connect_timeout_.InMicroseconds() > 0) {
269 DCHECK(connect_timeout_callback_.IsCancelled());
270 connect_timeout_callback_.Reset(
271 base::Bind(&CastSocketImpl::OnConnectTimeout, base::Unretained(this)));
272 GetTimer()->Start(FROM_HERE,
273 connect_timeout_,
274 connect_timeout_callback_.callback());
277 DoConnectLoop(net::OK);
280 CastTransport* CastSocketImpl::transport() const {
281 DCHECK_EQ(ready_state_, READY_STATE_OPEN);
282 return transport_.get();
285 void CastSocketImpl::OnConnectTimeout() {
286 DCHECK(CalledOnValidThread());
287 // Stop all pending connection setup tasks and report back to the client.
288 is_canceled_ = true;
289 logger_->LogSocketEvent(channel_id_, proto::CONNECT_TIMED_OUT);
290 VLOG_WITH_CONNECTION(1) << "Timeout while establishing a connection.";
291 SetErrorState(CHANNEL_ERROR_CONNECT_TIMEOUT);
292 DoConnectCallback();
295 void CastSocketImpl::PostTaskToStartConnectLoop(int result) {
296 DCHECK(CalledOnValidThread());
297 DCHECK(connect_loop_callback_.IsCancelled());
298 connect_loop_callback_.Reset(base::Bind(&CastSocketImpl::DoConnectLoop,
299 base::Unretained(this), result));
300 base::MessageLoop::current()->PostTask(FROM_HERE,
301 connect_loop_callback_.callback());
304 // This method performs the state machine transitions for connection flow.
305 // There are two entry points to this method:
306 // 1. Connect method: this starts the flow
307 // 2. Callback from network operations that finish asynchronously.
308 void CastSocketImpl::DoConnectLoop(int result) {
309 connect_loop_callback_.Cancel();
310 if (is_canceled_) {
311 LOG(ERROR) << "CANCELLED - Aborting DoConnectLoop.";
312 return;
315 // Network operations can either finish synchronously or asynchronously.
316 // This method executes the state machine transitions in a loop so that
317 // correct state transitions happen even when network operations finish
318 // synchronously.
319 int rv = result;
320 do {
321 proto::ConnectionState state = connect_state_;
322 // Default to CONN_STATE_NONE, which breaks the processing loop if any
323 // handler fails to transition to another state to continue processing.
324 connect_state_ = proto::CONN_STATE_NONE;
325 switch (state) {
326 case proto::CONN_STATE_TCP_CONNECT:
327 rv = DoTcpConnect();
328 break;
329 case proto::CONN_STATE_TCP_CONNECT_COMPLETE:
330 rv = DoTcpConnectComplete(rv);
331 break;
332 case proto::CONN_STATE_SSL_CONNECT:
333 DCHECK_EQ(net::OK, rv);
334 rv = DoSslConnect();
335 break;
336 case proto::CONN_STATE_SSL_CONNECT_COMPLETE:
337 rv = DoSslConnectComplete(rv);
338 break;
339 case proto::CONN_STATE_AUTH_CHALLENGE_SEND:
340 rv = DoAuthChallengeSend();
341 break;
342 case proto::CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE:
343 rv = DoAuthChallengeSendComplete(rv);
344 break;
345 case proto::CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE:
346 rv = DoAuthChallengeReplyComplete(rv);
347 break;
348 default:
349 NOTREACHED() << "BUG in connect flow. Unknown state: " << state;
350 break;
352 } while (rv != net::ERR_IO_PENDING &&
353 connect_state_ != proto::CONN_STATE_NONE);
354 // Get out of the loop either when: // a. A network operation is pending, OR
355 // b. The Do* method called did not change state
357 // No state change occurred above. This means state has transitioned to NONE.
358 if (connect_state_ == proto::CONN_STATE_NONE) {
359 logger_->LogSocketConnectState(channel_id_, connect_state_);
362 if (rv != net::ERR_IO_PENDING) {
363 GetTimer()->Stop();
364 DoConnectCallback();
368 int CastSocketImpl::DoTcpConnect() {
369 DCHECK(connect_loop_callback_.IsCancelled());
370 VLOG_WITH_CONNECTION(1) << "DoTcpConnect";
371 SetConnectState(proto::CONN_STATE_TCP_CONNECT_COMPLETE);
372 tcp_socket_ = CreateTcpSocket();
374 int rv = tcp_socket_->Connect(
375 base::Bind(&CastSocketImpl::DoConnectLoop, base::Unretained(this)));
376 logger_->LogSocketEventWithRv(channel_id_, proto::TCP_SOCKET_CONNECT, rv);
377 return rv;
380 int CastSocketImpl::DoTcpConnectComplete(int connect_result) {
381 VLOG_WITH_CONNECTION(1) << "DoTcpConnectComplete: " << connect_result;
382 logger_->LogSocketEventWithRv(channel_id_, proto::TCP_SOCKET_CONNECT_COMPLETE,
383 connect_result);
384 if (connect_result == net::OK) {
385 SetConnectState(proto::CONN_STATE_SSL_CONNECT);
386 } else {
387 SetErrorState(CHANNEL_ERROR_CONNECT_ERROR);
389 return connect_result;
392 int CastSocketImpl::DoSslConnect() {
393 DCHECK(connect_loop_callback_.IsCancelled());
394 VLOG_WITH_CONNECTION(1) << "DoSslConnect";
395 SetConnectState(proto::CONN_STATE_SSL_CONNECT_COMPLETE);
396 socket_ = CreateSslSocket(tcp_socket_.Pass());
398 int rv = socket_->Connect(
399 base::Bind(&CastSocketImpl::DoConnectLoop, base::Unretained(this)));
400 logger_->LogSocketEventWithRv(channel_id_, proto::SSL_SOCKET_CONNECT, rv);
401 return rv;
404 int CastSocketImpl::DoSslConnectComplete(int result) {
405 logger_->LogSocketEventWithRv(channel_id_, proto::SSL_SOCKET_CONNECT_COMPLETE,
406 result);
407 VLOG_WITH_CONNECTION(1) << "DoSslConnectComplete: " << result;
408 if (result == net::ERR_CERT_AUTHORITY_INVALID &&
409 peer_cert_.empty() && ExtractPeerCert(&peer_cert_)) {
410 // Peer certificate fallback - try the connection again, from the top.
411 SetConnectState(proto::CONN_STATE_TCP_CONNECT);
412 } else if (result == net::OK) {
413 // SSL connection succeeded.
414 if (!transport_.get()) {
415 // Create a channel transport if one wasn't already set (e.g. by test
416 // code).
417 transport_.reset(new CastTransportImpl(this->socket_.get(), channel_id_,
418 ip_endpoint_, channel_auth_,
419 logger_));
421 transport_->SetReadDelegate(
422 make_scoped_ptr(new AuthTransportDelegate(this)));
423 if (channel_auth_ == CHANNEL_AUTH_TYPE_SSL_VERIFIED) {
424 // Additionally verify the connection with a handshake.
425 SetConnectState(proto::CONN_STATE_AUTH_CHALLENGE_SEND);
426 } else {
427 transport_->Start();
429 } else {
430 SetErrorState(CHANNEL_ERROR_AUTHENTICATION_ERROR);
432 return result;
435 int CastSocketImpl::DoAuthChallengeSend() {
436 VLOG_WITH_CONNECTION(1) << "DoAuthChallengeSend";
437 SetConnectState(proto::CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE);
439 CastMessage challenge_message;
440 CreateAuthChallengeMessage(&challenge_message);
441 VLOG_WITH_CONNECTION(1) << "Sending challenge: "
442 << CastMessageToString(challenge_message);
444 transport_->SendMessage(
445 challenge_message,
446 base::Bind(&CastSocketImpl::DoConnectLoop, base::Unretained(this)));
448 // Always return IO_PENDING since the result is always asynchronous.
449 return net::ERR_IO_PENDING;
452 int CastSocketImpl::DoAuthChallengeSendComplete(int result) {
453 VLOG_WITH_CONNECTION(1) << "DoAuthChallengeSendComplete: " << result;
454 if (result < 0) {
455 SetErrorState(CHANNEL_ERROR_SOCKET_ERROR);
456 return result;
458 transport_->Start();
459 SetConnectState(proto::CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE);
460 return net::ERR_IO_PENDING;
463 CastSocketImpl::AuthTransportDelegate::AuthTransportDelegate(
464 CastSocketImpl* socket)
465 : socket_(socket) {
466 DCHECK(socket);
469 void CastSocketImpl::AuthTransportDelegate::OnError(
470 ChannelError error_state,
471 const LastErrors& last_errors) {
472 socket_->SetErrorState(error_state);
473 socket_->PostTaskToStartConnectLoop(net::ERR_CONNECTION_FAILED);
476 void CastSocketImpl::AuthTransportDelegate::OnMessage(
477 const CastMessage& message) {
478 if (!IsAuthMessage(message)) {
479 socket_->SetErrorState(CHANNEL_ERROR_TRANSPORT_ERROR);
480 socket_->PostTaskToStartConnectLoop(net::ERR_INVALID_RESPONSE);
481 } else {
482 socket_->challenge_reply_.reset(new CastMessage(message));
483 socket_->logger_->LogSocketEvent(socket_->channel_id_,
484 proto::RECEIVED_CHALLENGE_REPLY);
485 socket_->PostTaskToStartConnectLoop(net::OK);
489 void CastSocketImpl::AuthTransportDelegate::Start() {
492 int CastSocketImpl::DoAuthChallengeReplyComplete(int result) {
493 VLOG_WITH_CONNECTION(1) << "DoAuthChallengeReplyComplete: " << result;
494 if (result < 0) {
495 return result;
497 if (!VerifyChallengeReply()) {
498 SetErrorState(CHANNEL_ERROR_AUTHENTICATION_ERROR);
499 return net::ERR_CONNECTION_FAILED;
501 VLOG_WITH_CONNECTION(1) << "Auth challenge verification succeeded";
502 return net::OK;
505 void CastSocketImpl::DoConnectCallback() {
506 VLOG(1) << "DoConnectCallback (error_state = " << error_state_ << ")";
507 if (error_state_ == CHANNEL_ERROR_NONE) {
508 SetReadyState(READY_STATE_OPEN);
509 transport_->SetReadDelegate(read_delegate_.Pass());
510 } else {
511 SetReadyState(READY_STATE_CLOSED);
512 CloseInternal();
514 base::ResetAndReturn(&connect_callback_).Run(error_state_);
517 void CastSocketImpl::Close(const net::CompletionCallback& callback) {
518 DCHECK(CalledOnValidThread());
519 CloseInternal();
520 // Run this callback last. It may delete the socket.
521 callback.Run(net::OK);
524 void CastSocketImpl::CloseInternal() {
525 // TODO(mfoltz): Enforce this when CastChannelAPITest is rewritten to create
526 // and free sockets on the same thread. crbug.com/398242
527 DCHECK(CalledOnValidThread());
528 if (ready_state_ == READY_STATE_CLOSED) {
529 return;
532 VLOG_WITH_CONNECTION(1) << "Close ReadyState = " << ready_state_;
533 transport_.reset();
534 tcp_socket_.reset();
535 socket_.reset();
536 transport_security_state_.reset();
537 if (GetTimer()) {
538 GetTimer()->Stop();
541 // Cancel callbacks that we queued ourselves to re-enter the connect or read
542 // loops.
543 connect_loop_callback_.Cancel();
544 send_auth_challenge_callback_.Cancel();
545 connect_timeout_callback_.Cancel();
546 SetReadyState(READY_STATE_CLOSED);
547 logger_->LogSocketEvent(channel_id_, proto::SOCKET_CLOSED);
550 std::string CastSocketImpl::cast_url() const {
551 return ((channel_auth_ == CHANNEL_AUTH_TYPE_SSL_VERIFIED) ? "casts://"
552 : "cast://") +
553 ip_endpoint_.ToString();
556 bool CastSocketImpl::CalledOnValidThread() const {
557 return thread_checker_.CalledOnValidThread();
560 base::Timer* CastSocketImpl::GetTimer() {
561 return connect_timeout_timer_.get();
564 void CastSocketImpl::SetConnectState(proto::ConnectionState connect_state) {
565 if (connect_state_ != connect_state) {
566 connect_state_ = connect_state;
567 logger_->LogSocketConnectState(channel_id_, connect_state_);
571 void CastSocketImpl::SetReadyState(ReadyState ready_state) {
572 if (ready_state_ != ready_state) {
573 ready_state_ = ready_state;
574 logger_->LogSocketReadyState(channel_id_, ReadyStateToProto(ready_state_));
578 void CastSocketImpl::SetErrorState(ChannelError error_state) {
579 VLOG_WITH_CONNECTION(1) << "SetErrorState " << error_state;
580 DCHECK_EQ(CHANNEL_ERROR_NONE, error_state_);
581 error_state_ = error_state;
582 logger_->LogSocketErrorState(channel_id_, ErrorStateToProto(error_state_));
584 } // namespace cast_channel
585 } // namespace core_api
586 } // namespace extensions
587 #undef VLOG_WITH_CONNECTION