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