1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 14:47:44 +00:00

LookupServer: Retry with 0x20 randomization turned off on EREFUSED

Apparently some authoritative servers don't handle 0x20 randomization
well and may send EREFUSED. Retry with randomization turned off then.

Reference: https://github.com/dns-violations/dns-violations/blob/master/2017/DVE-2017-0006.md

More work towards #10.
This commit is contained in:
Andreas Kling 2020-01-26 13:45:15 +01:00
parent 00be9b33b1
commit a9ec2225a5
6 changed files with 48 additions and 11 deletions

View file

@ -13,22 +13,24 @@ DNSRequest::DNSRequest()
{ {
} }
void DNSRequest::add_question(const String& name, u16 record_type) void DNSRequest::add_question(const String& name, u16 record_type, ShouldRandomizeCase should_randomize_case)
{ {
ASSERT(m_questions.size() <= UINT16_MAX); ASSERT(m_questions.size() <= UINT16_MAX);
if (name.is_empty()) if (name.is_empty())
return; return;
// Randomize the 0x20 bit in every ASCII character.
StringBuilder builder; StringBuilder builder;
for (size_t i = 0; i < name.length(); ++i) { for (size_t i = 0; i < name.length(); ++i) {
u8 ch = name[i]; u8 ch = name[i];
if (isalpha(ch)) { if (should_randomize_case == ShouldRandomizeCase::Yes) {
if (arc4random_uniform(2)) // Randomize the 0x20 bit in every ASCII character.
ch |= 0x20; if (isalpha(ch)) {
else if (arc4random_uniform(2))
ch &= ~0x20; ch |= 0x20;
else
ch &= ~0x20;
}
} }
builder.append(ch); builder.append(ch);
} }

View file

@ -12,11 +12,16 @@
#define T_PTR 12 #define T_PTR 12
#define T_MX 15 #define T_MX 15
enum class ShouldRandomizeCase {
No = 0,
Yes
};
class DNSRequest { class DNSRequest {
public: public:
DNSRequest(); DNSRequest();
void add_question(const String& name, u16 record_type); void add_question(const String& name, u16 record_type, ShouldRandomizeCase);
const Vector<DNSQuestion>& questions() const { return m_questions; } const Vector<DNSQuestion>& questions() const { return m_questions; }

View file

@ -44,6 +44,10 @@ Optional<DNSResponse> DNSResponse::from_raw_response(const u8* raw_data, size_t
DNSResponse response; DNSResponse response;
response.m_id = response_header.id(); response.m_id = response_header.id();
response.m_code = response_header.response_code();
if (response.code() != DNSResponse::Code::NOERROR)
return response;
size_t offset = sizeof(DNSPacket); size_t offset = sizeof(DNSPacket);

View file

@ -26,10 +26,26 @@ public:
return m_answers.size(); return m_answers.size();
} }
enum class Code : u8 {
NOERROR = 0,
FORMERR = 1,
SERVFAIL = 2,
NXDOMAIN = 3,
NOTIMP = 4,
REFUSED = 5,
YXDOMAIN = 6,
XRRSET = 7,
NOTAUTH = 8,
NOTZONE = 9,
};
Code code() const { return (Code)m_code; }
private: private:
DNSResponse() {} DNSResponse() {}
u16 m_id { 0 }; u16 m_id { 0 };
u8 m_code { 0 };
Vector<DNSQuestion> m_questions; Vector<DNSQuestion> m_questions;
Vector<DNSAnswer> m_answers; Vector<DNSAnswer> m_answers;
}; };

View file

@ -161,7 +161,7 @@ void LookupServer::service_client(RefPtr<CLocalSocket> socket)
} }
} }
Vector<String> LookupServer::lookup(const String& hostname, bool& did_timeout, unsigned short record_type) Vector<String> LookupServer::lookup(const String& hostname, bool& did_timeout, 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;
@ -172,7 +172,7 @@ Vector<String> LookupServer::lookup(const String& hostname, bool& did_timeout, u
} }
DNSRequest request; DNSRequest request;
request.add_question(hostname, record_type); request.add_question(hostname, record_type, should_randomize_case);
auto buffer = request.to_byte_buffer(); auto buffer = request.to_byte_buffer();
@ -216,6 +216,15 @@ Vector<String> LookupServer::lookup(const String& hostname, bool& did_timeout, u
dbgprintf("LookupServer: ID mismatch (%u vs %u) :(\n", response.id(), request.id()); dbgprintf("LookupServer: ID mismatch (%u vs %u) :(\n", response.id(), request.id());
return {}; return {};
} }
if (response.code() == DNSResponse::Code::REFUSED) {
if (should_randomize_case == ShouldRandomizeCase::Yes) {
// Retry with 0x20 case randomization turned off.
return lookup(hostname, did_timeout, record_type, ShouldRandomizeCase::No);
}
return {};
}
if (response.question_count() != request.question_count()) { if (response.question_count() != request.question_count()) {
dbgprintf("LookupServer: Question count (%u vs %u) :(\n", response.question_count(), request.question_count()); dbgprintf("LookupServer: Question count (%u vs %u) :(\n", response.question_count(), request.question_count());
return {}; return {};

View file

@ -26,6 +26,7 @@
#pragma once #pragma once
#include "DNSRequest.h"
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/RefPtr.h> #include <AK/RefPtr.h>
#include <AK/String.h> #include <AK/String.h>
@ -44,7 +45,7 @@ public:
private: private:
void load_etc_hosts(); void load_etc_hosts();
void service_client(RefPtr<CLocalSocket>); void service_client(RefPtr<CLocalSocket>);
Vector<String> lookup(const String& hostname, bool& did_timeout, unsigned short record_type); Vector<String> lookup(const String& hostname, bool& did_timeout, unsigned short record_type, ShouldRandomizeCase = ShouldRandomizeCase::Yes);
int make_dns_request_socket(sockaddr_in& dst_addr); int make_dns_request_socket(sockaddr_in& dst_addr);