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_channel_api.h"
10 #include "base/json/json_writer.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/time/default_tick_clock.h"
15 #include "base/values.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "extensions/browser/api/cast_channel/cast_auth_ica.h"
18 #include "extensions/browser/api/cast_channel/cast_message_util.h"
19 #include "extensions/browser/api/cast_channel/cast_socket.h"
20 #include "extensions/browser/api/cast_channel/logger.h"
21 #include "extensions/browser/event_router.h"
22 #include "extensions/common/api/cast_channel/cast_channel.pb.h"
23 #include "extensions/common/api/cast_channel/logging.pb.h"
24 #include "net/base/ip_endpoint.h"
25 #include "net/base/net_errors.h"
26 #include "net/base/net_util.h"
29 // Default timeout interval for connection setup.
30 // Used if not otherwise specified at ConnectInfo::timeout.
31 const int kDefaultConnectTimeoutMillis
= 5000; // 5 seconds.
33 namespace extensions
{
35 namespace Close
= cast_channel::Close
;
36 namespace OnError
= cast_channel::OnError
;
37 namespace OnMessage
= cast_channel::OnMessage
;
38 namespace Open
= cast_channel::Open
;
39 namespace Send
= cast_channel::Send
;
40 using cast_channel::CastMessage
;
41 using cast_channel::CastSocket
;
42 using cast_channel::ChannelAuthType
;
43 using cast_channel::ChannelError
;
44 using cast_channel::ChannelInfo
;
45 using cast_channel::ConnectInfo
;
46 using cast_channel::ErrorInfo
;
47 using cast_channel::LastErrors
;
48 using cast_channel::Logger
;
49 using cast_channel::MessageInfo
;
50 using cast_channel::ReadyState
;
51 using content::BrowserThread
;
55 // T is an extension dictionary (MessageInfo or ChannelInfo)
57 std::string
ParamToString(const T
& info
) {
58 scoped_ptr
<base::DictionaryValue
> dict
= info
.ToValue();
60 base::JSONWriter::Write(dict
.get(), &out
);
64 // Fills |channel_info| from the destination and state of |socket|.
65 void FillChannelInfo(const CastSocket
& socket
, ChannelInfo
* channel_info
) {
67 channel_info
->channel_id
= socket
.id();
68 channel_info
->url
= socket
.cast_url();
69 const net::IPEndPoint
& ip_endpoint
= socket
.ip_endpoint();
70 channel_info
->connect_info
.ip_address
= ip_endpoint
.ToStringWithoutPort();
71 channel_info
->connect_info
.port
= ip_endpoint
.port();
72 channel_info
->connect_info
.auth
= socket
.channel_auth();
73 channel_info
->ready_state
= socket
.ready_state();
74 channel_info
->error_state
= socket
.error_state();
77 // Fills |error_info| from |error_state| and |last_errors|.
78 void FillErrorInfo(ChannelError error_state
,
79 const LastErrors
& last_errors
,
80 ErrorInfo
* error_info
) {
81 error_info
->error_state
= error_state
;
82 if (last_errors
.event_type
!= cast_channel::proto::EVENT_TYPE_UNKNOWN
)
83 error_info
->event_type
.reset(new int(last_errors
.event_type
));
84 if (last_errors
.challenge_reply_error_type
!=
85 cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE
) {
86 error_info
->challenge_reply_error_type
.reset(
87 new int(last_errors
.challenge_reply_error_type
));
89 if (last_errors
.net_return_value
<= 0)
90 error_info
->net_return_value
.reset(new int(last_errors
.net_return_value
));
91 if (last_errors
.nss_error_code
< 0)
92 error_info
->nss_error_code
.reset(new int(last_errors
.nss_error_code
));
95 bool IsValidConnectInfoPort(const ConnectInfo
& connect_info
) {
96 return connect_info
.port
> 0 && connect_info
.port
<
97 std::numeric_limits
<uint16_t>::max();
100 bool IsValidConnectInfoAuth(const ConnectInfo
& connect_info
) {
101 return connect_info
.auth
== cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED
||
102 connect_info
.auth
== cast_channel::CHANNEL_AUTH_TYPE_SSL
;
105 bool IsValidConnectInfoIpAddress(const ConnectInfo
& connect_info
) {
106 net::IPAddressNumber ip_address
;
107 return net::ParseIPLiteralToNumber(connect_info
.ip_address
, &ip_address
);
112 CastChannelAPI::CastChannelAPI(content::BrowserContext
* context
)
113 : browser_context_(context
),
115 new Logger(scoped_ptr
<base::TickClock
>(new base::DefaultTickClock
),
116 base::TimeTicks::UnixEpoch())) {
117 DCHECK(browser_context_
);
121 CastChannelAPI
* CastChannelAPI::Get(content::BrowserContext
* context
) {
122 return BrowserContextKeyedAPIFactory
<CastChannelAPI
>::Get(context
);
125 scoped_refptr
<Logger
> CastChannelAPI::GetLogger() {
129 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<CastChannelAPI
> >
130 g_factory
= LAZY_INSTANCE_INITIALIZER
;
133 BrowserContextKeyedAPIFactory
<CastChannelAPI
>*
134 CastChannelAPI::GetFactoryInstance() {
135 return g_factory
.Pointer();
138 void CastChannelAPI::SetSocketForTest(scoped_ptr
<CastSocket
> socket_for_test
) {
139 socket_for_test_
= socket_for_test
.Pass();
142 scoped_ptr
<CastSocket
> CastChannelAPI::GetSocketForTest() {
143 return socket_for_test_
.Pass();
146 content::BrowserContext
* CastChannelAPI::GetBrowserContext() const {
147 return browser_context_
;
150 CastChannelAPI::~CastChannelAPI() {}
152 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction() : manager_(NULL
) {
155 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
157 bool CastChannelAsyncApiFunction::PrePrepare() {
158 manager_
= ApiResourceManager
<CastSocket
>::Get(browser_context());
162 bool CastChannelAsyncApiFunction::Respond() {
163 return GetError().empty();
166 CastSocket
* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
168 CastSocket
* socket
= GetSocket(channel_id
);
170 SetResultFromError(channel_id
,
171 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID
);
172 AsyncWorkCompleted();
177 int CastChannelAsyncApiFunction::AddSocket(CastSocket
* socket
) {
178 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
181 const int id
= manager_
->Add(socket
);
186 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id
) {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
189 manager_
->Remove(extension_
->id(), channel_id
);
192 void CastChannelAsyncApiFunction::SetResultFromSocket(
193 const CastSocket
& socket
) {
194 ChannelInfo channel_info
;
195 FillChannelInfo(socket
, &channel_info
);
196 ChannelError error
= socket
.error_state();
197 if (error
!= cast_channel::CHANNEL_ERROR_NONE
) {
198 SetError("Channel socket error = " + base::IntToString(error
));
200 SetResultFromChannelInfo(channel_info
);
203 void CastChannelAsyncApiFunction::SetResultFromError(int channel_id
,
204 ChannelError error
) {
205 ChannelInfo channel_info
;
206 channel_info
.channel_id
= channel_id
;
207 channel_info
.url
= "";
208 channel_info
.ready_state
= cast_channel::READY_STATE_CLOSED
;
209 channel_info
.error_state
= error
;
210 channel_info
.connect_info
.ip_address
= "";
211 channel_info
.connect_info
.port
= 0;
212 channel_info
.connect_info
.auth
= cast_channel::CHANNEL_AUTH_TYPE_SSL
;
213 SetResultFromChannelInfo(channel_info
);
214 SetError("Channel error = " + base::IntToString(error
));
217 CastSocket
* CastChannelAsyncApiFunction::GetSocket(int channel_id
) const {
218 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
220 return manager_
->Get(extension_
->id(), channel_id
);
223 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
224 const ChannelInfo
& channel_info
) {
225 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
226 SetResult(channel_info
.ToValue().release());
229 CastChannelOpenFunction::CastChannelOpenFunction()
230 : new_channel_id_(0) { }
232 CastChannelOpenFunction::~CastChannelOpenFunction() { }
234 // TODO(mfoltz): Remove URL parsing when clients have converted to use
237 // Allowed schemes for Cast device URLs.
238 const char kCastInsecureScheme
[] = "cast";
239 const char kCastSecureScheme
[] = "casts";
241 bool CastChannelOpenFunction::ParseChannelUrl(const GURL
& url
,
242 ConnectInfo
* connect_info
) {
243 DCHECK(connect_info
);
244 VLOG(2) << "ParseChannelUrl";
245 bool auth_required
= false;
246 if (url
.SchemeIs(kCastSecureScheme
)) {
247 auth_required
= true;
248 } else if (!url
.SchemeIs(kCastInsecureScheme
)) {
251 // TODO(mfoltz): Test for IPv6 addresses. Brackets or no brackets?
252 // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6
253 // link-local networks
254 const std::string
& path
= url
.path();
255 // Shortest possible: //A:B
256 if (path
.size() < 5) {
259 if (path
.find("//") != 0) {
262 size_t colon
= path
.find_last_of(':');
263 if (colon
== std::string::npos
|| colon
< 3 || colon
> path
.size() - 2) {
266 const std::string
& ip_address_str
= path
.substr(2, colon
- 2);
267 const std::string
& port_str
= path
.substr(colon
+ 1);
268 VLOG(2) << "IP: " << ip_address_str
<< " Port: " << port_str
;
270 if (!base::StringToInt(port_str
, &port
))
272 connect_info
->ip_address
= ip_address_str
;
273 connect_info
->port
= port
;
274 connect_info
->auth
= auth_required
?
275 cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED
:
276 cast_channel::CHANNEL_AUTH_TYPE_SSL
;
280 net::IPEndPoint
* CastChannelOpenFunction::ParseConnectInfo(
281 const ConnectInfo
& connect_info
) {
282 net::IPAddressNumber ip_address
;
283 CHECK(net::ParseIPLiteralToNumber(connect_info
.ip_address
, &ip_address
));
284 return new net::IPEndPoint(ip_address
,
285 static_cast<uint16
>(connect_info
.port
));
288 bool CastChannelOpenFunction::PrePrepare() {
289 api_
= CastChannelAPI::Get(browser_context());
290 return CastChannelAsyncApiFunction::PrePrepare();
293 bool CastChannelOpenFunction::Prepare() {
294 params_
= Open::Params::Create(*args_
);
295 EXTENSION_FUNCTION_VALIDATE(params_
.get());
296 // The connect_info parameter may be a string URL like cast:// or casts:// or
297 // a ConnectInfo object.
298 std::string cast_url
;
299 switch (params_
->connect_info
->GetType()) {
300 case base::Value::TYPE_STRING
:
301 CHECK(params_
->connect_info
->GetAsString(&cast_url
));
302 connect_info_
.reset(new ConnectInfo
);
303 if (!ParseChannelUrl(GURL(cast_url
), connect_info_
.get())) {
304 connect_info_
.reset();
305 SetError("Invalid connect_info (invalid Cast URL " + cast_url
+ ")");
308 case base::Value::TYPE_DICTIONARY
:
309 connect_info_
= ConnectInfo::FromValue(*(params_
->connect_info
));
310 if (!connect_info_
.get()) {
311 SetError("connect_info.auth is required");
315 SetError("Invalid connect_info (unknown type)");
318 if (!connect_info_
.get()) {
321 if (!IsValidConnectInfoPort(*connect_info_
)) {
322 SetError("Invalid connect_info (invalid port)");
323 } else if (!IsValidConnectInfoAuth(*connect_info_
)) {
324 SetError("Invalid connect_info (invalid auth)");
325 } else if (!IsValidConnectInfoIpAddress(*connect_info_
)) {
326 SetError("Invalid connect_info (invalid IP address)");
328 if (!GetError().empty()) {
331 channel_auth_
= connect_info_
->auth
;
332 ip_endpoint_
.reset(ParseConnectInfo(*connect_info_
));
336 void CastChannelOpenFunction::AsyncWorkStart() {
338 DCHECK(ip_endpoint_
.get());
340 scoped_ptr
<CastSocket
> test_socket
= api_
->GetSocketForTest();
341 if (test_socket
.get()) {
342 socket
= test_socket
.release();
344 socket
= new cast_channel::CastSocketImpl(
345 extension_
->id(), *ip_endpoint_
, channel_auth_
,
346 ExtensionsBrowserClient::Get()->GetNetLog(),
347 base::TimeDelta::FromMilliseconds(connect_info_
->timeout
.get()
348 ? *connect_info_
->timeout
349 : kDefaultConnectTimeoutMillis
),
352 new_channel_id_
= AddSocket(socket
);
353 scoped_ptr
<CastMessageHandler
> delegate(new CastMessageHandler(api_
, socket
));
354 api_
->GetLogger()->LogNewSocketEvent(*socket
);
355 socket
->Connect(delegate
.Pass(),
356 base::Bind(&CastChannelOpenFunction::OnOpen
, this));
359 void CastChannelOpenFunction::OnOpen(cast_channel::ChannelError result
) {
360 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
361 VLOG(1) << "Connect finished, OnOpen invoked.";
362 CastSocket
* socket
= GetSocket(new_channel_id_
);
364 SetResultFromError(new_channel_id_
, result
);
366 SetResultFromSocket(*socket
);
368 AsyncWorkCompleted();
371 CastChannelSendFunction::CastChannelSendFunction() { }
373 CastChannelSendFunction::~CastChannelSendFunction() { }
375 bool CastChannelSendFunction::Prepare() {
376 params_
= Send::Params::Create(*args_
);
377 EXTENSION_FUNCTION_VALIDATE(params_
.get());
378 if (params_
->message
.namespace_
.empty()) {
379 SetError("message_info.namespace_ is required");
382 if (params_
->message
.source_id
.empty()) {
383 SetError("message_info.source_id is required");
386 if (params_
->message
.destination_id
.empty()) {
387 SetError("message_info.destination_id is required");
390 switch (params_
->message
.data
->GetType()) {
391 case base::Value::TYPE_STRING
:
392 case base::Value::TYPE_BINARY
:
395 SetError("Invalid type of message_info.data");
401 void CastChannelSendFunction::AsyncWorkStart() {
402 CastSocket
* socket
= GetSocket(params_
->channel
.channel_id
);
404 SetResultFromError(params_
->channel
.channel_id
,
405 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID
);
406 AsyncWorkCompleted();
409 CastMessage message_to_send
;
410 if (!MessageInfoToCastMessage(params_
->message
, &message_to_send
)) {
411 SetResultFromError(params_
->channel
.channel_id
,
412 cast_channel::CHANNEL_ERROR_INVALID_MESSAGE
);
413 AsyncWorkCompleted();
416 socket
->transport()->SendMessage(
417 message_to_send
, base::Bind(&CastChannelSendFunction::OnSend
, this));
420 void CastChannelSendFunction::OnSend(int result
) {
421 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
422 int channel_id
= params_
->channel
.channel_id
;
423 CastSocket
* socket
= GetSocket(channel_id
);
424 if (result
< 0 || !socket
) {
425 SetResultFromError(channel_id
,
426 cast_channel::CHANNEL_ERROR_SOCKET_ERROR
);
428 SetResultFromSocket(*socket
);
430 AsyncWorkCompleted();
433 CastChannelCloseFunction::CastChannelCloseFunction() { }
435 CastChannelCloseFunction::~CastChannelCloseFunction() { }
437 bool CastChannelCloseFunction::Prepare() {
438 params_
= Close::Params::Create(*args_
);
439 EXTENSION_FUNCTION_VALIDATE(params_
.get());
443 void CastChannelCloseFunction::AsyncWorkStart() {
444 CastSocket
* socket
= GetSocket(params_
->channel
.channel_id
);
446 SetResultFromError(params_
->channel
.channel_id
,
447 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID
);
448 AsyncWorkCompleted();
450 socket
->Close(base::Bind(&CastChannelCloseFunction::OnClose
, this));
454 void CastChannelCloseFunction::OnClose(int result
) {
455 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
456 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result
;
457 int channel_id
= params_
->channel
.channel_id
;
458 CastSocket
* socket
= GetSocket(channel_id
);
459 if (result
< 0 || !socket
) {
460 SetResultFromError(channel_id
,
461 cast_channel::CHANNEL_ERROR_SOCKET_ERROR
);
463 SetResultFromSocket(*socket
);
464 // This will delete |socket|.
465 RemoveSocket(channel_id
);
467 AsyncWorkCompleted();
470 CastChannelGetLogsFunction::CastChannelGetLogsFunction() {
473 CastChannelGetLogsFunction::~CastChannelGetLogsFunction() {
476 bool CastChannelGetLogsFunction::PrePrepare() {
477 api_
= CastChannelAPI::Get(browser_context());
478 return CastChannelAsyncApiFunction::PrePrepare();
481 bool CastChannelGetLogsFunction::Prepare() {
485 void CastChannelGetLogsFunction::AsyncWorkStart() {
489 scoped_ptr
<char[]> out
= api_
->GetLogger()->GetLogs(&length
);
491 SetResult(new base::BinaryValue(out
.Pass(), length
));
493 SetError("Unable to get logs.");
496 api_
->GetLogger()->Reset();
498 AsyncWorkCompleted();
501 CastChannelOpenFunction::CastMessageHandler::CastMessageHandler(
503 cast_channel::CastSocket
* socket
)
504 : api(api
), socket(socket
) {
509 CastChannelOpenFunction::CastMessageHandler::~CastMessageHandler() {
512 void CastChannelOpenFunction::CastMessageHandler::OnError(
513 cast_channel::ChannelError error_state
,
514 const cast_channel::LastErrors
& last_errors
) {
515 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
517 ChannelInfo channel_info
;
518 FillChannelInfo(*socket
, &channel_info
);
519 channel_info
.error_state
= error_state
;
520 ErrorInfo error_info
;
521 FillErrorInfo(error_state
, last_errors
, &error_info
);
523 scoped_ptr
<base::ListValue
> results
=
524 OnError::Create(channel_info
, error_info
);
525 scoped_ptr
<Event
> event(new Event(OnError::kEventName
, results
.Pass()));
526 extensions::EventRouter::Get(api
->GetBrowserContext())
527 ->DispatchEventToExtension(socket
->owner_extension_id(), event
.Pass());
530 void CastChannelOpenFunction::CastMessageHandler::OnMessage(
531 const CastMessage
& message
) {
532 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
534 MessageInfo message_info
;
535 cast_channel::CastMessageToMessageInfo(message
, &message_info
);
536 ChannelInfo channel_info
;
537 FillChannelInfo(*socket
, &channel_info
);
538 VLOG(1) << "Received message " << ParamToString(message_info
)
539 << " on channel " << ParamToString(channel_info
);
541 scoped_ptr
<base::ListValue
> results
=
542 OnMessage::Create(channel_info
, message_info
);
543 scoped_ptr
<Event
> event(new Event(OnMessage::kEventName
, results
.Pass()));
544 extensions::EventRouter::Get(api
->GetBrowserContext())
545 ->DispatchEventToExtension(socket
->owner_extension_id(), event
.Pass());
548 CastChannelSetAuthorityKeysFunction::CastChannelSetAuthorityKeysFunction() {
551 CastChannelSetAuthorityKeysFunction::~CastChannelSetAuthorityKeysFunction() {
554 bool CastChannelSetAuthorityKeysFunction::Prepare() {
555 params_
= cast_channel::SetAuthorityKeys::Params::Create(*args_
);
556 EXTENSION_FUNCTION_VALIDATE(params_
.get());
560 void CastChannelSetAuthorityKeysFunction::AsyncWorkStart() {
561 std::string
& keys
= params_
->keys
;
562 std::string
& signature
= params_
->signature
;
563 if (signature
.empty() || keys
.empty() ||
564 !cast_channel::SetTrustedCertificateAuthorities(keys
, signature
)) {
565 SetError("Unable to set authority keys.");
568 AsyncWorkCompleted();
571 } // namespace extensions