diff options
Diffstat (limited to 'chromium/net/dns/mdns_cache.cc')
-rw-r--r-- | chromium/net/dns/mdns_cache.cc | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/chromium/net/dns/mdns_cache.cc b/chromium/net/dns/mdns_cache.cc new file mode 100644 index 00000000000..010a34f45d4 --- /dev/null +++ b/chromium/net/dns/mdns_cache.cc @@ -0,0 +1,212 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/dns/mdns_cache.h" + +#include <algorithm> +#include <utility> + +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "net/dns/dns_protocol.h" +#include "net/dns/record_parsed.h" +#include "net/dns/record_rdata.h" + +// TODO(noamsml): Recursive CNAME closure (backwards and forwards). + +namespace net { + +// The effective TTL given to records with a nominal zero TTL. +// Allows time for hosts to send updated records, as detailed in RFC 6762 +// Section 10.1. +static const unsigned kZeroTTLSeconds = 1; + +MDnsCache::Key::Key(unsigned type, const std::string& name, + const std::string& optional) + : type_(type), name_(name), optional_(optional) { +} + +MDnsCache::Key::Key( + const MDnsCache::Key& other) + : type_(other.type_), name_(other.name_), optional_(other.optional_) { +} + + +MDnsCache::Key& MDnsCache::Key::operator=( + const MDnsCache::Key& other) { + type_ = other.type_; + name_ = other.name_; + optional_ = other.optional_; + return *this; +} + +MDnsCache::Key::~Key() { +} + +bool MDnsCache::Key::operator<(const MDnsCache::Key& key) const { + if (name_ != key.name_) + return name_ < key.name_; + + if (type_ != key.type_) + return type_ < key.type_; + + if (optional_ != key.optional_) + return optional_ < key.optional_; + return false; // keys are equal +} + +bool MDnsCache::Key::operator==(const MDnsCache::Key& key) const { + return type_ == key.type_ && name_ == key.name_ && optional_ == key.optional_; +} + +// static +MDnsCache::Key MDnsCache::Key::CreateFor(const RecordParsed* record) { + return Key(record->type(), + record->name(), + GetOptionalFieldForRecord(record)); +} + + +MDnsCache::MDnsCache() { +} + +MDnsCache::~MDnsCache() { + Clear(); +} + +void MDnsCache::Clear() { + next_expiration_ = base::Time(); + STLDeleteValues(&mdns_cache_); +} + +const RecordParsed* MDnsCache::LookupKey(const Key& key) { + RecordMap::iterator found = mdns_cache_.find(key); + if (found != mdns_cache_.end()) { + return found->second; + } + return NULL; +} + +MDnsCache::UpdateType MDnsCache::UpdateDnsRecord( + scoped_ptr<const RecordParsed> record) { + Key cache_key = Key::CreateFor(record.get()); + + // Ignore "goodbye" packets for records not in cache. + if (record->ttl() == 0 && mdns_cache_.find(cache_key) == mdns_cache_.end()) + return NoChange; + + base::Time new_expiration = GetEffectiveExpiration(record.get()); + if (next_expiration_ != base::Time()) + new_expiration = std::min(new_expiration, next_expiration_); + + std::pair<RecordMap::iterator, bool> insert_result = + mdns_cache_.insert(std::make_pair(cache_key, (const RecordParsed*)NULL)); + UpdateType type = NoChange; + if (insert_result.second) { + type = RecordAdded; + } else { + const RecordParsed* other_record = insert_result.first->second; + + if (record->ttl() != 0 && !record->IsEqual(other_record, true)) { + type = RecordChanged; + } + delete other_record; + } + + insert_result.first->second = record.release(); + next_expiration_ = new_expiration; + return type; +} + +void MDnsCache::CleanupRecords( + base::Time now, + const RecordRemovedCallback& record_removed_callback) { + base::Time next_expiration; + + // We are guaranteed that |next_expiration_| will be at or before the next + // expiration. This allows clients to eagrely call CleanupRecords with + // impunity. + if (now < next_expiration_) return; + + for (RecordMap::iterator i = mdns_cache_.begin(); + i != mdns_cache_.end(); ) { + base::Time expiration = GetEffectiveExpiration(i->second); + if (now >= expiration) { + record_removed_callback.Run(i->second); + delete i->second; + mdns_cache_.erase(i++); + } else { + if (next_expiration == base::Time() || expiration < next_expiration) { + next_expiration = expiration; + } + ++i; + } + } + + next_expiration_ = next_expiration; +} + +void MDnsCache::FindDnsRecords(unsigned type, + const std::string& name, + std::vector<const RecordParsed*>* results, + base::Time now) const { + DCHECK(results); + results->clear(); + + RecordMap::const_iterator i = mdns_cache_.lower_bound(Key(type, name, "")); + for (; i != mdns_cache_.end(); ++i) { + if (i->first.name() != name || + (type != 0 && i->first.type() != type)) { + break; + } + + const RecordParsed* record = i->second; + + // Records are deleted only upon request. + if (now >= GetEffectiveExpiration(record)) continue; + + results->push_back(record); + } +} + +scoped_ptr<const RecordParsed> MDnsCache::RemoveRecord( + const RecordParsed* record) { + Key key = Key::CreateFor(record); + RecordMap::iterator found = mdns_cache_.find(key); + + if (found != mdns_cache_.end() && found->second == record) { + mdns_cache_.erase(key); + return scoped_ptr<const RecordParsed>(record); + } + + return scoped_ptr<const RecordParsed>(); +} + +// static +std::string MDnsCache::GetOptionalFieldForRecord( + const RecordParsed* record) { + switch (record->type()) { + case PtrRecordRdata::kType: { + const PtrRecordRdata* rdata = record->rdata<PtrRecordRdata>(); + return rdata->ptrdomain(); + } + default: // Most records are considered unique for our purposes + return ""; + } +} + +// static +base::Time MDnsCache::GetEffectiveExpiration(const RecordParsed* record) { + base::TimeDelta ttl; + + if (record->ttl()) { + ttl = base::TimeDelta::FromSeconds(record->ttl()); + } else { + ttl = base::TimeDelta::FromSeconds(kZeroTTLSeconds); + } + + return record->time_created() + ttl; +} + +} // namespace net |