Skip to content
API Documentation
BastionGuard™
GeoResolver.hpp
Go to the documentation of this file.
1/*
2 * BastionGuard™ Secure Connection
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 * GPL-3.0
5 *
6 * Risolve IP -> {country_code, lat, lon, city, asn} usando ip-api.com.
7 * Ha:
8 * - Cache in-memory (IP -> risultato) con scadenza 24h
9 * - Rate-limiter client-side (ip-api.com permette 45 req/min in free tier)
10 * - Fallback silent se no network (non blocca la UI)
11 *
12 * L'implementazione usa un worker thread dedicato per non bloccare la UI:
13 * la UI chiama resolve() che ritorna subito l'entry cache o std::nullopt;
14 * se nullopt, mette l'IP in una coda di lookup e notifica via callback
15 * quando la risoluzione completa.
16 */
17
18#pragma once
19
20#include <atomic>
21#include <chrono>
22#include <condition_variable>
23#include <functional>
24#include <mutex>
25#include <optional>
26#include <queue>
27#include <string>
28#include <thread>
29#include <unordered_map>
30#include <unordered_set>
31
32namespace bsc::ui_util {
33
34struct GeoEntry {
35 std::string country_code; // "IT", "US", "DE", ...
36 std::string country_name; // "Italy", "United States", ...
37 std::string city;
38 std::string region;
39 std::string isp; // ISP name (es. "Telecom Italia")
40 std::string asn; // "AS12345"
41 double lat{0.0};
42 double lon{0.0};
43 std::chrono::steady_clock::time_point fetched_at;
44};
45
46/**
47 * Service singleton che risolve IP -> GeoEntry.
48 * Ha un worker thread che processa una coda di lookup; la UI si iscrive
49 * via on_resolved(callback) per ricevere i risultati mano mano che arrivano.
50 */
52public:
53 using ResolvedCallback = std::function<void(const std::string& ip, const GeoEntry&)>;
54
55 static GeoResolver& instance();
56
57 GeoResolver(const GeoResolver&) = delete;
59
60 /**
61 * Se l'IP e' gia' in cache ritorna subito la GeoEntry.
62 * Altrimenti ritorna nullopt e mette l'IP in coda: il callback
63 * registrato via on_resolved sara' chiamato quando il lookup completa.
64 *
65 * Gli IP privati/loopback/link-local non vengono mai risolti (ritorno
66 * un GeoEntry "locale" con country_code = "" e lat/lon = 0).
67 */
68 std::optional<GeoEntry> resolve(const std::string& ip);
69
70 /**
71 * Risolve l'IP pubblico del PC locale e la sua geolocalizzazione.
72 * Chiamando ip-api.com senza IP, il servizio geolocalizza il chiamante.
73 * Il risultato viene cached come qualsiasi altro IP, sotto la chiave
74 * speciale "__self__".
75 *
76 * Ritorna subito la cache se presente, altrimenti mette in coda e
77 * ritorna nullopt. Il callback on_resolved() sara' chiamato con
78 * ip == "__self__" quando la risoluzione completa.
79 */
80 std::optional<GeoEntry> resolve_self();
81
82 /** Registra un callback per i risultati asincroni. Thread-safe. */
84
85 /** Shutdown ordinato del worker thread. */
86 void stop();
87
88private:
91
92 void worker_loop();
93 std::optional<GeoEntry> fetch_from_ipapi(const std::string& ip);
94 static bool is_private_ip(const std::string& ip);
95
96 std::mutex mu_;
97 std::condition_variable cv_;
98 std::atomic_bool running_{true};
99 std::queue<std::string> queue_;
100 std::unordered_set<std::string> in_flight_;
101 std::unordered_map<std::string, GeoEntry> cache_;
102 std::vector<ResolvedCallback> callbacks_;
103 std::thread worker_;
104
105 // Rate limiter: max 40 req/min per stare sotto il limite di 45
106 std::chrono::steady_clock::time_point last_request_{};
107};
108
109} // namespace bsc::ui_util
static bool is_private_ip(const std::string &ip)
std::condition_variable cv_
std::optional< GeoEntry > fetch_from_ipapi(const std::string &ip)
std::optional< GeoEntry > resolve_self()
std::chrono::steady_clock::time_point last_request_
static GeoResolver & instance()
GeoResolver(const GeoResolver &)=delete
std::vector< ResolvedCallback > callbacks_
std::unordered_map< std::string, GeoEntry > cache_
std::optional< GeoEntry > resolve(const std::string &ip)
GeoResolver & operator=(const GeoResolver &)=delete
std::atomic_bool running_
void on_resolved(ResolvedCallback cb)
std::queue< std::string > queue_
std::unordered_set< std::string > in_flight_
std::function< void(const std::string &ip, const GeoEntry &)> ResolvedCallback
std::chrono::steady_clock::time_point fetched_at