1 // Copyright (c) 2012 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/dial/dial_service.h" 6 7 #include <algorithm> 8 9 #include "base/basictypes.h" 10 #include "base/callback.h" 11 #include "base/logging.h" 12 #include "base/rand_util.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/time/time.h" 16 #include "chrome/browser/extensions/api/dial/dial_device_data.h" 17 #include "chrome/common/chrome_version_info.h" 18 #include "content/public/browser/browser_thread.h" 19 #include "net/base/completion_callback.h" 20 #include "net/base/io_buffer.h" 21 #include "net/base/ip_endpoint.h" 22 #include "net/base/net_errors.h" 23 #include "net/base/net_util.h" 24 #include "net/http/http_response_headers.h" 25 #include "net/http/http_util.h" 26 #include "url/gurl.h" 27 #if defined(OS_CHROMEOS) 28 #include "chromeos/network/network_state.h" 29 #include "chromeos/network/network_state_handler.h" 30 #include "third_party/cros_system_api/dbus/service_constants.h" 31 #endif 32 33 using base::Time; 34 using base::TimeDelta; 35 using content::BrowserThread; 36 using net::HttpResponseHeaders; 37 using net::HttpUtil; 38 using net::IOBufferWithSize; 39 using net::IPAddressNumber; 40 using net::IPEndPoint; 41 using net::NetworkInterface; 42 using net::NetworkInterfaceList; 43 using net::StringIOBuffer; 44 using net::UDPSocket; 45 46 namespace extensions { 47 48 namespace { 49 50 // The total number of requests to make per discovery cycle. 51 const int kDialMaxRequests = 4; 52 53 // The interval to wait between successive requests. 54 const int kDialRequestIntervalMillis = 1000; 55 56 // The timeout (MX) set for responses. 57 const int kDialResponseTimeoutSecs = 2; 58 59 // The multicast IP address for discovery. 60 const char kDialRequestAddress[] = "239.255.255.250"; 61 62 // The UDP port number for discovery. 63 const int kDialRequestPort = 1900; 64 65 // The DIAL service type as part of the search request. 66 const char kDialSearchType[] = "urn:dial-multiscreen-org:service:dial:1"; 67 68 // SSDP headers parsed from the response. 69 const char kSsdpLocationHeader[] = "LOCATION"; 70 const char kSsdpCacheControlHeader[] = "CACHE-CONTROL"; 71 const char kSsdpConfigIdHeader[] = "CONFIGID.UPNP.ORG"; 72 const char kSsdpUsnHeader[] = "USN"; 73 74 // The receive buffer size, in bytes. 75 const int kDialRecvBufferSize = 1500; 76 77 // Gets a specific header from |headers| and puts it in |value|. 78 bool GetHeader(HttpResponseHeaders* headers, const char* name, 79 std::string* value) { 80 return headers->EnumerateHeader(NULL, std::string(name), value); 81 } 82 83 // Returns the request string. 84 std::string BuildRequest() { 85 // Extra line at the end to make UPnP lib happy. 86 chrome::VersionInfo version; 87 std::string request(base::StringPrintf( 88 "M-SEARCH * HTTP/1.1\r\n" 89 "HOST:%s:%i\r\n" 90 "MAN:\"ssdp:discover\"\r\n" 91 "MX:%d\r\n" 92 "ST:%s\r\n" 93 "USER-AGENT:%s/%s %s\r\n" 94 "\r\n", 95 kDialRequestAddress, 96 kDialRequestPort, 97 kDialResponseTimeoutSecs, 98 kDialSearchType, 99 version.Name().c_str(), 100 version.Version().c_str(), 101 version.OSType().c_str())); 102 // 1500 is a good MTU value for most Ethernet LANs. 103 DCHECK(request.size() <= 1500); 104 return request; 105 } 106 107 void GetNetworkListOnFileThread( 108 const scoped_refptr<base::MessageLoopProxy>& loop, 109 const base::Callback<void(const NetworkInterfaceList& networks)>& cb) { 110 NetworkInterfaceList list; 111 bool success = net::GetNetworkList(&list); 112 if (!success) 113 DVLOG(1) << "Could not retrieve network list!"; 114 115 loop->PostTask(FROM_HERE, base::Bind(cb, list)); 116 } 117 118 } // namespace 119 120 DialServiceImpl::DialServiceImpl(net::NetLog* net_log) 121 : is_writing_(false), 122 is_reading_(false), 123 discovery_active_(false), 124 num_requests_sent_(0), 125 max_requests_(kDialMaxRequests), 126 finish_delay_(TimeDelta::FromMilliseconds((kDialMaxRequests - 1) * 127 kDialRequestIntervalMillis) + 128 TimeDelta::FromSeconds(kDialResponseTimeoutSecs)), 129 request_interval_(TimeDelta::FromMilliseconds(kDialRequestIntervalMillis)) 130 { 131 IPAddressNumber address; 132 bool result = net::ParseIPLiteralToNumber(kDialRequestAddress, &address); 133 DCHECK(result); 134 send_address_ = IPEndPoint(address, kDialRequestPort); 135 send_buffer_ = new StringIOBuffer(BuildRequest()); 136 net_log_ = net_log; 137 net_log_source_.type = net::NetLog::SOURCE_UDP_SOCKET; 138 net_log_source_.id = net_log_->NextID(); 139 } 140 141 DialServiceImpl::~DialServiceImpl() { 142 DCHECK(thread_checker_.CalledOnValidThread()); 143 } 144 145 void DialServiceImpl::AddObserver(Observer* observer) { 146 DCHECK(thread_checker_.CalledOnValidThread()); 147 observer_list_.AddObserver(observer); 148 } 149 150 void DialServiceImpl::RemoveObserver(Observer* observer) { 151 DCHECK(thread_checker_.CalledOnValidThread()); 152 observer_list_.RemoveObserver(observer); 153 } 154 155 bool DialServiceImpl::HasObserver(Observer* observer) { 156 DCHECK(thread_checker_.CalledOnValidThread()); 157 return observer_list_.HasObserver(observer); 158 } 159 160 bool DialServiceImpl::Discover() { 161 DCHECK(thread_checker_.CalledOnValidThread()); 162 if (discovery_active_) 163 return false; 164 discovery_active_ = true; 165 166 DVLOG(1) << "Discovery started."; 167 168 StartDiscovery(); 169 return true; 170 } 171 172 void DialServiceImpl::StartDiscovery() { 173 DCHECK(thread_checker_.CalledOnValidThread()); 174 DCHECK(discovery_active_); 175 if (socket_.get()) 176 return; 177 178 #if defined(OS_CHROMEOS) 179 // The ChromeOS specific version of getting network interfaces does not 180 // require trampolining to another thread, and contains additional interface 181 // information such as interface types (i.e. wifi vs cellular). 182 BindSocketAndSendRequest(GetBestBindAddressChromeOS()); 183 #else 184 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( 185 &GetNetworkListOnFileThread, 186 base::MessageLoopProxy::current(), base::Bind( 187 &DialServiceImpl::SendNetworkList, AsWeakPtr()))); 188 #endif 189 } 190 191 #if defined(OS_CHROMEOS) 192 IPAddressNumber DialServiceImpl::GetBestBindAddressChromeOS() { 193 std::string connection_types[] = 194 {flimflam::kTypeWifi, flimflam::kTypeEthernet}; 195 for (uint i = 0; i < arraysize(connection_types); ++i) { 196 IPAddressNumber bind_ip_address; 197 const chromeos::NetworkState* state = 198 chromeos::NetworkHandler::Get()->network_state_handler()-> 199 ConnectedNetworkByType(connection_types[i]); 200 if (state && 201 net::ParseIPLiteralToNumber(state->ip_address(), &bind_ip_address)) { 202 DCHECK(bind_ip_address.size() == net::kIPv4AddressSize); 203 DVLOG(1) << "Found " << state->type() << ", " << state->name() << ":" 204 << state->ip_address(); 205 return bind_ip_address; 206 } 207 } 208 return IPAddressNumber(); 209 } 210 #endif 211 212 bool DialServiceImpl::BindSocketAndSendRequest( 213 const IPAddressNumber& bind_ip_address) { 214 DCHECK(thread_checker_.CalledOnValidThread()); 215 DCHECK(!socket_.get()); 216 217 if (bind_ip_address.size() == 0) { 218 DVLOG(1) << "Could not find a valid interface to bind."; 219 FinishDiscovery(); 220 return false; 221 } 222 223 net::RandIntCallback rand_cb = base::Bind(&base::RandInt); 224 socket_.reset(new UDPSocket(net::DatagramSocket::RANDOM_BIND, 225 rand_cb, 226 net_log_, 227 net_log_source_)); 228 socket_->AllowBroadcast(); 229 230 // Schedule a timer to finish the discovery process (and close the socket). 231 if (finish_delay_ > TimeDelta::FromSeconds(0)) { 232 finish_timer_.Start(FROM_HERE, 233 finish_delay_, 234 this, 235 &DialServiceImpl::FinishDiscovery); 236 } 237 238 // 0 means bind a random port 239 IPEndPoint address(bind_ip_address, 0); 240 241 if (!CheckResult("Bind", socket_->Bind(address))) 242 return false; 243 244 DCHECK(socket_.get()); 245 246 recv_buffer_ = new IOBufferWithSize(kDialRecvBufferSize); 247 if (!ReadSocket()) 248 return false; 249 SendOneRequest(); 250 return true; 251 } 252 253 void DialServiceImpl::SendOneRequest() { 254 if (num_requests_sent_ == max_requests_) { 255 request_timer_.Stop(); 256 return; 257 } 258 num_requests_sent_++; 259 if (!socket_.get()) { 260 DLOG(WARNING) << "Socket not connected."; 261 return; 262 } 263 264 if (is_writing_) { 265 DVLOG(1) << "Already writing."; 266 return; 267 } 268 DVLOG(1) << "Sending request " << num_requests_sent_ << "/" 269 << max_requests_; 270 is_writing_ = true; 271 int result = socket_->SendTo( 272 send_buffer_.get(), send_buffer_->size(), send_address_, 273 base::Bind(&DialServiceImpl::OnSocketWrite, AsWeakPtr())); 274 bool result_ok = CheckResult("SendTo", result); 275 if (result_ok && result > 0) { 276 // Synchronous write. 277 OnSocketWrite(result); 278 } 279 } 280 281 void DialServiceImpl::OnSocketWrite(int result) { 282 DCHECK(thread_checker_.CalledOnValidThread()); 283 is_writing_ = false; 284 if (!CheckResult("OnSocketWrite", result)) 285 return; 286 287 if (result != send_buffer_->size()) { 288 DLOG(ERROR) << "Sent " << result << " chars, expected " 289 << send_buffer_->size() << " chars"; 290 } 291 // If discovery is inactive, no reason to notify observers. 292 if (!discovery_active_) { 293 DVLOG(1) << "Request sent after discovery finished. Ignoring."; 294 return; 295 } 296 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryRequest(this)); 297 // If we need to send additional requests, schedule a timer to do so. 298 if (num_requests_sent_ < max_requests_ && num_requests_sent_ == 1) { 299 request_timer_.Start(FROM_HERE, 300 request_interval_, 301 this, 302 &DialServiceImpl::SendOneRequest); 303 } 304 } 305 306 bool DialServiceImpl::ReadSocket() { 307 DCHECK(thread_checker_.CalledOnValidThread()); 308 if (!socket_.get()) { 309 DLOG(WARNING) << "Socket not connected."; 310 return false; 311 } 312 313 if (is_reading_) { 314 DVLOG(1) << "Already reading."; 315 return false; 316 } 317 318 int result = net::OK; 319 bool result_ok = true; 320 do { 321 is_reading_ = true; 322 result = socket_->RecvFrom( 323 recv_buffer_.get(), 324 kDialRecvBufferSize, &recv_address_, 325 base::Bind(&DialServiceImpl::OnSocketRead, AsWeakPtr())); 326 result_ok = CheckResult("RecvFrom", result); 327 if (result != net::ERR_IO_PENDING) 328 is_reading_ = false; 329 if (result_ok && result > 0) { 330 // Synchronous read. 331 HandleResponse(result); 332 } 333 } while (result_ok && result != net::OK && result != net::ERR_IO_PENDING); 334 return result_ok; 335 } 336 337 void DialServiceImpl::OnSocketRead(int result) { 338 DCHECK(thread_checker_.CalledOnValidThread()); 339 is_reading_ = false; 340 if (!CheckResult("OnSocketRead", result)) 341 return; 342 if (result > 0) 343 HandleResponse(result); 344 345 // Await next response. 346 ReadSocket(); 347 } 348 349 void DialServiceImpl::HandleResponse(int bytes_read) { 350 DCHECK(thread_checker_.CalledOnValidThread()); 351 DCHECK_GT(bytes_read, 0); 352 if (bytes_read > kDialRecvBufferSize) { 353 DLOG(ERROR) << bytes_read << " > " << kDialRecvBufferSize << "!?"; 354 return; 355 } 356 DVLOG(1) << "Read " << bytes_read << " bytes from " 357 << recv_address_.ToString(); 358 359 // If discovery is inactive, no reason to handle response. 360 if (!discovery_active_) { 361 DVLOG(1) << "Got response after discovery finished. Ignoring."; 362 return; 363 } 364 365 std::string response(recv_buffer_->data(), bytes_read); 366 Time response_time = Time::Now(); 367 368 // Attempt to parse response, notify observers if successful. 369 DialDeviceData parsed_device; 370 if (ParseResponse(response, response_time, &parsed_device)) 371 FOR_EACH_OBSERVER(Observer, observer_list_, 372 OnDeviceDiscovered(this, parsed_device)); 373 } 374 375 // static 376 bool DialServiceImpl::ParseResponse(const std::string& response, 377 const base::Time& response_time, 378 DialDeviceData* device) { 379 int headers_end = HttpUtil::LocateEndOfHeaders(response.c_str(), 380 response.size()); 381 if (headers_end < 1) { 382 DVLOG(1) << "Headers invalid or empty, ignoring: " << response; 383 return false; 384 } 385 std::string raw_headers = 386 HttpUtil::AssembleRawHeaders(response.c_str(), headers_end); 387 DVLOG(1) << "raw_headers: " << raw_headers << "\n"; 388 scoped_refptr<HttpResponseHeaders> headers = 389 new HttpResponseHeaders(raw_headers); 390 391 std::string device_url_str; 392 if (!GetHeader(headers.get(), kSsdpLocationHeader, &device_url_str) || 393 device_url_str.empty()) { 394 DVLOG(1) << "No LOCATION header found."; 395 return false; 396 } 397 398 GURL device_url(device_url_str); 399 if (!DialDeviceData::IsDeviceDescriptionUrl(device_url)) { 400 DVLOG(1) << "URL " << device_url_str << " not valid."; 401 return false; 402 } 403 404 std::string device_id; 405 if (!GetHeader(headers.get(), kSsdpUsnHeader, &device_id) || 406 device_id.empty()) { 407 DVLOG(1) << "No USN header found."; 408 return false; 409 } 410 411 device->set_device_id(device_id); 412 device->set_device_description_url(device_url); 413 device->set_response_time(response_time); 414 415 // TODO(mfoltz): Parse the max-age value from the cache control header. 416 // http://crbug.com/165289 417 std::string cache_control; 418 GetHeader(headers.get(), kSsdpCacheControlHeader, &cache_control); 419 420 std::string config_id; 421 int config_id_int; 422 if (GetHeader(headers.get(), kSsdpConfigIdHeader, &config_id) && 423 base::StringToInt(config_id, &config_id_int)) { 424 device->set_config_id(config_id_int); 425 } else { 426 DVLOG(1) << "Malformed or missing " << kSsdpConfigIdHeader << ": " 427 << config_id; 428 } 429 430 return true; 431 } 432 433 void DialServiceImpl::SendNetworkList(const NetworkInterfaceList& networks) { 434 DCHECK(thread_checker_.CalledOnValidThread()); 435 IPAddressNumber bind_ip_address; 436 // Returns the first IPv4 address found. If there is a need for discovery 437 // across multiple networks, we could manage multiple sockets. 438 439 // TODO(mfoltz): Support IPV6 multicast. http://crbug.com/165286 440 for (NetworkInterfaceList::const_iterator iter = networks.begin(); 441 iter != networks.end(); ++iter) { 442 DVLOG(1) << "Found " << iter->name << ", " 443 << net::IPAddressToString(iter->address); 444 if (iter->address.size() == net::kIPv4AddressSize) { 445 bind_ip_address = (*iter).address; 446 break; 447 } 448 } 449 450 BindSocketAndSendRequest(bind_ip_address); 451 } 452 453 void DialServiceImpl::FinishDiscovery() { 454 DCHECK(thread_checker_.CalledOnValidThread()); 455 DCHECK(discovery_active_); 456 DVLOG(1) << "Discovery finished."; 457 CloseSocket(); 458 finish_timer_.Stop(); 459 request_timer_.Stop(); 460 discovery_active_ = false; 461 num_requests_sent_ = 0; 462 FOR_EACH_OBSERVER(Observer, observer_list_, OnDiscoveryFinished(this)); 463 } 464 465 void DialServiceImpl::CloseSocket() { 466 DCHECK(thread_checker_.CalledOnValidThread()); 467 is_reading_ = false; 468 is_writing_ = false; 469 socket_.reset(); 470 } 471 472 bool DialServiceImpl::CheckResult(const char* operation, int result) { 473 DCHECK(thread_checker_.CalledOnValidThread()); 474 DVLOG(1) << "Operation " << operation << " result " << result; 475 if (result < net::OK && result != net::ERR_IO_PENDING) { 476 CloseSocket(); 477 std::string error_str(net::ErrorToString(result)); 478 DVLOG(0) << "dial socket error: " << error_str; 479 // TODO(justinlin): More granular socket errors. 480 FOR_EACH_OBSERVER( 481 Observer, observer_list_, OnError(this, DIAL_SERVICE_SOCKET_ERROR)); 482 return false; 483 } 484 return true; 485 } 486 487 } // namespace extensions 488