LookupServer: Cache DNS answers for TTL seconds

We now keep DNS answers around in a cache for TTL seconds after getting
them the first time. The cache is capped at 256 responses for now.

Suggested by @zecke in #10.
This commit is contained in:
Andreas Kling 2020-01-26 15:42:03 +01:00
parent 90a5907b44
commit 5e47508672
Notes: sideshowbarker 2024-07-19 09:48:11 +09:00
5 changed files with 71 additions and 17 deletions

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "DNSAnswer.h"
#include <time.h>
DNSAnswer::DNSAnswer(const String& name, u16 type, u16 class_code, u32 ttl, const String& record_data)
: m_name(name)
, m_type(type)
, m_class_code(class_code)
, m_ttl(ttl)
, m_record_data(record_data)
{
auto now = time(nullptr);
m_expiration_time = now + m_ttl;
if (m_expiration_time < now)
m_expiration_time = 0;
}
bool DNSAnswer::has_expired() const
{
return time(nullptr) >= m_expiration_time;
}

View file

@ -31,14 +31,7 @@
class DNSAnswer {
public:
DNSAnswer(const String& name, u16 type, u16 class_code, u32 ttl, const String& record_data)
: m_name(name)
, m_type(type)
, m_class_code(class_code)
, m_ttl(ttl)
, m_record_data(record_data)
{
}
DNSAnswer(const String& name, u16 type, u16 class_code, u32 ttl, const String& record_data);
const String& name() const { return m_name; }
u16 type() const { return m_type; }
@ -46,10 +39,13 @@ public:
u32 ttl() const { return m_ttl; }
const String& record_data() const { return m_record_data; }
bool has_expired() const;
private:
String m_name;
u16 m_type { 0 };
u16 m_class_code { 0 };
u32 m_ttl { 0 };
time_t m_expiration_time { 0 };
String m_record_data;
};

View file

@ -157,8 +157,16 @@ Vector<String> LookupServer::lookup(const String& hostname, bool& did_timeout, u
{
if (auto it = m_lookup_cache.find(hostname); it != m_lookup_cache.end()) {
auto& cached_lookup = it->value;
if (cached_lookup.record_type == record_type && cached_lookup.timestamp < (time(nullptr) + 60)) {
return it->value.responses;
if (cached_lookup.question.record_type() == record_type) {
Vector<String> responses;
for (auto& cached_answer : cached_lookup.answers) {
dbg() << "Cache hit: " << hostname << " -> " << cached_answer.record_data() << ", expired: " << cached_answer.has_expired();
if (!cached_answer.has_expired()) {
responses.append(cached_answer.record_data());
}
}
if (!responses.is_empty())
return responses;
}
m_lookup_cache.remove(it);
}
@ -232,11 +240,13 @@ Vector<String> LookupServer::lookup(const String& hostname, bool& did_timeout, u
return {};
}
Vector<String> addresses;
Vector<String> responses;
for (auto& answer : response.answers()) {
addresses.append(answer.record_data());
responses.append(answer.record_data());
}
m_lookup_cache.set(hostname, { time(nullptr), record_type, addresses });
return addresses;
if (m_lookup_cache.size() >= 256)
m_lookup_cache.remove(m_lookup_cache.begin());
m_lookup_cache.set(hostname, { request.questions()[0], response.answers() });
return responses;
}

View file

@ -27,11 +27,13 @@
#pragma once
#include "DNSRequest.h"
#include "DNSResponse.h"
#include <AK/HashMap.h>
#include <LibCore/CObject.h>
class CLocalSocket;
class CLocalServer;
class DNSAnswer;
class LookupServer final : public CObject {
C_OBJECT(LookupServer)
@ -45,9 +47,8 @@ private:
Vector<String> lookup(const String& hostname, bool& did_timeout, unsigned short record_type, ShouldRandomizeCase = ShouldRandomizeCase::Yes);
struct CachedLookup {
time_t timestamp { 0 };
unsigned short record_type { 0 };
Vector<String> responses;
DNSQuestion question;
Vector<DNSAnswer> answers;
};
RefPtr<CLocalServer> m_local_server;

View file

@ -2,6 +2,7 @@ OBJS = \
LookupServer.o \
DNSRequest.o \
DNSResponse.o \
DNSAnswer.o \
main.o
PROGRAM = LookupServer