1 // Copyright (c) 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 "net/dns/mdns_cache.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "net/dns/dns_protocol.h"
13 #include "net/dns/record_parsed.h"
14 #include "net/dns/record_rdata.h"
16 // TODO(noamsml): Recursive CNAME closure (backwards and forwards).
20 // The effective TTL given to records with a nominal zero TTL.
21 // Allows time for hosts to send updated records, as detailed in RFC 6762
23 static const unsigned kZeroTTLSeconds
= 1;
25 MDnsCache::Key::Key(unsigned type
, const std::string
& name
,
26 const std::string
& optional
)
27 : type_(type
), name_(name
), optional_(optional
) {
31 const MDnsCache::Key
& other
)
32 : type_(other
.type_
), name_(other
.name_
), optional_(other
.optional_
) {
36 MDnsCache::Key
& MDnsCache::Key::operator=(
37 const MDnsCache::Key
& other
) {
40 optional_
= other
.optional_
;
44 MDnsCache::Key::~Key() {
47 bool MDnsCache::Key::operator<(const MDnsCache::Key
& key
) const {
48 if (name_
!= key
.name_
)
49 return name_
< key
.name_
;
51 if (type_
!= key
.type_
)
52 return type_
< key
.type_
;
54 if (optional_
!= key
.optional_
)
55 return optional_
< key
.optional_
;
56 return false; // keys are equal
59 bool MDnsCache::Key::operator==(const MDnsCache::Key
& key
) const {
60 return type_
== key
.type_
&& name_
== key
.name_
&& optional_
== key
.optional_
;
64 MDnsCache::Key
MDnsCache::Key::CreateFor(const RecordParsed
* record
) {
65 return Key(record
->type(),
67 GetOptionalFieldForRecord(record
));
71 MDnsCache::MDnsCache() {
74 MDnsCache::~MDnsCache() {
78 void MDnsCache::Clear() {
79 next_expiration_
= base::Time();
80 STLDeleteValues(&mdns_cache_
);
83 const RecordParsed
* MDnsCache::LookupKey(const Key
& key
) {
84 RecordMap::iterator found
= mdns_cache_
.find(key
);
85 if (found
!= mdns_cache_
.end()) {
91 MDnsCache::UpdateType
MDnsCache::UpdateDnsRecord(
92 scoped_ptr
<const RecordParsed
> record
) {
93 Key cache_key
= Key::CreateFor(record
.get());
95 // Ignore "goodbye" packets for records not in cache.
96 if (record
->ttl() == 0 && mdns_cache_
.find(cache_key
) == mdns_cache_
.end())
99 base::Time new_expiration
= GetEffectiveExpiration(record
.get());
100 if (next_expiration_
!= base::Time())
101 new_expiration
= std::min(new_expiration
, next_expiration_
);
103 std::pair
<RecordMap::iterator
, bool> insert_result
=
104 mdns_cache_
.insert(std::make_pair(cache_key
, (const RecordParsed
*)NULL
));
105 UpdateType type
= NoChange
;
106 if (insert_result
.second
) {
109 const RecordParsed
* other_record
= insert_result
.first
->second
;
111 if (record
->ttl() != 0 && !record
->IsEqual(other_record
, true)) {
112 type
= RecordChanged
;
117 insert_result
.first
->second
= record
.release();
118 next_expiration_
= new_expiration
;
122 void MDnsCache::CleanupRecords(
124 const RecordRemovedCallback
& record_removed_callback
) {
125 base::Time next_expiration
;
127 // We are guaranteed that |next_expiration_| will be at or before the next
128 // expiration. This allows clients to eagrely call CleanupRecords with
130 if (now
< next_expiration_
) return;
132 for (RecordMap::iterator i
= mdns_cache_
.begin();
133 i
!= mdns_cache_
.end(); ) {
134 base::Time expiration
= GetEffectiveExpiration(i
->second
);
135 if (now
>= expiration
) {
136 record_removed_callback
.Run(i
->second
);
138 mdns_cache_
.erase(i
++);
140 if (next_expiration
== base::Time() || expiration
< next_expiration
) {
141 next_expiration
= expiration
;
147 next_expiration_
= next_expiration
;
150 void MDnsCache::FindDnsRecords(unsigned type
,
151 const std::string
& name
,
152 std::vector
<const RecordParsed
*>* results
,
153 base::Time now
) const {
157 RecordMap::const_iterator i
= mdns_cache_
.lower_bound(Key(type
, name
, ""));
158 for (; i
!= mdns_cache_
.end(); ++i
) {
159 if (i
->first
.name() != name
||
160 (type
!= 0 && i
->first
.type() != type
)) {
164 const RecordParsed
* record
= i
->second
;
166 // Records are deleted only upon request.
167 if (now
>= GetEffectiveExpiration(record
)) continue;
169 results
->push_back(record
);
173 scoped_ptr
<const RecordParsed
> MDnsCache::RemoveRecord(
174 const RecordParsed
* record
) {
175 Key key
= Key::CreateFor(record
);
176 RecordMap::iterator found
= mdns_cache_
.find(key
);
178 if (found
!= mdns_cache_
.end() && found
->second
== record
) {
179 mdns_cache_
.erase(key
);
180 return scoped_ptr
<const RecordParsed
>(record
);
183 return scoped_ptr
<const RecordParsed
>();
187 std::string
MDnsCache::GetOptionalFieldForRecord(
188 const RecordParsed
* record
) {
189 switch (record
->type()) {
190 case PtrRecordRdata::kType
: {
191 const PtrRecordRdata
* rdata
= record
->rdata
<PtrRecordRdata
>();
192 return rdata
->ptrdomain();
194 default: // Most records are considered unique for our purposes
200 base::Time
MDnsCache::GetEffectiveExpiration(const RecordParsed
* record
) {
204 ttl
= base::TimeDelta::FromSeconds(record
->ttl());
206 ttl
= base::TimeDelta::FromSeconds(kZeroTTLSeconds
);
209 return record
->time_created() + ttl
;