| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 "chrome/browser/extensions/api/cast_channel/cast_channel_api.h" | |
| 6 | |
| 7 #include <limits> | |
| 8 | |
| 9 #include "base/json/json_writer.h" | |
| 10 #include "base/memory/scoped_ptr.h" | |
| 11 #include "base/strings/string_number_conversions.h" | |
| 12 #include "base/values.h" | |
| 13 #include "chrome/browser/browser_process.h" | |
| 14 #include "chrome/browser/extensions/api/cast_channel/cast_socket.h" | |
| 15 #include "chrome/browser/net/chrome_net_log.h" | |
| 16 #include "content/public/browser/browser_thread.h" | |
| 17 #include "extensions/browser/event_router.h" | |
| 18 #include "net/base/ip_endpoint.h" | |
| 19 #include "net/base/net_errors.h" | |
| 20 #include "net/base/net_util.h" | |
| 21 #include "url/gurl.h" | |
| 22 | |
| 23 namespace extensions { | |
| 24 | |
| 25 namespace Close = cast_channel::Close; | |
| 26 namespace OnError = cast_channel::OnError; | |
| 27 namespace OnMessage = cast_channel::OnMessage; | |
| 28 namespace Open = cast_channel::Open; | |
| 29 namespace Send = cast_channel::Send; | |
| 30 using cast_channel::CastSocket; | |
| 31 using cast_channel::ChannelAuthType; | |
| 32 using cast_channel::ChannelError; | |
| 33 using cast_channel::ChannelInfo; | |
| 34 using cast_channel::ConnectInfo; | |
| 35 using cast_channel::MessageInfo; | |
| 36 using cast_channel::ReadyState; | |
| 37 using content::BrowserThread; | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 // T is an extension dictionary (MessageInfo or ChannelInfo) | |
| 42 template <class T> | |
| 43 std::string ParamToString(const T& info) { | |
| 44 scoped_ptr<base::DictionaryValue> dict = info.ToValue(); | |
| 45 std::string out; | |
| 46 base::JSONWriter::Write(dict.get(), &out); | |
| 47 return out; | |
| 48 } | |
| 49 | |
| 50 // Fills |channel_info| from the destination and state of |socket|. | |
| 51 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) { | |
| 52 DCHECK(channel_info); | |
| 53 channel_info->channel_id = socket.id(); | |
| 54 channel_info->url = socket.CastUrl(); | |
| 55 const net::IPEndPoint& ip_endpoint = socket.ip_endpoint(); | |
| 56 channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort(); | |
| 57 channel_info->connect_info.port = ip_endpoint.port(); | |
| 58 channel_info->connect_info.auth = socket.channel_auth(); | |
| 59 channel_info->ready_state = socket.ready_state(); | |
| 60 channel_info->error_state = socket.error_state(); | |
| 61 } | |
| 62 | |
| 63 } // namespace | |
| 64 | |
| 65 CastChannelAPI::CastChannelAPI(content::BrowserContext* context) | |
| 66 : browser_context_(context) { | |
| 67 DCHECK(browser_context_); | |
| 68 } | |
| 69 | |
| 70 // static | |
| 71 CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) { | |
| 72 return BrowserContextKeyedAPIFactory<CastChannelAPI>::Get(context); | |
| 73 } | |
| 74 | |
| 75 static base::LazyInstance<BrowserContextKeyedAPIFactory<CastChannelAPI> > | |
| 76 g_factory = LAZY_INSTANCE_INITIALIZER; | |
| 77 | |
| 78 // static | |
| 79 BrowserContextKeyedAPIFactory<CastChannelAPI>* | |
| 80 CastChannelAPI::GetFactoryInstance() { | |
| 81 return g_factory.Pointer(); | |
| 82 } | |
| 83 | |
| 84 scoped_ptr<CastSocket> CastChannelAPI::CreateCastSocket( | |
| 85 const std::string& extension_id, const net::IPEndPoint& ip_endpoint, | |
| 86 ChannelAuthType channel_auth) { | |
| 87 if (socket_for_test_.get()) { | |
| 88 return socket_for_test_.Pass(); | |
| 89 } else { | |
| 90 return scoped_ptr<CastSocket>( | |
| 91 new CastSocket(extension_id, ip_endpoint, channel_auth, this, | |
| 92 g_browser_process->net_log())); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 void CastChannelAPI::SetSocketForTest(scoped_ptr<CastSocket> socket_for_test) { | |
| 97 socket_for_test_ = socket_for_test.Pass(); | |
| 98 } | |
| 99 | |
| 100 void CastChannelAPI::OnError(const CastSocket* socket, | |
| 101 cast_channel::ChannelError error) { | |
| 102 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 103 ChannelInfo channel_info; | |
| 104 FillChannelInfo(*socket, &channel_info); | |
| 105 channel_info.error_state = error; | |
| 106 scoped_ptr<base::ListValue> results = OnError::Create(channel_info); | |
| 107 scoped_ptr<Event> event(new Event(OnError::kEventName, results.Pass())); | |
| 108 extensions::EventRouter::Get(browser_context_) | |
| 109 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass()); | |
| 110 } | |
| 111 | |
| 112 void CastChannelAPI::OnMessage(const CastSocket* socket, | |
| 113 const MessageInfo& message_info) { | |
| 114 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 115 ChannelInfo channel_info; | |
| 116 FillChannelInfo(*socket, &channel_info); | |
| 117 scoped_ptr<base::ListValue> results = | |
| 118 OnMessage::Create(channel_info, message_info); | |
| 119 VLOG(1) << "Sending message " << ParamToString(message_info) | |
| 120 << " to channel " << ParamToString(channel_info); | |
| 121 scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass())); | |
| 122 extensions::EventRouter::Get(browser_context_) | |
| 123 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass()); | |
| 124 } | |
| 125 | |
| 126 CastChannelAPI::~CastChannelAPI() {} | |
| 127 | |
| 128 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction() | |
| 129 : manager_(NULL), error_(cast_channel::CHANNEL_ERROR_NONE) { } | |
| 130 | |
| 131 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { } | |
| 132 | |
| 133 bool CastChannelAsyncApiFunction::PrePrepare() { | |
| 134 manager_ = ApiResourceManager<CastSocket>::Get(browser_context()); | |
| 135 return true; | |
| 136 } | |
| 137 | |
| 138 bool CastChannelAsyncApiFunction::Respond() { | |
| 139 return error_ != cast_channel::CHANNEL_ERROR_NONE; | |
| 140 } | |
| 141 | |
| 142 CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError( | |
| 143 int channel_id) { | |
| 144 CastSocket* socket = GetSocket(channel_id); | |
| 145 if (!socket) { | |
| 146 SetResultFromError(cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID); | |
| 147 AsyncWorkCompleted(); | |
| 148 } | |
| 149 return socket; | |
| 150 } | |
| 151 | |
| 152 int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) { | |
| 153 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 154 DCHECK(socket); | |
| 155 DCHECK(manager_); | |
| 156 const int id = manager_->Add(socket); | |
| 157 socket->set_id(id); | |
| 158 return id; | |
| 159 } | |
| 160 | |
| 161 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) { | |
| 162 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 163 DCHECK(manager_); | |
| 164 manager_->Remove(extension_->id(), channel_id); | |
| 165 } | |
| 166 | |
| 167 void CastChannelAsyncApiFunction::SetResultFromSocket(int channel_id) { | |
| 168 CastSocket* socket = GetSocket(channel_id); | |
| 169 DCHECK(socket); | |
| 170 ChannelInfo channel_info; | |
| 171 FillChannelInfo(*socket, &channel_info); | |
| 172 error_ = socket->error_state(); | |
| 173 SetResultFromChannelInfo(channel_info); | |
| 174 } | |
| 175 | |
| 176 void CastChannelAsyncApiFunction::SetResultFromError(ChannelError error) { | |
| 177 ChannelInfo channel_info; | |
| 178 channel_info.channel_id = -1; | |
| 179 channel_info.url = ""; | |
| 180 channel_info.ready_state = cast_channel::READY_STATE_CLOSED; | |
| 181 channel_info.error_state = error; | |
| 182 SetResultFromChannelInfo(channel_info); | |
| 183 error_ = error; | |
| 184 } | |
| 185 | |
| 186 CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) { | |
| 187 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 188 DCHECK(manager_); | |
| 189 return manager_->Get(extension_->id(), channel_id); | |
| 190 } | |
| 191 | |
| 192 void CastChannelAsyncApiFunction::SetResultFromChannelInfo( | |
| 193 const ChannelInfo& channel_info) { | |
| 194 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 195 SetResult(channel_info.ToValue().release()); | |
| 196 } | |
| 197 | |
| 198 CastChannelOpenFunction::CastChannelOpenFunction() | |
| 199 : new_channel_id_(0) { } | |
| 200 | |
| 201 CastChannelOpenFunction::~CastChannelOpenFunction() { } | |
| 202 | |
| 203 // TODO(mfoltz): Remove URL parsing when clients have converted to use | |
| 204 // ConnectInfo. | |
| 205 | |
| 206 // Allowed schemes for Cast device URLs. | |
| 207 const char kCastInsecureScheme[] = "cast"; | |
| 208 const char kCastSecureScheme[] = "casts"; | |
| 209 | |
| 210 bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url, | |
| 211 ConnectInfo* connect_info) { | |
| 212 DCHECK(connect_info); | |
| 213 VLOG(2) << "ParseChannelUrl"; | |
| 214 bool auth_required = false; | |
| 215 if (url.SchemeIs(kCastSecureScheme)) { | |
| 216 auth_required = true; | |
| 217 } else if (!url.SchemeIs(kCastInsecureScheme)) { | |
| 218 return false; | |
| 219 } | |
| 220 // TODO(mfoltz): Test for IPv6 addresses. Brackets or no brackets? | |
| 221 // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6 | |
| 222 // link-local networks | |
| 223 const std::string& path = url.path(); | |
| 224 // Shortest possible: //A:B | |
| 225 if (path.size() < 5) { | |
| 226 return false; | |
| 227 } | |
| 228 if (path.find("//") != 0) { | |
| 229 return false; | |
| 230 } | |
| 231 size_t colon = path.find_last_of(':'); | |
| 232 if (colon == std::string::npos || colon < 3 || colon > path.size() - 2) { | |
| 233 return false; | |
| 234 } | |
| 235 const std::string& ip_address_str = path.substr(2, colon - 2); | |
| 236 const std::string& port_str = path.substr(colon + 1); | |
| 237 VLOG(2) << "IP: " << ip_address_str << " Port: " << port_str; | |
| 238 int port; | |
| 239 if (!base::StringToInt(port_str, &port)) | |
| 240 return false; | |
| 241 connect_info->ip_address = ip_address_str; | |
| 242 connect_info->port = port; | |
| 243 connect_info->auth = auth_required ? | |
| 244 cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED : | |
| 245 cast_channel::CHANNEL_AUTH_TYPE_SSL; | |
| 246 return true; | |
| 247 }; | |
| 248 | |
| 249 net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo( | |
| 250 const ConnectInfo& connect_info) { | |
| 251 net::IPAddressNumber ip_address; | |
| 252 if (!net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address)) { | |
| 253 return NULL; | |
| 254 } | |
| 255 if (connect_info.port < 0 || connect_info.port > | |
| 256 std::numeric_limits<unsigned short>::max()) { | |
| 257 return NULL; | |
| 258 } | |
| 259 if (connect_info.auth != cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED && | |
| 260 connect_info.auth != cast_channel::CHANNEL_AUTH_TYPE_SSL) { | |
| 261 return NULL; | |
| 262 } | |
| 263 return new net::IPEndPoint(ip_address, connect_info.port); | |
| 264 } | |
| 265 | |
| 266 bool CastChannelOpenFunction::PrePrepare() { | |
| 267 api_ = CastChannelAPI::Get(browser_context()); | |
| 268 return CastChannelAsyncApiFunction::PrePrepare(); | |
| 269 } | |
| 270 | |
| 271 bool CastChannelOpenFunction::Prepare() { | |
| 272 params_ = Open::Params::Create(*args_); | |
| 273 EXTENSION_FUNCTION_VALIDATE(params_.get()); | |
| 274 // The connect_info parameter may be a string URL like cast:// or casts:// or | |
| 275 // a ConnectInfo object. | |
| 276 std::string cast_url; | |
| 277 switch (params_->connect_info->GetType()) { | |
| 278 case base::Value::TYPE_STRING: | |
| 279 CHECK(params_->connect_info->GetAsString(&cast_url)); | |
| 280 connect_info_.reset(new ConnectInfo); | |
| 281 if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) { | |
| 282 connect_info_.reset(); | |
| 283 } | |
| 284 break; | |
| 285 case base::Value::TYPE_DICTIONARY: | |
| 286 connect_info_ = ConnectInfo::FromValue(*(params_->connect_info)); | |
| 287 break; | |
| 288 default: | |
| 289 break; | |
| 290 } | |
| 291 if (connect_info_.get()) { | |
| 292 channel_auth_ = connect_info_->auth; | |
| 293 ip_endpoint_.reset(ParseConnectInfo(*connect_info_)); | |
| 294 return ip_endpoint_.get() != NULL; | |
| 295 } | |
| 296 return false; | |
| 297 } | |
| 298 | |
| 299 void CastChannelOpenFunction::AsyncWorkStart() { | |
| 300 DCHECK(api_); | |
| 301 DCHECK(ip_endpoint_.get()); | |
| 302 scoped_ptr<CastSocket> socket = api_->CreateCastSocket( | |
| 303 extension_->id(), *ip_endpoint_, channel_auth_); | |
| 304 new_channel_id_ = AddSocket(socket.release()); | |
| 305 GetSocket(new_channel_id_)->Connect( | |
| 306 base::Bind(&CastChannelOpenFunction::OnOpen, this)); | |
| 307 } | |
| 308 | |
| 309 void CastChannelOpenFunction::OnOpen(int result) { | |
| 310 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 311 SetResultFromSocket(new_channel_id_); | |
| 312 AsyncWorkCompleted(); | |
| 313 } | |
| 314 | |
| 315 CastChannelSendFunction::CastChannelSendFunction() { } | |
| 316 | |
| 317 CastChannelSendFunction::~CastChannelSendFunction() { } | |
| 318 | |
| 319 bool CastChannelSendFunction::Prepare() { | |
| 320 params_ = Send::Params::Create(*args_); | |
| 321 EXTENSION_FUNCTION_VALIDATE(params_.get()); | |
| 322 return true; | |
| 323 } | |
| 324 | |
| 325 void CastChannelSendFunction::AsyncWorkStart() { | |
| 326 CastSocket* socket = GetSocketOrCompleteWithError( | |
| 327 params_->channel.channel_id); | |
| 328 if (socket) | |
| 329 socket->SendMessage(params_->message, | |
| 330 base::Bind(&CastChannelSendFunction::OnSend, this)); | |
| 331 } | |
| 332 | |
| 333 void CastChannelSendFunction::OnSend(int result) { | |
| 334 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 335 if (result < 0) { | |
| 336 SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR); | |
| 337 } else { | |
| 338 SetResultFromSocket(params_->channel.channel_id); | |
| 339 } | |
| 340 AsyncWorkCompleted(); | |
| 341 } | |
| 342 | |
| 343 CastChannelCloseFunction::CastChannelCloseFunction() { } | |
| 344 | |
| 345 CastChannelCloseFunction::~CastChannelCloseFunction() { } | |
| 346 | |
| 347 bool CastChannelCloseFunction::Prepare() { | |
| 348 params_ = Close::Params::Create(*args_); | |
| 349 EXTENSION_FUNCTION_VALIDATE(params_.get()); | |
| 350 return true; | |
| 351 } | |
| 352 | |
| 353 void CastChannelCloseFunction::AsyncWorkStart() { | |
| 354 CastSocket* socket = GetSocketOrCompleteWithError( | |
| 355 params_->channel.channel_id); | |
| 356 if (socket) | |
| 357 socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this)); | |
| 358 } | |
| 359 | |
| 360 void CastChannelCloseFunction::OnClose(int result) { | |
| 361 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 362 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result; | |
| 363 if (result < 0) { | |
| 364 SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR); | |
| 365 } else { | |
| 366 int channel_id = params_->channel.channel_id; | |
| 367 SetResultFromSocket(channel_id); | |
| 368 RemoveSocket(channel_id); | |
| 369 } | |
| 370 AsyncWorkCompleted(); | |
| 371 } | |
| 372 | |
| 373 } // namespace extensions | |
| OLD | NEW |