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"; 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, 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 56namespace local_discovery { 57 58PrivetTrafficDetector::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 70void 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 78PrivetTrafficDetector::~PrivetTrafficDetector() { 79 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 80 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); 81} 82 83void PrivetTrafficDetector::StartOnIOThread() { 84 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 85 net::NetworkChangeNotifier::AddNetworkChangeObserver(this); 86 ScheduleRestart(); 87} 88 89void 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 97void 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 110void 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 122int 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 141bool 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 151bool 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 172int 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 199