Add random_padding to dns requests.

This commit is contained in:
John Preston 2019-01-30 21:10:53 +03:00
parent 3cfc3dcecf
commit 93a967dc74
1 changed files with 82 additions and 37 deletions

View File

@ -69,7 +69,32 @@ bool CheckPhoneByPrefixesRules(const QString &phone, const QString &rules) {
return result; return result;
} }
std::vector<DnsEntry> ParseDnsResponse(const QByteArray &response) { QString GenerateRandomPadding() {
constexpr char kValid[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
auto result = QString();
const auto count = [&] {
constexpr auto kMinPadding = 13;
constexpr auto kMaxPadding = 128;
while (true) {
const auto result = 1 + (rand_value<uchar>() / 2);
Assert(result <= kMaxPadding);
if (result >= kMinPadding) {
return result;
}
}
}();
result.resize(count);
for (auto &ch : result) {
ch = kValid[rand_value<uchar>() % (sizeof(kValid) - 1)];
}
return result;
}
std::vector<DnsEntry> ParseDnsResponse(
const QByteArray &bytes,
std::optional<int> typeRestriction = std::nullopt) {
// Read and store to "result" all the data bytes from the response: // Read and store to "result" all the data bytes from the response:
// { .., // { ..,
// "Answer": [ // "Answer": [
@ -77,47 +102,62 @@ std::vector<DnsEntry> ParseDnsResponse(const QByteArray &response) {
// { .., "data": "bytes2", "TTL": int, .. } // { .., "data": "bytes2", "TTL": int, .. }
// ], // ],
// .. } // .. }
auto result = std::vector<DnsEntry>();
auto error = QJsonParseError{ 0, QJsonParseError::NoError }; auto error = QJsonParseError{ 0, QJsonParseError::NoError };
auto document = QJsonDocument::fromJson(response, &error); const auto document = QJsonDocument::fromJson(bytes, &error);
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
LOG(("Config Error: Failed to parse dns response JSON, error: %1" LOG(("Config Error: Failed to parse dns response JSON, error: %1"
).arg(error.errorString())); ).arg(error.errorString()));
return {};
} else if (!document.isObject()) { } else if (!document.isObject()) {
LOG(("Config Error: Not an object received in dns response JSON.")); LOG(("Config Error: Not an object received in dns response JSON."));
} else { return {};
auto response = document.object(); }
auto answerIt = response.find(qsl("Answer")); const auto response = document.object();
if (answerIt == response.constEnd()) { const auto answerIt = response.find(qsl("Answer"));
LOG(("Config Error: Could not find Answer " if (answerIt == response.constEnd()) {
"in dns response JSON.")); LOG(("Config Error: Could not find Answer in dns response JSON."));
} else if (!(*answerIt).isArray()) { return {};
LOG(("Config Error: Not an array received " } else if (!(*answerIt).isArray()) {
"in Answer in dns response JSON.")); LOG(("Config Error: Not an array received "
} else { "in Answer in dns response JSON."));
for (auto elem : (*answerIt).toArray()) { return {};
if (!elem.isObject()) { }
LOG(("Config Error: Not an object found "
"in Answer array in dns response JSON.")); auto result = std::vector<DnsEntry>();
} else { for (const auto elem : (*answerIt).toArray()) {
auto object = elem.toObject(); if (!elem.isObject()) {
auto dataIt = object.find(qsl("data")); LOG(("Config Error: Not an object found "
auto ttlIt = object.find(qsl("TTL")); "in Answer array in dns response JSON."));
auto ttl = (ttlIt != object.constEnd()) continue;
? int64(std::round((*ttlIt).toDouble())) }
: int64(0); const auto object = elem.toObject();
if (dataIt == object.constEnd()) { if (typeRestriction) {
LOG(("Config Error: Could not find data " const auto typeIt = object.find(qsl("type"));
"in Answer array entry in dns response JSON.")); const auto type = int(std::round((*typeIt).toDouble()));
} else if (!(*dataIt).isString()) { if (!(*typeIt).isDouble()) {
LOG(("Config Error: Not a string data found " LOG(("Config Error: Not a number in type field "
"in Answer array entry in dns response JSON.")); "in Answer array in dns response JSON."));
} else { continue;
result.push_back({ (*dataIt).toString(), ttl }); } else if (type != *typeRestriction) {
} continue;
}
} }
} }
const auto dataIt = object.find(qsl("data"));
if (dataIt == object.constEnd()) {
LOG(("Config Error: Could not find data "
"in Answer array entry in dns response JSON."));
continue;
} else if (!(*dataIt).isString()) {
LOG(("Config Error: Not a string data found "
"in Answer array entry in dns response JSON."));
continue;
}
const auto ttlIt = object.find(qsl("TTL"));
const auto ttl = (ttlIt != object.constEnd())
? int64(std::round((*ttlIt).toDouble()))
: int64(0);
result.push_back({ (*dataIt).toString(), ttl });
} }
return result; return result;
} }
@ -215,7 +255,9 @@ void SpecialConfigRequest::performRequest(const Attempt &attempt) {
} break; } break;
case Type::Dns: { case Type::Dns: {
url.setPath(qsl("/resolve")); url.setPath(qsl("/resolve"));
url.setQuery(qsl("name=%1&type=16").arg(Global::TxtDomainString())); url.setQuery(qsl("name=%1&type=ANY&random_padding=%2"
).arg(Global::TxtDomainString()
).arg(GenerateRandomPadding()));
request.setRawHeader("Host", "dns.google.com"); request.setRawHeader("Host", "dns.google.com");
} break; } break;
default: Unexpected("Type in SpecialConfigRequest::performRequest."); default: Unexpected("Type in SpecialConfigRequest::performRequest.");
@ -236,8 +278,11 @@ void SpecialConfigRequest::requestFinished(
const auto result = finalizeRequest(reply); const auto result = finalizeRequest(reply);
switch (type) { switch (type) {
case Type::App: handleResponse(result); break; case Type::App: handleResponse(result); break;
case Type::Dns: handleResponse( case Type::Dns: {
ConcatenateDnsTxtFields(ParseDnsResponse(result))); break; constexpr auto kTypeRestriction = 16; // TXT
handleResponse(ConcatenateDnsTxtFields(
ParseDnsResponse(result, kTypeRestriction)));
} break;
default: Unexpected("Type in SpecialConfigRequest::requestFinished."); default: Unexpected("Type in SpecialConfigRequest::requestFinished.");
} }
} }