blob: 79060a478bb2f009b8030873923087740dc786a0 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "resolv"
#include "DnsTlsQueryMap.h"
#include <android-base/logging.h>
namespace android {
namespace net {
std::unique_ptr<DnsTlsQueryMap::QueryFuture> DnsTlsQueryMap::recordQuery(
const netdutils::Slice query) {
std::lock_guard guard(mLock);
// Store the query so it can be matched to the response or reissued.
if (query.size() < 2) {
LOG(WARNING) << "Query is too short";
return nullptr;
}
int32_t newId = getFreeId();
if (newId < 0) {
LOG(WARNING) << "All query IDs are in use";
return nullptr;
}
Query q = { .newId = static_cast<uint16_t>(newId), .query = query };
std::map<uint16_t, QueryPromise>::iterator it;
bool inserted;
std::tie(it, inserted) = mQueries.emplace(newId, q);
if (!inserted) {
LOG(ERROR) << "Failed to store pending query";
return nullptr;
}
return std::make_unique<QueryFuture>(q, it->second.result.get_future());
}
void DnsTlsQueryMap::expire(QueryPromise* p) {
Result r = { .code = Response::network_error };
p->result.set_value(r);
}
void DnsTlsQueryMap::markTried(uint16_t newId) {
std::lock_guard guard(mLock);
auto it = mQueries.find(newId);
if (it != mQueries.end()) {
it->second.tries++;
}
}
void DnsTlsQueryMap::cleanup() {
std::lock_guard guard(mLock);
for (auto it = mQueries.begin(); it != mQueries.end();) {
auto& p = it->second;
if (p.tries >= kMaxTries) {
expire(&p);
it = mQueries.erase(it);
} else {
++it;
}
}
}
int32_t DnsTlsQueryMap::getFreeId() {
if (mQueries.empty()) {
return 0;
}
uint16_t maxId = mQueries.rbegin()->first;
if (maxId < UINT16_MAX) {
return maxId + 1;
}
if (mQueries.size() == UINT16_MAX + 1) {
// Map is full.
return -1;
}
// Linear scan.
uint16_t nextId = 0;
for (auto& pair : mQueries) {
uint16_t id = pair.first;
if (id != nextId) {
// Found a gap.
return nextId;
}
nextId = id + 1;
}
// Unreachable (but the compiler isn't smart enough to prove it).
return -1;
}
std::vector<DnsTlsQueryMap::Query> DnsTlsQueryMap::getAll() {
std::lock_guard guard(mLock);
std::vector<DnsTlsQueryMap::Query> queries;
for (auto& q : mQueries) {
queries.push_back(q.second.query);
}
return queries;
}
bool DnsTlsQueryMap::empty() {
std::lock_guard guard(mLock);
return mQueries.empty();
}
void DnsTlsQueryMap::clear() {
std::lock_guard guard(mLock);
for (auto& q : mQueries) {
expire(&q.second);
}
mQueries.clear();
}
void DnsTlsQueryMap::onResponse(std::vector<uint8_t> response) {
LOG(VERBOSE) << "Got response of size " << response.size();
if (response.size() < 2) {
LOG(WARNING) << "Response is too short";
return;
}
uint16_t id = response[0] << 8 | response[1];
std::lock_guard guard(mLock);
auto it = mQueries.find(id);
if (it == mQueries.end()) {
LOG(WARNING) << "Discarding response: unknown ID " << id;
return;
}
Result r = { .code = Response::success, .response = std::move(response) };
// Rewrite ID to match the query
const uint8_t* data = it->second.query.query.base();
r.response[0] = data[0];
r.response[1] = data[1];
LOG(DEBUG) << "Sending result to dispatcher";
it->second.result.set_value(std::move(r));
mQueries.erase(it);
}
} // end of namespace net
} // end of namespace android