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