1 // Copyright (c) 2012 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 "net/dns/dns_config_service.h" 6 7 #include "base/logging.h" 8 #include "base/metrics/histogram.h" 9 #include "base/values.h" 10 #include "net/base/ip_endpoint.h" 11 12 namespace net { 13 14 // Default values are taken from glibc resolv.h except timeout which is set to 15 // |kDnsTimeoutSeconds|. 16 DnsConfig::DnsConfig() 17 : append_to_multi_label_name(true), 18 randomize_ports(false), 19 ndots(1), 20 timeout(base::TimeDelta::FromSeconds(kDnsTimeoutSeconds)), 21 attempts(2), 22 rotate(false), 23 edns0(false) {} 24 25 DnsConfig::~DnsConfig() {} 26 27 bool DnsConfig::Equals(const DnsConfig& d) const { 28 return EqualsIgnoreHosts(d) && (hosts == d.hosts); 29 } 30 31 bool DnsConfig::EqualsIgnoreHosts(const DnsConfig& d) const { 32 return (nameservers == d.nameservers) && 33 (search == d.search) && 34 (append_to_multi_label_name == d.append_to_multi_label_name) && 35 (ndots == d.ndots) && 36 (timeout == d.timeout) && 37 (attempts == d.attempts) && 38 (rotate == d.rotate) && 39 (edns0 == d.edns0); 40 } 41 42 void DnsConfig::CopyIgnoreHosts(const DnsConfig& d) { 43 nameservers = d.nameservers; 44 search = d.search; 45 append_to_multi_label_name = d.append_to_multi_label_name; 46 ndots = d.ndots; 47 timeout = d.timeout; 48 attempts = d.attempts; 49 rotate = d.rotate; 50 edns0 = d.edns0; 51 } 52 53 base::Value* DnsConfig::ToValue() const { 54 base::DictionaryValue* dict = new base::DictionaryValue(); 55 56 base::ListValue* list = new base::ListValue(); 57 for (size_t i = 0; i < nameservers.size(); ++i) 58 list->Append(new base::StringValue(nameservers[i].ToString())); 59 dict->Set("nameservers", list); 60 61 list = new base::ListValue(); 62 for (size_t i = 0; i < search.size(); ++i) 63 list->Append(new base::StringValue(search[i])); 64 dict->Set("search", list); 65 66 dict->SetBoolean("append_to_multi_label_name", append_to_multi_label_name); 67 dict->SetInteger("ndots", ndots); 68 dict->SetDouble("timeout", timeout.InSecondsF()); 69 dict->SetInteger("attempts", attempts); 70 dict->SetBoolean("rotate", rotate); 71 dict->SetBoolean("edns0", edns0); 72 dict->SetInteger("num_hosts", hosts.size()); 73 74 return dict; 75 } 76 77 78 DnsConfigService::DnsConfigService() 79 : watch_failed_(false), 80 have_config_(false), 81 have_hosts_(false), 82 need_update_(false), 83 last_sent_empty_(true) {} 84 85 DnsConfigService::~DnsConfigService() { 86 } 87 88 void DnsConfigService::ReadConfig(const CallbackType& callback) { 89 DCHECK(CalledOnValidThread()); 90 DCHECK(!callback.is_null()); 91 DCHECK(callback_.is_null()); 92 callback_ = callback; 93 ReadNow(); 94 } 95 96 void DnsConfigService::WatchConfig(const CallbackType& callback) { 97 DCHECK(CalledOnValidThread()); 98 DCHECK(!callback.is_null()); 99 DCHECK(callback_.is_null()); 100 callback_ = callback; 101 watch_failed_ = !StartWatching(); 102 ReadNow(); 103 } 104 105 void DnsConfigService::InvalidateConfig() { 106 DCHECK(CalledOnValidThread()); 107 base::TimeTicks now = base::TimeTicks::Now(); 108 if (!last_invalidate_config_time_.is_null()) { 109 UMA_HISTOGRAM_LONG_TIMES("AsyncDNS.ConfigNotifyInterval", 110 now - last_invalidate_config_time_); 111 } 112 last_invalidate_config_time_ = now; 113 if (!have_config_) 114 return; 115 have_config_ = false; 116 StartTimer(); 117 } 118 119 void DnsConfigService::InvalidateHosts() { 120 DCHECK(CalledOnValidThread()); 121 base::TimeTicks now = base::TimeTicks::Now(); 122 if (!last_invalidate_hosts_time_.is_null()) { 123 UMA_HISTOGRAM_LONG_TIMES("AsyncDNS.HostsNotifyInterval", 124 now - last_invalidate_hosts_time_); 125 } 126 last_invalidate_hosts_time_ = now; 127 if (!have_hosts_) 128 return; 129 have_hosts_ = false; 130 StartTimer(); 131 } 132 133 void DnsConfigService::OnConfigRead(const DnsConfig& config) { 134 DCHECK(CalledOnValidThread()); 135 DCHECK(config.IsValid()); 136 137 bool changed = false; 138 if (!config.EqualsIgnoreHosts(dns_config_)) { 139 dns_config_.CopyIgnoreHosts(config); 140 need_update_ = true; 141 changed = true; 142 } 143 if (!changed && !last_sent_empty_time_.is_null()) { 144 UMA_HISTOGRAM_LONG_TIMES("AsyncDNS.UnchangedConfigInterval", 145 base::TimeTicks::Now() - last_sent_empty_time_); 146 } 147 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigChange", changed); 148 149 have_config_ = true; 150 if (have_hosts_ || watch_failed_) 151 OnCompleteConfig(); 152 } 153 154 void DnsConfigService::OnHostsRead(const DnsHosts& hosts) { 155 DCHECK(CalledOnValidThread()); 156 157 bool changed = false; 158 if (hosts != dns_config_.hosts) { 159 dns_config_.hosts = hosts; 160 need_update_ = true; 161 changed = true; 162 } 163 if (!changed && !last_sent_empty_time_.is_null()) { 164 UMA_HISTOGRAM_LONG_TIMES("AsyncDNS.UnchangedHostsInterval", 165 base::TimeTicks::Now() - last_sent_empty_time_); 166 } 167 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostsChange", changed); 168 169 have_hosts_ = true; 170 if (have_config_ || watch_failed_) 171 OnCompleteConfig(); 172 } 173 174 void DnsConfigService::StartTimer() { 175 DCHECK(CalledOnValidThread()); 176 if (last_sent_empty_) { 177 DCHECK(!timer_.IsRunning()); 178 return; // No need to withdraw again. 179 } 180 timer_.Stop(); 181 182 // Give it a short timeout to come up with a valid config. Otherwise withdraw 183 // the config from the receiver. The goal is to avoid perceivable network 184 // outage (when using the wrong config) but at the same time avoid 185 // unnecessary Job aborts in HostResolverImpl. The signals come from multiple 186 // sources so it might receive multiple events during a config change. 187 188 // DHCP and user-induced changes are on the order of seconds, so 150ms should 189 // not add perceivable delay. On the other hand, config readers should finish 190 // within 150ms with the rare exception of I/O block or extra large HOSTS. 191 const base::TimeDelta kTimeout = base::TimeDelta::FromMilliseconds(150); 192 193 timer_.Start(FROM_HERE, 194 kTimeout, 195 this, 196 &DnsConfigService::OnTimeout); 197 } 198 199 void DnsConfigService::OnTimeout() { 200 DCHECK(CalledOnValidThread()); 201 DCHECK(!last_sent_empty_); 202 // Indicate that even if there is no change in On*Read, we will need to 203 // update the receiver when the config becomes complete. 204 need_update_ = true; 205 // Empty config is considered invalid. 206 last_sent_empty_ = true; 207 last_sent_empty_time_ = base::TimeTicks::Now(); 208 callback_.Run(DnsConfig()); 209 } 210 211 void DnsConfigService::OnCompleteConfig() { 212 timer_.Stop(); 213 if (!need_update_) 214 return; 215 need_update_ = false; 216 last_sent_empty_ = false; 217 if (watch_failed_) { 218 // If a watch failed, the config may not be accurate, so report empty. 219 callback_.Run(DnsConfig()); 220 } else { 221 callback_.Run(dns_config_); 222 } 223 } 224 225 } // namespace net 226 227