Home | History | Annotate | Download | only in cast_channel
      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