Skip to content
API Documentation
BastionGuard™
async_dns_resolver.hpp
Go to the documentation of this file.
1/*
2 * BastionGuard™
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 *
17 * BastionGuard™ is a trademark of Calogero Scarnà.
18 * The BastionGuard™ name and branding are not licensed under the GPL.
19 */
20
21#pragma once
22#include <string>
23#include <unordered_map>
24#include <future>
25#include <mutex>
26#include <thread>
27#include <chrono>
28#include <netdb.h>
29#include <arpa/inet.h>
30
32public:
34 static AsyncDNSResolver inst;
35 return inst;
36 }
37
38 std::future<std::string> resolve_async(const std::string& host) {
39 {
40 std::lock_guard<std::mutex> lk(mutex_);
41 auto itc = cache_.find(host);
42 if (itc != cache_.end() && now_ms() < itc->second.expires) {
43 std::promise<std::string> p;
44 p.set_value(itc->second.ip);
45 return p.get_future();
46 }
47 auto itp = pending_.find(host);
48 if (itp != pending_.end()) {
49 return itp->second->get_future();
50 }
51 }
52
53 auto prom = std::make_shared<std::promise<std::string>>();
54 {
55 std::lock_guard<std::mutex> lk(mutex_);
56 pending_[host] = prom;
57 }
58
59 std::thread([this, host, prom]() {
60 std::string ip;
61 struct addrinfo hints{}, *res = nullptr;
62 hints.ai_family = AF_INET;
63 hints.ai_socktype = SOCK_STREAM;
64 int rc = getaddrinfo(host.c_str(), nullptr, &hints, &res);
65 if (rc == 0 && res) {
66 char buf[INET_ADDRSTRLEN] = {0};
67 if (res->ai_family == AF_INET) {
68 auto *a = reinterpret_cast<struct sockaddr_in*>(res->ai_addr);
69 inet_ntop(AF_INET, &a->sin_addr, buf, sizeof(buf));
70 ip = buf;
71 }
72 freeaddrinfo(res);
73 }
74 {
75 std::lock_guard<std::mutex> lk(mutex_);
76 cache_[host] = { ip, now_ms() + TTL_MS };
77 pending_.erase(host);
78 }
79 prom->set_value(ip);
80 }).detach();
81
82 return prom->get_future();
83 }
84
85 std::string resolve_cached(const std::string& host) {
86 std::lock_guard<std::mutex> lk(mutex_);
87 auto it = cache_.find(host);
88 if (it != cache_.end() && now_ms() < it->second.expires) return it->second.ip;
89 return {};
90 }
91
92private:
93 struct Entry { std::string ip; uint64_t expires; };
94 static constexpr uint64_t TTL_MS = 5 * 60 * 1000;
95 std::unordered_map<std::string, Entry> cache_;
96 std::unordered_map<std::string, std::shared_ptr<std::promise<std::string>>> pending_;
97 std::mutex mutex_;
98
99 static uint64_t now_ms() {
100 using namespace std::chrono;
101 return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
102 }
103 AsyncDNSResolver() = default;
104};
#define AF_INET
Definition bsc_ebpf.c:56
std::unordered_map< std::string, std::shared_ptr< std::promise< std::string > > > pending_
static uint64_t now_ms()
static constexpr uint64_t TTL_MS
std::future< std::string > resolve_async(const std::string &host)
std::string resolve_cached(const std::string &host)
std::unordered_map< std::string, Entry > cache_
AsyncDNSResolver()=default
static AsyncDNSResolver & instance()