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. 4 5 #include "extensions/browser/api/cast_channel/cast_channel_api.h" 6 7 #include <limits> 8 #include <string> 9 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_socket.h" 18 #include "extensions/browser/api/cast_channel/logger.h" 19 #include "extensions/browser/event_router.h" 20 #include "extensions/common/api/cast_channel/logging.pb.h" 21 #include "net/base/ip_endpoint.h" 22 #include "net/base/net_errors.h" 23 #include "net/base/net_util.h" 24 #include "url/gurl.h" 25 26 // Default timeout interval for connection setup. 27 // Used if not otherwise specified at ConnectInfo::timeout. 28 const int kDefaultConnectTimeoutMillis = 5000; // 5 seconds. 29 30 namespace extensions { 31 32 namespace Close = cast_channel::Close; 33 namespace OnError = cast_channel::OnError; 34 namespace OnMessage = cast_channel::OnMessage; 35 namespace Open = cast_channel::Open; 36 namespace Send = cast_channel::Send; 37 using cast_channel::CastSocket; 38 using cast_channel::ChannelAuthType; 39 using cast_channel::ChannelError; 40 using cast_channel::ChannelInfo; 41 using cast_channel::ConnectInfo; 42 using cast_channel::ErrorInfo; 43 using cast_channel::LastErrors; 44 using cast_channel::Logger; 45 using cast_channel::MessageInfo; 46 using cast_channel::ReadyState; 47 using content::BrowserThread; 48 49 namespace { 50 51 // T is an extension dictionary (MessageInfo or ChannelInfo) 52 template <class T> 53 std::string ParamToString(const T& info) { 54 scoped_ptr<base::DictionaryValue> dict = info.ToValue(); 55 std::string out; 56 base::JSONWriter::Write(dict.get(), &out); 57 return out; 58 } 59 60 // Fills |channel_info| from the destination and state of |socket|. 61 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) { 62 DCHECK(channel_info); 63 channel_info->channel_id = socket.id(); 64 channel_info->url = socket.CastUrl(); 65 const net::IPEndPoint& ip_endpoint = socket.ip_endpoint(); 66 channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort(); 67 channel_info->connect_info.port = ip_endpoint.port(); 68 channel_info->connect_info.auth = socket.channel_auth(); 69 channel_info->ready_state = socket.ready_state(); 70 channel_info->error_state = socket.error_state(); 71 } 72 73 // Fills |error_info| from |error_state| and |last_errors|. 74 void FillErrorInfo(ChannelError error_state, 75 const LastErrors& last_errors, 76 ErrorInfo* error_info) { 77 error_info->error_state = error_state; 78 if (last_errors.event_type != cast_channel::proto::EVENT_TYPE_UNKNOWN) 79 error_info->event_type.reset(new int(last_errors.event_type)); 80 if (last_errors.challenge_reply_error_type != 81 cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE) { 82 error_info->challenge_reply_error_type.reset( 83 new int(last_errors.challenge_reply_error_type)); 84 } 85 if (last_errors.net_return_value <= 0) 86 error_info->net_return_value.reset(new int(last_errors.net_return_value)); 87 if (last_errors.nss_error_code < 0) 88 error_info->nss_error_code.reset(new int(last_errors.nss_error_code)); 89 } 90 91 bool IsValidConnectInfoPort(const ConnectInfo& connect_info) { 92 return connect_info.port > 0 && connect_info.port < 93 std::numeric_limits<uint16_t>::max(); 94 } 95 96 bool IsValidConnectInfoAuth(const ConnectInfo& connect_info) { 97 return connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED || 98 connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL; 99 } 100 101 bool IsValidConnectInfoIpAddress(const ConnectInfo& connect_info) { 102 net::IPAddressNumber ip_address; 103 return net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address); 104 } 105 106 } // namespace 107 108 CastChannelAPI::CastChannelAPI(content::BrowserContext* context) 109 : browser_context_(context), 110 logger_( 111 new Logger(scoped_ptr<base::TickClock>(new base::DefaultTickClock), 112 base::TimeTicks::UnixEpoch())) { 113 DCHECK(browser_context_); 114 } 115 116 // static 117 CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) { 118 return BrowserContextKeyedAPIFactory<CastChannelAPI>::Get(context); 119 } 120 121 scoped_refptr<Logger> CastChannelAPI::GetLogger() { 122 return logger_; 123 } 124 125 static base::LazyInstance<BrowserContextKeyedAPIFactory<CastChannelAPI> > 126 g_factory = LAZY_INSTANCE_INITIALIZER; 127 128 // static 129 BrowserContextKeyedAPIFactory<CastChannelAPI>* 130 CastChannelAPI::GetFactoryInstance() { 131 return g_factory.Pointer(); 132 } 133 134 void CastChannelAPI::SetSocketForTest(scoped_ptr<CastSocket> socket_for_test) { 135 socket_for_test_ = socket_for_test.Pass(); 136 } 137 138 scoped_ptr<cast_channel::CastSocket> CastChannelAPI::GetSocketForTest() { 139 return socket_for_test_.Pass(); 140 } 141 142 void CastChannelAPI::OnError(const CastSocket* socket, 143 cast_channel::ChannelError error_state, 144 const cast_channel::LastErrors& last_errors) { 145 DCHECK_CURRENTLY_ON(BrowserThread::IO); 146 ChannelInfo channel_info; 147 FillChannelInfo(*socket, &channel_info); 148 channel_info.error_state = error_state; 149 ErrorInfo error_info; 150 FillErrorInfo(error_state, last_errors, &error_info); 151 scoped_ptr<base::ListValue> results = 152 OnError::Create(channel_info, error_info); 153 scoped_ptr<Event> event(new Event(OnError::kEventName, results.Pass())); 154 extensions::EventRouter::Get(browser_context_) 155 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass()); 156 } 157 158 void CastChannelAPI::OnMessage(const CastSocket* socket, 159 const MessageInfo& message_info) { 160 DCHECK_CURRENTLY_ON(BrowserThread::IO); 161 ChannelInfo channel_info; 162 FillChannelInfo(*socket, &channel_info); 163 scoped_ptr<base::ListValue> results = 164 OnMessage::Create(channel_info, message_info); 165 VLOG(1) << "Sending message " << ParamToString(message_info) 166 << " to channel " << ParamToString(channel_info); 167 scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass())); 168 extensions::EventRouter::Get(browser_context_) 169 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass()); 170 } 171 172 CastChannelAPI::~CastChannelAPI() {} 173 174 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction() 175 : manager_(NULL), error_(cast_channel::CHANNEL_ERROR_NONE) { } 176 177 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { } 178 179 bool CastChannelAsyncApiFunction::PrePrepare() { 180 manager_ = ApiResourceManager<CastSocket>::Get(browser_context()); 181 return true; 182 } 183 184 bool CastChannelAsyncApiFunction::Respond() { 185 return error_ == cast_channel::CHANNEL_ERROR_NONE; 186 } 187 188 CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError( 189 int channel_id) { 190 CastSocket* socket = GetSocket(channel_id); 191 if (!socket) { 192 SetResultFromError(channel_id, 193 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID); 194 AsyncWorkCompleted(); 195 } 196 return socket; 197 } 198 199 int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) { 200 DCHECK_CURRENTLY_ON(BrowserThread::IO); 201 DCHECK(socket); 202 DCHECK(manager_); 203 const int id = manager_->Add(socket); 204 socket->set_id(id); 205 return id; 206 } 207 208 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) { 209 DCHECK_CURRENTLY_ON(BrowserThread::IO); 210 DCHECK(manager_); 211 manager_->Remove(extension_->id(), channel_id); 212 } 213 214 void CastChannelAsyncApiFunction::SetResultFromSocket( 215 const CastSocket& socket) { 216 ChannelInfo channel_info; 217 FillChannelInfo(socket, &channel_info); 218 error_ = socket.error_state(); 219 SetResultFromChannelInfo(channel_info); 220 } 221 222 void CastChannelAsyncApiFunction::SetResultFromError(int channel_id, 223 ChannelError error) { 224 ChannelInfo channel_info; 225 channel_info.channel_id = channel_id; 226 channel_info.url = ""; 227 channel_info.ready_state = cast_channel::READY_STATE_CLOSED; 228 channel_info.error_state = error; 229 channel_info.connect_info.ip_address = ""; 230 channel_info.connect_info.port = 0; 231 channel_info.connect_info.auth = cast_channel::CHANNEL_AUTH_TYPE_SSL; 232 SetResultFromChannelInfo(channel_info); 233 error_ = error; 234 } 235 236 CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) { 237 DCHECK_CURRENTLY_ON(BrowserThread::IO); 238 DCHECK(manager_); 239 return manager_->Get(extension_->id(), channel_id); 240 } 241 242 void CastChannelAsyncApiFunction::SetResultFromChannelInfo( 243 const ChannelInfo& channel_info) { 244 DCHECK_CURRENTLY_ON(BrowserThread::IO); 245 SetResult(channel_info.ToValue().release()); 246 } 247 248 CastChannelOpenFunction::CastChannelOpenFunction() 249 : new_channel_id_(0) { } 250 251 CastChannelOpenFunction::~CastChannelOpenFunction() { } 252 253 // TODO(mfoltz): Remove URL parsing when clients have converted to use 254 // ConnectInfo. 255 256 // Allowed schemes for Cast device URLs. 257 const char kCastInsecureScheme[] = "cast"; 258 const char kCastSecureScheme[] = "casts"; 259 260 bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url, 261 ConnectInfo* connect_info) { 262 DCHECK(connect_info); 263 VLOG(2) << "ParseChannelUrl"; 264 bool auth_required = false; 265 if (url.SchemeIs(kCastSecureScheme)) { 266 auth_required = true; 267 } else if (!url.SchemeIs(kCastInsecureScheme)) { 268 return false; 269 } 270 // TODO(mfoltz): Test for IPv6 addresses. Brackets or no brackets? 271 // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6 272 // link-local networks 273 const std::string& path = url.path(); 274 // Shortest possible: //A:B 275 if (path.size() < 5) { 276 return false; 277 } 278 if (path.find("//") != 0) { 279 return false; 280 } 281 size_t colon = path.find_last_of(':'); 282 if (colon == std::string::npos || colon < 3 || colon > path.size() - 2) { 283 return false; 284 } 285 const std::string& ip_address_str = path.substr(2, colon - 2); 286 const std::string& port_str = path.substr(colon + 1); 287 VLOG(2) << "IP: " << ip_address_str << " Port: " << port_str; 288 int port; 289 if (!base::StringToInt(port_str, &port)) 290 return false; 291 connect_info->ip_address = ip_address_str; 292 connect_info->port = port; 293 connect_info->auth = auth_required ? 294 cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED : 295 cast_channel::CHANNEL_AUTH_TYPE_SSL; 296 return true; 297 } 298 299 net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo( 300 const ConnectInfo& connect_info) { 301 net::IPAddressNumber ip_address; 302 CHECK(net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address)); 303 return new net::IPEndPoint(ip_address, connect_info.port); 304 } 305 306 bool CastChannelOpenFunction::PrePrepare() { 307 api_ = CastChannelAPI::Get(browser_context()); 308 return CastChannelAsyncApiFunction::PrePrepare(); 309 } 310 311 bool CastChannelOpenFunction::Prepare() { 312 params_ = Open::Params::Create(*args_); 313 EXTENSION_FUNCTION_VALIDATE(params_.get()); 314 // The connect_info parameter may be a string URL like cast:// or casts:// or 315 // a ConnectInfo object. 316 std::string cast_url; 317 switch (params_->connect_info->GetType()) { 318 case base::Value::TYPE_STRING: 319 CHECK(params_->connect_info->GetAsString(&cast_url)); 320 connect_info_.reset(new ConnectInfo); 321 if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) { 322 connect_info_.reset(); 323 SetError("Invalid connect_info (invalid Cast URL " + cast_url + ")"); 324 } 325 break; 326 case base::Value::TYPE_DICTIONARY: 327 connect_info_ = ConnectInfo::FromValue(*(params_->connect_info)); 328 if (!connect_info_.get()) { 329 SetError("connect_info.auth is required"); 330 } 331 break; 332 default: 333 SetError("Invalid connect_info (unknown type)"); 334 break; 335 } 336 if (!connect_info_.get()) { 337 return false; 338 } 339 if (!IsValidConnectInfoPort(*connect_info_)) { 340 SetError("Invalid connect_info (invalid port)"); 341 } else if (!IsValidConnectInfoAuth(*connect_info_)) { 342 SetError("Invalid connect_info (invalid auth)"); 343 } else if (!IsValidConnectInfoIpAddress(*connect_info_)) { 344 SetError("Invalid connect_info (invalid IP address)"); 345 } 346 if (!GetError().empty()) { 347 return false; 348 } 349 channel_auth_ = connect_info_->auth; 350 ip_endpoint_.reset(ParseConnectInfo(*connect_info_)); 351 return true; 352 } 353 354 void CastChannelOpenFunction::AsyncWorkStart() { 355 DCHECK(api_); 356 DCHECK(ip_endpoint_.get()); 357 scoped_ptr<CastSocket> socket = api_->GetSocketForTest(); 358 if (!socket.get()) { 359 socket.reset(new CastSocket( 360 extension_->id(), 361 *ip_endpoint_, 362 channel_auth_, 363 api_, 364 ExtensionsBrowserClient::Get()->GetNetLog(), 365 base::TimeDelta::FromMilliseconds(connect_info_->timeout.get() 366 ? *connect_info_->timeout 367 : kDefaultConnectTimeoutMillis), 368 api_->GetLogger())); 369 } 370 new_channel_id_ = AddSocket(socket.release()); 371 CastSocket* new_socket = GetSocket(new_channel_id_); 372 api_->GetLogger()->LogNewSocketEvent(*new_socket); 373 new_socket->Connect(base::Bind(&CastChannelOpenFunction::OnOpen, this)); 374 } 375 376 void CastChannelOpenFunction::OnOpen(int result) { 377 DCHECK_CURRENTLY_ON(BrowserThread::IO); 378 VLOG(1) << "Connect finished, OnOpen invoked."; 379 CastSocket* socket = GetSocket(new_channel_id_); 380 if (!socket) { 381 SetResultFromError(new_channel_id_, 382 cast_channel::CHANNEL_ERROR_CONNECT_ERROR); 383 } else { 384 SetResultFromSocket(*socket); 385 } 386 AsyncWorkCompleted(); 387 } 388 389 CastChannelSendFunction::CastChannelSendFunction() { } 390 391 CastChannelSendFunction::~CastChannelSendFunction() { } 392 393 bool CastChannelSendFunction::Prepare() { 394 params_ = Send::Params::Create(*args_); 395 EXTENSION_FUNCTION_VALIDATE(params_.get()); 396 if (params_->message.namespace_.empty()) { 397 SetError("message_info.namespace_ is required"); 398 return false; 399 } 400 if (params_->message.source_id.empty()) { 401 SetError("message_info.source_id is required"); 402 return false; 403 } 404 if (params_->message.destination_id.empty()) { 405 SetError("message_info.destination_id is required"); 406 return false; 407 } 408 switch (params_->message.data->GetType()) { 409 case base::Value::TYPE_STRING: 410 case base::Value::TYPE_BINARY: 411 break; 412 default: 413 SetError("Invalid type of message_info.data"); 414 return false; 415 } 416 return true; 417 } 418 419 void CastChannelSendFunction::AsyncWorkStart() { 420 CastSocket* socket = GetSocket(params_->channel.channel_id); 421 if (!socket) { 422 SetResultFromError(params_->channel.channel_id, 423 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID); 424 AsyncWorkCompleted(); 425 return; 426 } 427 socket->SendMessage(params_->message, 428 base::Bind(&CastChannelSendFunction::OnSend, this)); 429 } 430 431 void CastChannelSendFunction::OnSend(int result) { 432 DCHECK_CURRENTLY_ON(BrowserThread::IO); 433 int channel_id = params_->channel.channel_id; 434 CastSocket* socket = GetSocket(channel_id); 435 if (result < 0 || !socket) { 436 SetResultFromError(channel_id, 437 cast_channel::CHANNEL_ERROR_SOCKET_ERROR); 438 } else { 439 SetResultFromSocket(*socket); 440 } 441 AsyncWorkCompleted(); 442 } 443 444 CastChannelCloseFunction::CastChannelCloseFunction() { } 445 446 CastChannelCloseFunction::~CastChannelCloseFunction() { } 447 448 bool CastChannelCloseFunction::Prepare() { 449 params_ = Close::Params::Create(*args_); 450 EXTENSION_FUNCTION_VALIDATE(params_.get()); 451 return true; 452 } 453 454 void CastChannelCloseFunction::AsyncWorkStart() { 455 CastSocket* socket = GetSocket(params_->channel.channel_id); 456 if (!socket) { 457 SetResultFromError(params_->channel.channel_id, 458 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID); 459 AsyncWorkCompleted(); 460 } else { 461 socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this)); 462 } 463 } 464 465 void CastChannelCloseFunction::OnClose(int result) { 466 DCHECK_CURRENTLY_ON(BrowserThread::IO); 467 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result; 468 int channel_id = params_->channel.channel_id; 469 CastSocket* socket = GetSocket(channel_id); 470 if (result < 0 || !socket) { 471 SetResultFromError(channel_id, 472 cast_channel::CHANNEL_ERROR_SOCKET_ERROR); 473 } else { 474 SetResultFromSocket(*socket); 475 // This will delete |socket|. 476 RemoveSocket(channel_id); 477 socket = NULL; 478 } 479 AsyncWorkCompleted(); 480 } 481 482 CastChannelGetLogsFunction::CastChannelGetLogsFunction() { 483 } 484 485 CastChannelGetLogsFunction::~CastChannelGetLogsFunction() { 486 } 487 488 bool CastChannelGetLogsFunction::PrePrepare() { 489 api_ = CastChannelAPI::Get(browser_context()); 490 return CastChannelAsyncApiFunction::PrePrepare(); 491 } 492 493 bool CastChannelGetLogsFunction::Prepare() { 494 return true; 495 } 496 497 void CastChannelGetLogsFunction::AsyncWorkStart() { 498 DCHECK(api_); 499 500 size_t length = 0; 501 scoped_ptr<char[]> out = api_->GetLogger()->GetLogs(&length); 502 if (out.get()) { 503 SetResult(new base::BinaryValue(out.Pass(), length)); 504 } else { 505 SetError("Unable to get logs."); 506 } 507 508 api_->GetLogger()->Reset(); 509 510 AsyncWorkCompleted(); 511 } 512 513 } // namespace extensions 514