LookupServer: Support multiple nameservers

The configuration key [DNS] Nameserver has been renamed to Nameservers
and accepts a comma-separated list of nameserver addresses, which will
be queried in the given order until a response has been received.

The new default value is still Cloudflare's 1.1.1.1 as well as their
secondary DNS server 1.0.0.1.
This commit is contained in:
Linus Groh 2020-10-25 15:30:44 +00:00 committed by Andreas Kling
parent 2440112d53
commit 7eee39b850
3 changed files with 30 additions and 19 deletions

View file

@ -1,2 +1,2 @@
[DNS] [DNS]
Nameserver=1.1.1.1 Nameservers=1.1.1.1,1.0.0.1

View file

@ -43,7 +43,7 @@ LookupServer::LookupServer()
{ {
auto config = Core::ConfigFile::get_for_system("LookupServer"); auto config = Core::ConfigFile::get_for_system("LookupServer");
dbgln("Using network config file at {}", config->file_name()); dbgln("Using network config file at {}", config->file_name());
m_nameserver = config->read_entry("DNS", "Nameserver", "1.1.1.1"); m_nameservers = config->read_entry("DNS", "Nameservers", "1.1.1.1,1.0.0.1").split(',');
load_etc_hosts(); load_etc_hosts();
@ -113,25 +113,36 @@ void LookupServer::service_client(RefPtr<Core::LocalSocket> socket)
return; return;
} }
auto hostname = String((const char*)client_buffer + 1, nrecv - 1, Chomp); auto hostname = String((const char*)client_buffer + 1, nrecv - 1, Chomp);
dbgln("Got request for '{}' (using IP {})", hostname, m_nameserver); dbgln("Got request for '{}'", hostname);
Vector<String> responses; Vector<String> responses;
if (auto known_host = m_etc_hosts.get(hostname); known_host.has_value()) { if (auto known_host = m_etc_hosts.get(hostname); known_host.has_value()) {
responses.append(known_host.value()); responses.append(known_host.value());
} else if (!hostname.is_empty()) { } else if (!hostname.is_empty()) {
bool did_get_response = false; for (auto& nameserver : m_nameservers) {
int retries = 3; dbgln("Doing lookup using nameserver '{}'", nameserver);
do { bool did_get_response = false;
if (lookup_type == 'L') int retries = 3;
responses = lookup(hostname, did_get_response, T_A); do {
else if (lookup_type == 'R') if (lookup_type == 'L')
responses = lookup(hostname, did_get_response, T_PTR); responses = lookup(hostname, nameserver, did_get_response, T_A);
if (did_get_response) else if (lookup_type == 'R')
responses = lookup(hostname, nameserver, did_get_response, T_PTR);
if (did_get_response)
break;
} while (--retries);
if (!responses.is_empty()) {
break; break;
} while (--retries); } else {
if (!did_get_response) { if (!did_get_response)
fprintf(stderr, "LookupServer: Never got a response but out of retries :(\n"); dbgln("Never got a response from '{}', trying next nameserver", nameserver);
else
dbgln("Received response from '{}' but no result(s), trying next nameserver", nameserver);
}
}
if (responses.is_empty()) {
fprintf(stderr, "LookupServer: Tried all nameservers but never got a response :(\n");
return; return;
} }
} }
@ -152,7 +163,7 @@ void LookupServer::service_client(RefPtr<Core::LocalSocket> socket)
} }
} }
Vector<String> LookupServer::lookup(const String& hostname, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase should_randomize_case) Vector<String> LookupServer::lookup(const String& hostname, const String& nameserver, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase should_randomize_case)
{ {
if (auto it = m_lookup_cache.find(hostname); it != m_lookup_cache.end()) { if (auto it = m_lookup_cache.find(hostname); it != m_lookup_cache.end()) {
auto& cached_lookup = it->value; auto& cached_lookup = it->value;
@ -187,7 +198,7 @@ Vector<String> LookupServer::lookup(const String& hostname, bool& did_get_respon
return {}; return {};
} }
if (!udp_socket->connect(m_nameserver, 53)) if (!udp_socket->connect(nameserver, 53))
return {}; return {};
if (!udp_socket->write(buffer)) if (!udp_socket->write(buffer))
@ -214,7 +225,7 @@ Vector<String> LookupServer::lookup(const String& hostname, bool& did_get_respon
if (response.code() == DNSResponse::Code::REFUSED) { if (response.code() == DNSResponse::Code::REFUSED) {
if (should_randomize_case == ShouldRandomizeCase::Yes) { if (should_randomize_case == ShouldRandomizeCase::Yes) {
// Retry with 0x20 case randomization turned off. // Retry with 0x20 case randomization turned off.
return lookup(hostname, did_get_response, record_type, ShouldRandomizeCase::No); return lookup(hostname, nameserver, did_get_response, record_type, ShouldRandomizeCase::No);
} }
return {}; return {};
} }

View file

@ -42,7 +42,7 @@ public:
private: private:
void load_etc_hosts(); void load_etc_hosts();
void service_client(RefPtr<Core::LocalSocket>); void service_client(RefPtr<Core::LocalSocket>);
Vector<String> lookup(const String& hostname, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase = ShouldRandomizeCase::Yes); Vector<String> lookup(const String& hostname, const String& nameserver, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase = ShouldRandomizeCase::Yes);
struct CachedLookup { struct CachedLookup {
DNSQuestion question; DNSQuestion question;
@ -50,7 +50,7 @@ private:
}; };
RefPtr<Core::LocalServer> m_local_server; RefPtr<Core::LocalServer> m_local_server;
String m_nameserver; Vector<String> m_nameservers;
HashMap<String, String> m_etc_hosts; HashMap<String, String> m_etc_hosts;
HashMap<String, CachedLookup> m_lookup_cache; HashMap<String, CachedLookup> m_lookup_cache;
}; };