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