Skip to content

Commit

Permalink
LookupServer: Cache DNS answers for TTL seconds
Browse files Browse the repository at this point in the history
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 SerenityOS#10.
  • Loading branch information
awesomekling committed Jan 26, 2020
1 parent 90a5907 commit 5e47508
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 17 deletions.
46 changes: 46 additions & 0 deletions Servers/LookupServer/DNSAnswer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2020, Andreas Kling <[email protected]>
* 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;
}
12 changes: 4 additions & 8 deletions Servers/LookupServer/DNSAnswer.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,21 @@

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; }
u16 class_code() const { return m_class_code; }
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;
};
22 changes: 16 additions & 6 deletions Servers/LookupServer/LookupServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
7 changes: 4 additions & 3 deletions Servers/LookupServer/LookupServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -45,9 +47,8 @@ class LookupServer final : public CObject {
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;
Expand Down
1 change: 1 addition & 0 deletions Servers/LookupServer/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ OBJS = \
LookupServer.o \
DNSRequest.o \
DNSResponse.o \
DNSAnswer.o \
main.o

PROGRAM = LookupServer
Expand Down

0 comments on commit 5e47508

Please sign in to comment.