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