Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / local_discovery / privet_traffic_detector.cc
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/local_discovery/privet_traffic_detector.h"
6
7 #include "base/metrics/histogram.h"
8 #include "base/sys_byteorder.h"
9 #include "net/base/dns_util.h"
10 #include "net/base/net_errors.h"
11 #include "net/base/net_log.h"
12 #include "net/dns/dns_protocol.h"
13 #include "net/dns/dns_response.h"
14 #include "net/dns/mdns_client.h"
15 #include "net/udp/datagram_server_socket.h"
16 #include "net/udp/udp_server_socket.h"
17
18 namespace {
19
20 const int kMaxRestartAttempts = 10;
21 const char kPrivetDeviceTypeDnsString[] = "\x07_privet";
22
23 void GetNetworkListOnFileThread(
24     const base::Callback<void(const net::NetworkInterfaceList&)> callback) {
25   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
26   net::NetworkInterfaceList networks;
27   if (!GetNetworkList(&networks, net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES))
28     return;
29
30   net::NetworkInterfaceList ip4_networks;
31   for (size_t i = 0; i < networks.size(); ++i) {
32     net::AddressFamily address_family =
33         net::GetAddressFamily(networks[i].address);
34     if (address_family == net::ADDRESS_FAMILY_IPV4 &&
35         networks[i].network_prefix >= 24) {
36       ip4_networks.push_back(networks[i]);
37     }
38   }
39
40   net::IPAddressNumber localhost_prefix(4, 0);
41   localhost_prefix[0] = 127;
42   ip4_networks.push_back(
43       net::NetworkInterface("lo",
44                             "lo",
45                             0,
46                             net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
47                             localhost_prefix,
48                             8,
49                             net::IP_ADDRESS_ATTRIBUTE_NONE));
50   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
51                                    base::Bind(callback, ip4_networks));
52 }
53
54 }  // namespace
55
56 namespace local_discovery {
57
58 PrivetTrafficDetector::PrivetTrafficDetector(
59     net::AddressFamily address_family,
60     const base::Closure& on_traffic_detected)
61     : on_traffic_detected_(on_traffic_detected),
62       callback_runner_(base::MessageLoop::current()->message_loop_proxy()),
63       address_family_(address_family),
64       io_buffer_(
65           new net::IOBufferWithSize(net::dns_protocol::kMaxMulticastSize)),
66       restart_attempts_(kMaxRestartAttempts),
67       weak_ptr_factory_(this) {
68 }
69
70 void PrivetTrafficDetector::Start() {
71   content::BrowserThread::PostTask(
72       content::BrowserThread::IO,
73       FROM_HERE,
74       base::Bind(&PrivetTrafficDetector::StartOnIOThread,
75                  weak_ptr_factory_.GetWeakPtr()));
76 }
77
78 PrivetTrafficDetector::~PrivetTrafficDetector() {
79   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
80   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
81 }
82
83 void PrivetTrafficDetector::StartOnIOThread() {
84   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
85   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
86   ScheduleRestart();
87 }
88
89 void PrivetTrafficDetector::OnNetworkChanged(
90     net::NetworkChangeNotifier::ConnectionType type) {
91   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
92   restart_attempts_ = kMaxRestartAttempts;
93   if (type != net::NetworkChangeNotifier::CONNECTION_NONE)
94     ScheduleRestart();
95 }
96
97 void PrivetTrafficDetector::ScheduleRestart() {
98   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
99   socket_.reset();
100   weak_ptr_factory_.InvalidateWeakPtrs();
101   content::BrowserThread::PostDelayedTask(
102       content::BrowserThread::FILE,
103       FROM_HERE,
104       base::Bind(&GetNetworkListOnFileThread,
105                  base::Bind(&PrivetTrafficDetector::Restart,
106                             weak_ptr_factory_.GetWeakPtr())),
107       base::TimeDelta::FromSeconds(3));
108 }
109
110 void PrivetTrafficDetector::Restart(const net::NetworkInterfaceList& networks) {
111   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
112   networks_ = networks;
113   if (Bind() < net::OK || DoLoop(0) < net::OK) {
114     if ((restart_attempts_--) > 0)
115       ScheduleRestart();
116   } else {
117     // Reset on success.
118     restart_attempts_ = kMaxRestartAttempts;
119   }
120 }
121
122 int PrivetTrafficDetector::Bind() {
123   if (!start_time_.is_null()) {
124     base::TimeDelta time_delta = base::Time::Now() - start_time_;
125     UMA_HISTOGRAM_LONG_TIMES("LocalDiscovery.DetectorRestartTime", time_delta);
126   }
127   start_time_ = base::Time::Now();
128   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
129   socket_.reset(new net::UDPServerSocket(NULL, net::NetLog::Source()));
130   net::IPEndPoint multicast_addr = net::GetMDnsIPEndPoint(address_family_);
131   net::IPAddressNumber address_any(multicast_addr.address().size());
132   net::IPEndPoint bind_endpoint(address_any, multicast_addr.port());
133   socket_->AllowAddressReuse();
134   int rv = socket_->Listen(bind_endpoint);
135   if (rv < net::OK)
136     return rv;
137   socket_->SetMulticastLoopbackMode(false);
138   return socket_->JoinGroup(multicast_addr.address());
139 }
140
141 bool PrivetTrafficDetector::IsSourceAcceptable() const {
142   for (size_t i = 0; i < networks_.size(); ++i) {
143     if (net::IPNumberMatchesPrefix(recv_addr_.address(), networks_[i].address,
144                                    networks_[i].network_prefix)) {
145       return true;
146     }
147   }
148   return false;
149 }
150
151 bool PrivetTrafficDetector::IsPrivetPacket(int rv) const {
152   if (rv <= static_cast<int>(sizeof(net::dns_protocol::Header)) ||
153       !IsSourceAcceptable()) {
154     return false;
155   }
156
157   const char* buffer_begin = io_buffer_->data();
158   const char* buffer_end = buffer_begin + rv;
159   const net::dns_protocol::Header* header =
160       reinterpret_cast<const net::dns_protocol::Header*>(buffer_begin);
161   // Check if response packet.
162   if (!(header->flags & base::HostToNet16(net::dns_protocol::kFlagResponse)))
163     return false;
164   const char* substring_begin = kPrivetDeviceTypeDnsString;
165   const char* substring_end = substring_begin +
166                               arraysize(kPrivetDeviceTypeDnsString) - 1;
167   // Check for expected substring, any Privet device must include this.
168   return std::search(buffer_begin, buffer_end, substring_begin,
169                      substring_end) != buffer_end;
170 }
171
172 int PrivetTrafficDetector::DoLoop(int rv) {
173   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
174   do {
175     if (IsPrivetPacket(rv)) {
176       socket_.reset();
177       callback_runner_->PostTask(FROM_HERE, on_traffic_detected_);
178       base::TimeDelta time_delta = base::Time::Now() - start_time_;
179       UMA_HISTOGRAM_LONG_TIMES("LocalDiscovery.DetectorTriggerTime",
180                                time_delta);
181       return net::OK;
182     }
183
184     rv = socket_->RecvFrom(
185         io_buffer_.get(),
186         io_buffer_->size(),
187         &recv_addr_,
188         base::Bind(base::IgnoreResult(&PrivetTrafficDetector::DoLoop),
189                    base::Unretained(this)));
190   } while (rv > 0);
191
192   if (rv != net::ERR_IO_PENDING)
193     return rv;
194
195   return net::OK;
196 }
197
198 }  // namespace local_discovery