Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / local_discovery / privet_traffic_detector.cc
blob62f8cd818869e8a45eb3576bb995a776dda8f7e0
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.
5 #include "chrome/browser/local_discovery/privet_traffic_detector.h"
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"
18 namespace {
20 const int kMaxRestartAttempts = 10;
21 const char kPrivetDeviceTypeDnsString[] = "\x07_privet";
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;
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].prefix_length >= 24) {
36 ip4_networks.push_back(networks[i]);
40 net::IPAddressNumber localhost_prefix(4, 0);
41 localhost_prefix[0] = 127;
42 ip4_networks.push_back(
43 net::NetworkInterface("lo",
44 "lo",
46 net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
47 localhost_prefix,
49 net::IP_ADDRESS_ATTRIBUTE_NONE));
50 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
51 base::Bind(callback, ip4_networks));
54 } // namespace
56 namespace local_discovery {
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) {
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()));
78 PrivetTrafficDetector::~PrivetTrafficDetector() {
79 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
80 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
83 void PrivetTrafficDetector::StartOnIOThread() {
84 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
85 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
86 ScheduleRestart();
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();
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));
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;
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);
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());
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].prefix_length)) {
145 return true;
148 return false;
151 bool PrivetTrafficDetector::IsPrivetPacket(int rv) const {
152 if (rv <= static_cast<int>(sizeof(net::dns_protocol::Header)) ||
153 !IsSourceAcceptable()) {
154 return false;
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;
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;
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);
192 if (rv != net::ERR_IO_PENDING)
193 return rv;
195 return net::OK;
198 } // namespace local_discovery