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/memory/scoped_ptr.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/time/default_tick_clock.h"
14 #include "base/values.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "extensions/browser/api/cast_channel/cast_socket.h"
17 #include "extensions/browser/api/cast_channel/logger.h"
18 #include "extensions/browser/api/cast_channel/logging.pb.h"
19 #include "extensions/browser/event_router.h"
20 #include "net/base/ip_endpoint.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/net_util.h"
25 // Default timeout interval for connection setup.
26 // Used if not otherwise specified at ConnectInfo::timeout.
27 const int kDefaultConnectTimeoutMillis = 5000; // 5 seconds.
29 namespace extensions {
31 namespace Close = cast_channel::Close;
32 namespace OnError = cast_channel::OnError;
33 namespace OnMessage = cast_channel::OnMessage;
34 namespace Open = cast_channel::Open;
35 namespace Send = cast_channel::Send;
36 using cast_channel::CastSocket;
37 using cast_channel::ChannelAuthType;
38 using cast_channel::ChannelError;
39 using cast_channel::ChannelInfo;
40 using cast_channel::ConnectInfo;
41 using cast_channel::ErrorInfo;
42 using cast_channel::LastErrors;
43 using cast_channel::Logger;
44 using cast_channel::MessageInfo;
45 using cast_channel::ReadyState;
46 using content::BrowserThread;
50 // T is an extension dictionary (MessageInfo or ChannelInfo)
52 std::string ParamToString(const T& info) {
53 scoped_ptr<base::DictionaryValue> dict = info.ToValue();
55 base::JSONWriter::Write(dict.get(), &out);
59 // Fills |channel_info| from the destination and state of |socket|.
60 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) {
62 channel_info->channel_id = socket.id();
63 channel_info->url = socket.CastUrl();
64 const net::IPEndPoint& ip_endpoint = socket.ip_endpoint();
65 channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort();
66 channel_info->connect_info.port = ip_endpoint.port();
67 channel_info->connect_info.auth = socket.channel_auth();
68 channel_info->ready_state = socket.ready_state();
69 channel_info->error_state = socket.error_state();
72 // Fills |error_info| from |error_state| and |last_errors|.
73 void FillErrorInfo(ChannelError error_state,
74 const LastErrors& last_errors,
75 ErrorInfo* error_info) {
76 error_info->error_state = error_state;
77 if (last_errors.event_type != cast_channel::proto::EVENT_TYPE_UNKNOWN)
78 error_info->event_type.reset(new int(last_errors.event_type));
79 if (last_errors.challenge_reply_error_type !=
80 cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE) {
81 error_info->challenge_reply_error_type.reset(
82 new int(last_errors.challenge_reply_error_type));
84 if (last_errors.net_return_value <= 0)
85 error_info->net_return_value.reset(new int(last_errors.net_return_value));
86 if (last_errors.nss_error_code < 0)
87 error_info->nss_error_code.reset(new int(last_errors.nss_error_code));
90 bool IsValidConnectInfoPort(const ConnectInfo& connect_info) {
91 return connect_info.port > 0 && connect_info.port <
92 std::numeric_limits<uint16_t>::max();
95 bool IsValidConnectInfoAuth(const ConnectInfo& connect_info) {
96 return connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED ||
97 connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL;
100 bool IsValidConnectInfoIpAddress(const ConnectInfo& connect_info) {
101 net::IPAddressNumber ip_address;
102 return net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address);
107 CastChannelAPI::CastChannelAPI(content::BrowserContext* context)
108 : browser_context_(context),
110 new Logger(scoped_ptr<base::TickClock>(new base::DefaultTickClock),
111 base::TimeTicks::UnixEpoch())) {
112 DCHECK(browser_context_);
116 CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) {
117 return BrowserContextKeyedAPIFactory<CastChannelAPI>::Get(context);
120 scoped_refptr<Logger> CastChannelAPI::GetLogger() {
124 static base::LazyInstance<BrowserContextKeyedAPIFactory<CastChannelAPI> >
125 g_factory = LAZY_INSTANCE_INITIALIZER;
128 BrowserContextKeyedAPIFactory<CastChannelAPI>*
129 CastChannelAPI::GetFactoryInstance() {
130 return g_factory.Pointer();
133 scoped_ptr<CastSocket> CastChannelAPI::CreateCastSocket(
134 const std::string& extension_id, const net::IPEndPoint& ip_endpoint,
135 ChannelAuthType channel_auth, const base::TimeDelta& timeout) {
136 if (socket_for_test_.get()) {
137 return socket_for_test_.Pass();
139 return scoped_ptr<CastSocket>(
140 new CastSocket(extension_id,
144 ExtensionsBrowserClient::Get()->GetNetLog(),
150 void CastChannelAPI::SetSocketForTest(scoped_ptr<CastSocket> socket_for_test) {
151 socket_for_test_ = socket_for_test.Pass();
154 void CastChannelAPI::OnError(const CastSocket* socket,
155 cast_channel::ChannelError error_state,
156 const cast_channel::LastErrors& last_errors) {
157 DCHECK_CURRENTLY_ON(BrowserThread::IO);
158 ChannelInfo channel_info;
159 FillChannelInfo(*socket, &channel_info);
160 channel_info.error_state = error_state;
161 ErrorInfo error_info;
162 FillErrorInfo(error_state, last_errors, &error_info);
163 scoped_ptr<base::ListValue> results =
164 OnError::Create(channel_info, error_info);
165 scoped_ptr<Event> event(new Event(OnError::kEventName, results.Pass()));
166 extensions::EventRouter::Get(browser_context_)
167 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
170 void CastChannelAPI::OnMessage(const CastSocket* socket,
171 const MessageInfo& message_info) {
172 DCHECK_CURRENTLY_ON(BrowserThread::IO);
173 ChannelInfo channel_info;
174 FillChannelInfo(*socket, &channel_info);
175 scoped_ptr<base::ListValue> results =
176 OnMessage::Create(channel_info, message_info);
177 VLOG(1) << "Sending message " << ParamToString(message_info)
178 << " to channel " << ParamToString(channel_info);
179 scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass()));
180 extensions::EventRouter::Get(browser_context_)
181 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
184 CastChannelAPI::~CastChannelAPI() {}
186 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction()
187 : manager_(NULL), error_(cast_channel::CHANNEL_ERROR_NONE) { }
189 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
191 bool CastChannelAsyncApiFunction::PrePrepare() {
192 manager_ = ApiResourceManager<CastSocket>::Get(browser_context());
196 bool CastChannelAsyncApiFunction::Respond() {
197 return error_ != cast_channel::CHANNEL_ERROR_NONE;
200 CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
202 CastSocket* socket = GetSocket(channel_id);
204 SetResultFromError(channel_id,
205 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID);
206 AsyncWorkCompleted();
211 int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) {
212 DCHECK_CURRENTLY_ON(BrowserThread::IO);
215 const int id = manager_->Add(socket);
220 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) {
221 DCHECK_CURRENTLY_ON(BrowserThread::IO);
223 manager_->Remove(extension_->id(), channel_id);
226 void CastChannelAsyncApiFunction::SetResultFromSocket(
227 const CastSocket& socket) {
228 ChannelInfo channel_info;
229 FillChannelInfo(socket, &channel_info);
230 error_ = socket.error_state();
231 SetResultFromChannelInfo(channel_info);
234 void CastChannelAsyncApiFunction::SetResultFromError(int channel_id,
235 ChannelError error) {
236 ChannelInfo channel_info;
237 channel_info.channel_id = channel_id;
238 channel_info.url = "";
239 channel_info.ready_state = cast_channel::READY_STATE_CLOSED;
240 channel_info.error_state = error;
241 channel_info.connect_info.ip_address = "";
242 channel_info.connect_info.port = 0;
243 channel_info.connect_info.auth = cast_channel::CHANNEL_AUTH_TYPE_SSL;
244 SetResultFromChannelInfo(channel_info);
248 CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) {
249 DCHECK_CURRENTLY_ON(BrowserThread::IO);
251 return manager_->Get(extension_->id(), channel_id);
254 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
255 const ChannelInfo& channel_info) {
256 DCHECK_CURRENTLY_ON(BrowserThread::IO);
257 SetResult(channel_info.ToValue().release());
260 CastChannelOpenFunction::CastChannelOpenFunction()
261 : new_channel_id_(0) { }
263 CastChannelOpenFunction::~CastChannelOpenFunction() { }
265 // TODO(mfoltz): Remove URL parsing when clients have converted to use
268 // Allowed schemes for Cast device URLs.
269 const char kCastInsecureScheme[] = "cast";
270 const char kCastSecureScheme[] = "casts";
272 bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url,
273 ConnectInfo* connect_info) {
274 DCHECK(connect_info);
275 VLOG(2) << "ParseChannelUrl";
276 bool auth_required = false;
277 if (url.SchemeIs(kCastSecureScheme)) {
278 auth_required = true;
279 } else if (!url.SchemeIs(kCastInsecureScheme)) {
282 // TODO(mfoltz): Test for IPv6 addresses. Brackets or no brackets?
283 // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6
284 // link-local networks
285 const std::string& path = url.path();
286 // Shortest possible: //A:B
287 if (path.size() < 5) {
290 if (path.find("//") != 0) {
293 size_t colon = path.find_last_of(':');
294 if (colon == std::string::npos || colon < 3 || colon > path.size() - 2) {
297 const std::string& ip_address_str = path.substr(2, colon - 2);
298 const std::string& port_str = path.substr(colon + 1);
299 VLOG(2) << "IP: " << ip_address_str << " Port: " << port_str;
301 if (!base::StringToInt(port_str, &port))
303 connect_info->ip_address = ip_address_str;
304 connect_info->port = port;
305 connect_info->auth = auth_required ?
306 cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED :
307 cast_channel::CHANNEL_AUTH_TYPE_SSL;
311 net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo(
312 const ConnectInfo& connect_info) {
313 net::IPAddressNumber ip_address;
314 CHECK(net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address));
315 return new net::IPEndPoint(ip_address, connect_info.port);
318 bool CastChannelOpenFunction::PrePrepare() {
319 api_ = CastChannelAPI::Get(browser_context());
320 return CastChannelAsyncApiFunction::PrePrepare();
323 bool CastChannelOpenFunction::Prepare() {
324 params_ = Open::Params::Create(*args_);
325 EXTENSION_FUNCTION_VALIDATE(params_.get());
326 // The connect_info parameter may be a string URL like cast:// or casts:// or
327 // a ConnectInfo object.
328 std::string cast_url;
329 switch (params_->connect_info->GetType()) {
330 case base::Value::TYPE_STRING:
331 CHECK(params_->connect_info->GetAsString(&cast_url));
332 connect_info_.reset(new ConnectInfo);
333 if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) {
334 connect_info_.reset();
335 SetError("Invalid connect_info (invalid Cast URL " + cast_url + ")");
338 case base::Value::TYPE_DICTIONARY:
339 connect_info_ = ConnectInfo::FromValue(*(params_->connect_info));
340 if (!connect_info_.get()) {
341 SetError("connect_info.auth is required");
345 SetError("Invalid connect_info (unknown type)");
348 if (!connect_info_.get()) {
351 if (!IsValidConnectInfoPort(*connect_info_)) {
352 SetError("Invalid connect_info (invalid port)");
353 } else if (!IsValidConnectInfoAuth(*connect_info_)) {
354 SetError("Invalid connect_info (invalid auth)");
355 } else if (!IsValidConnectInfoIpAddress(*connect_info_)) {
356 SetError("Invalid connect_info (invalid IP address)");
358 if (!GetError().empty()) {
361 channel_auth_ = connect_info_->auth;
362 ip_endpoint_.reset(ParseConnectInfo(*connect_info_));
366 void CastChannelOpenFunction::AsyncWorkStart() {
368 DCHECK(ip_endpoint_.get());
369 scoped_ptr<CastSocket> socket = api_->CreateCastSocket(
373 base::TimeDelta::FromMilliseconds(connect_info_->timeout.get()
374 ? *connect_info_->timeout
375 : kDefaultConnectTimeoutMillis));
376 new_channel_id_ = AddSocket(socket.release());
377 CastSocket* new_socket = GetSocket(new_channel_id_);
378 api_->GetLogger()->LogNewSocketEvent(*new_socket);
379 new_socket->Connect(base::Bind(&CastChannelOpenFunction::OnOpen, this));
382 void CastChannelOpenFunction::OnOpen(int result) {
383 DCHECK_CURRENTLY_ON(BrowserThread::IO);
384 VLOG(1) << "Connect finished, OnOpen invoked.";
385 CastSocket* socket = GetSocket(new_channel_id_);
387 SetResultFromError(new_channel_id_,
388 cast_channel::CHANNEL_ERROR_CONNECT_ERROR);
390 SetResultFromSocket(*socket);
392 AsyncWorkCompleted();
395 CastChannelSendFunction::CastChannelSendFunction() { }
397 CastChannelSendFunction::~CastChannelSendFunction() { }
399 bool CastChannelSendFunction::Prepare() {
400 params_ = Send::Params::Create(*args_);
401 EXTENSION_FUNCTION_VALIDATE(params_.get());
402 if (params_->message.namespace_.empty()) {
403 SetError("message_info.namespace_ is required");
406 if (params_->message.source_id.empty()) {
407 SetError("message_info.source_id is required");
410 if (params_->message.destination_id.empty()) {
411 SetError("message_info.destination_id is required");
414 switch (params_->message.data->GetType()) {
415 case base::Value::TYPE_STRING:
416 case base::Value::TYPE_BINARY:
419 SetError("Invalid type of message_info.data");
425 void CastChannelSendFunction::AsyncWorkStart() {
426 CastSocket* socket = GetSocketOrCompleteWithError(
427 params_->channel.channel_id);
429 socket->SendMessage(params_->message,
430 base::Bind(&CastChannelSendFunction::OnSend, this));
433 void CastChannelSendFunction::OnSend(int result) {
434 DCHECK_CURRENTLY_ON(BrowserThread::IO);
435 int channel_id = params_->channel.channel_id;
436 CastSocket* socket = GetSocket(channel_id);
437 if (result < 0 || !socket) {
438 SetResultFromError(channel_id,
439 cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
441 SetResultFromSocket(*socket);
443 AsyncWorkCompleted();
446 CastChannelCloseFunction::CastChannelCloseFunction() { }
448 CastChannelCloseFunction::~CastChannelCloseFunction() { }
450 bool CastChannelCloseFunction::Prepare() {
451 params_ = Close::Params::Create(*args_);
452 EXTENSION_FUNCTION_VALIDATE(params_.get());
456 void CastChannelCloseFunction::AsyncWorkStart() {
457 CastSocket* socket = GetSocketOrCompleteWithError(
458 params_->channel.channel_id);
460 socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this));
463 void CastChannelCloseFunction::OnClose(int result) {
464 DCHECK_CURRENTLY_ON(BrowserThread::IO);
465 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result;
466 int channel_id = params_->channel.channel_id;
467 CastSocket* socket = GetSocket(channel_id);
468 if (result < 0 || !socket) {
469 SetResultFromError(channel_id,
470 cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
472 SetResultFromSocket(*socket);
473 // This will delete |socket|.
474 RemoveSocket(channel_id);
477 AsyncWorkCompleted();
480 CastChannelGetLogsFunction::CastChannelGetLogsFunction() {
483 CastChannelGetLogsFunction::~CastChannelGetLogsFunction() {
486 bool CastChannelGetLogsFunction::PrePrepare() {
487 api_ = CastChannelAPI::Get(browser_context());
488 return CastChannelAsyncApiFunction::PrePrepare();
491 bool CastChannelGetLogsFunction::Prepare() {
495 void CastChannelGetLogsFunction::AsyncWorkStart() {
499 scoped_ptr<char[]> out = api_->GetLogger()->GetLogs(&length);
501 SetResult(new base::BinaryValue(out.Pass(), length));
503 SetError("Unable to get logs.");
506 api_->GetLogger()->Reset();
508 AsyncWorkCompleted();
511 } // namespace extensions