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