Skip to content
API Documentation
BastionGuard™
DomainFilter Class Reference

#include <bank_filter.hpp>

Classes

struct  CidrEntry

Public Member Functions

void load ()
bool is_proxy_bypass (const std::string &host) const
bool is_secure (const std::string &host) const
bool is_trusted (const std::string &host) const
bool is_bank (const std::string &host) const
bool is_payment (const std::string &host) const

Static Public Member Functions

static bool is_local_network (const std::string &host_raw)

Static Private Member Functions

static const std::unordered_set< std::string > & get_builtin_payment_providers ()
static const std::unordered_set< std::string > & get_builtin_exclusions ()
static void load_file (const std::filesystem::path &path, std::unordered_set< std::string > &target, const char *label)
static bool match (const std::string &host, const std::unordered_set< std::string > &set)
static bool parse_cidr (const std::string &s, CidrEntry &out)
static bool match_cidr (const std::string &host_raw, const std::vector< CidrEntry > &cidrs)

Private Attributes

std::shared_mutex mutex_
std::unordered_set< std::string > banks_
std::unordered_set< std::string > user_banks_
std::unordered_set< std::string > payments_
std::unordered_set< std::string > trusted_
std::unordered_set< std::string > exclusions_
std::unordered_set< std::string > proxy_bypass_
std::vector< CidrEntry > proxy_bypass_cidrs_

Detailed Description

Definition at line 113 of file bank_filter.hpp.

Member Function Documentation

◆ get_builtin_exclusions()

const std::unordered_set< std::string > & DomainFilter::get_builtin_exclusions ( )
inlinestaticprivate

Definition at line 395 of file bank_filter.hpp.

395 {
396 static const std::unordered_set<std::string> ex = {
397 "js.stripe.com",
398 "api.stripe.com",
399 "m.stripe.com",
400 "r.stripe.com",
401 "b.stripe.com",
402 "q.stripe.com",
403 "hooks.stripe.com",
404 "cdn.stripe.com",
405 "errors.stripe.com",
406 "stripe.network",
407 "api.paypal.com",
408 "t.paypal.com",
409 "c.paypal.com",
410 "pics.paypal.com",
411 "history.paypal.com",
412 "stats.paypal.com",
413 "api-m.paypal.com",
414 "api-m.sandbox.paypal.com",
415 "www.sandbox.paypal.com",
416 "api.sandbox.paypal.com",
417 "payments.amazon.com",
418 "static.payments.amazon.com",
419 "widgets.amazon.com",
420 "fls-eu.amazon.com",
421 "google.com/pay",
422 "tpay.google.com",
423 "pay-sandbox.google.com",
424 "apple-pay-gateway.apple.com",
425 "apple-pay-gateway-cert.apple.com",
426 "cn-apple-pay-gateway.apple.com",
427 "live.adyen.com",
428 "checkoutshopper-live.adyen.com",
429 "checkoutshopper-test.adyen.com",
430 "ca-live.adyen.com",
431 "analytics.adyen.com",
432 "js.klarna.com",
433 "api.klarna.com",
434 "x.klarnacdn.net",
435 "klarnaservices.com",
436 "osm.klarnaservices.com",
437 "api.checkout.com",
438 "cdn.checkout.com",
439 "events.checkout.com",
440 "api.worldpay.com",
441 "access.worldpay.com",
442 "cdn.worldpay.com",
443 "api.braintreegateway.com",
444 "assets.braintreegateway.com",
445 "payments.braintree-api.com",
446 "client-analytics.braintreegateway.com",
447 "api.braintreepayments.com",
448 "api.nexi.it",
449 "xpay.nexigroup.com",
450 "ecommerce.nexi.it",
451 "api.sisalpay.it",
452 "facebook.com",
453 "www.facebook.com",
454 "m.facebook.com",
455 "graph.facebook.com",
456 "connect.facebook.net",
457 "staticxx.facebook.com",
458 "static.xx.fbcdn.net",
459 "accounts.facebook.com",
460 "web.facebook.com",
461 "business.facebook.com",
462 "developers.facebook.com",
463 "l.facebook.com",
464 "fbcdn.net",
465 "akamaihd.net",
466 "instagram.com",
467 "www.instagram.com",
468 "cdninstagram.com",
469 "whatsapp.com",
470 "web.whatsapp.com",
471 "api.whatsapp.com",
472 "messenger.com",
473 "www.messenger.com",
474 "accounts.google.com",
475 "oauth2.googleapis.com",
476 "apis.google.com",
477 "recaptcha.google.com",
478 "www.recaptcha.net",
479 "recaptcha.net",
480 "www.gstatic.com",
481 "gstatic.com",
482 "google.com",
483 "www.google.com",
484 "google.it",
485 "www.google.it",
486 "id.google.com",
487 "myaccount.google.com",
488 "signin.google.com",
489 "login.microsoftonline.com",
490 "login.live.com",
491 "login.windows.net",
492 "account.microsoft.com",
493 "microsoftonline.com",
494 "appleid.apple.com",
495 "idmsa.apple.com",
496 "gsa.apple.com",
497 "challenges.cloudflare.com",
498 "cloudflare.com",
499 "twitter.com",
500 "x.com",
501 "api.twitter.com",
502 "t.co",
503 };
504
505 return ex;
506 }

Referenced by load().

◆ get_builtin_payment_providers()

const std::unordered_set< std::string > & DomainFilter::get_builtin_payment_providers ( )
inlinestaticprivate

Definition at line 364 of file bank_filter.hpp.

364 {
365 static const std::unordered_set<std::string> pp = {
366 "stripe.com",
367 "paypal.com",
368 "paypalobjects.com",
369 "adyen.com",
370 "checkout.com",
371 "klarna.com",
372 "worldpay.com",
373 "braintreegateway.com",
374 "nexi.it",
375 "nexigroup.com",
376 "sisalpay.it",
377 "satispay.com",
378 "sumup.com",
379 "square.com",
380 "squareup.com",
381 "mollie.com",
382 "gocardless.com",
383 "wise.com",
384 "revolut.com",
385 "skrill.com",
386 "neteller.com",
387 "paysafecard.com",
388 "amazonpay.com",
389 "wallet.google.com",
390 "pay.google.com",
391 };
392 return pp;
393 }

Referenced by is_payment(), and is_secure().

◆ is_bank()

bool DomainFilter::is_bank ( const std::string & host) const
inline

Definition at line 321 of file bank_filter.hpp.

321 {
322 if (is_local_network(host)) return false;
323 std::shared_lock lock(mutex_);
324 if (match(host, proxy_bypass_)) return false;
325 if (match(host, exclusions_)) return false;
326 return match(host, banks_) || match(host, user_banks_);
327 }
std::shared_mutex mutex_
static bool is_local_network(const std::string &host_raw)
std::unordered_set< std::string > user_banks_
std::unordered_set< std::string > banks_
static bool match(const std::string &host, const std::unordered_set< std::string > &set)
std::unordered_set< std::string > proxy_bypass_
std::unordered_set< std::string > exclusions_

References banks_, exclusions_, is_local_network(), match(), mutex_, proxy_bypass_, and user_banks_.

Referenced by handle_client().

◆ is_local_network()

bool DomainFilter::is_local_network ( const std::string & host_raw)
inlinestatic

Definition at line 208 of file bank_filter.hpp.

208 {
209 if (host_raw.empty()) return false;
210
211 // Strip eventuali bracket IPv6: [::1] → ::1
212 std::string h = host_raw;
213 if (h.size() >= 2 && h.front() == '[' && h.back() == ']')
214 h = h.substr(1, h.size() - 2);
215
216 // Lowercase per il match hostname (DNS è case-insensitive)
217 std::string h_lower = h;
218 std::transform(h_lower.begin(), h_lower.end(), h_lower.begin(),
219 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
220
221 // 1) Hostname speciali
222 if (h_lower == "localhost") return true;
223 if (h_lower == "ip6-localhost") return true;
224 if (h_lower == "ip6-loopback") return true;
225
226 // 2) TLD locali standard (mDNS/zeroconf, RFC6762, RFC8375, RFC2606)
227 static const char* local_suffixes[] = {
228 ".local", ".localhost", ".lan", ".intranet",
229 ".internal", ".private", ".corp", ".home", ".home.arpa",
230 nullptr
231 };
232 for (int i = 0; local_suffixes[i]; ++i) {
233 const std::string suf = local_suffixes[i];
234 if (h_lower.size() >= suf.size() &&
235 h_lower.compare(h_lower.size() - suf.size(), suf.size(), suf) == 0) {
236 return true;
237 }
238 }
239
240 // 3) IPv4 letterale → check per range
241 {
242 in_addr v4{};
243 if (::inet_pton(AF_INET, h_lower.c_str(), &v4) == 1) {
244 const std::uint32_t a = ntohl(v4.s_addr);
245 const std::uint8_t b1 = (a >> 24) & 0xFF;
246 const std::uint8_t b2 = (a >> 16) & 0xFF;
247
248 // 127.0.0.0/8 loopback
249 if (b1 == 127) return true;
250 // 10.0.0.0/8 RFC1918
251 if (b1 == 10) return true;
252 // 172.16.0.0/12 RFC1918
253 if (b1 == 172 && (b2 >= 16 && b2 <= 31)) return true;
254 // 192.168.0.0/16 RFC1918
255 if (b1 == 192 && b2 == 168) return true;
256 // 169.254.0.0/16 link-local
257 if (b1 == 169 && b2 == 254) return true;
258 // 100.64.0.0/10 CGNAT (RFC6598)
259 if (b1 == 100 && (b2 & 0xC0) == 0x40) return true;
260 // 0.0.0.0/8 "this network"
261 if (b1 == 0) return true;
262
263 return false; // IPv4 pubblico
264 }
265 }
266
267 // 4) IPv6 letterale → check per range
268 {
269 in6_addr v6{};
270 if (::inet_pton(AF_INET6, h_lower.c_str(), &v6) == 1) {
271 // ::1 loopback
272 static const std::uint8_t loopback[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
273 if (std::memcmp(v6.s6_addr, loopback, 16) == 0) return true;
274
275 // fe80::/10 link-local: i primi 10 bit = 1111 1110 10
276 if (v6.s6_addr[0] == 0xFE && (v6.s6_addr[1] & 0xC0) == 0x80) return true;
277
278 // fc00::/7 unique local: i primi 7 bit = 1111 110
279 if ((v6.s6_addr[0] & 0xFE) == 0xFC) return true;
280
281 // ::ffff:0:0/96 IPv4-mapped → estraiamo l'IPv4 dentro e ricorriamo
282 static const std::uint8_t v4mapped_prefix[12] =
283 {0,0,0,0,0,0,0,0,0,0,0xFF,0xFF};
284 if (std::memcmp(v6.s6_addr, v4mapped_prefix, 12) == 0) {
285 char buf[INET_ADDRSTRLEN];
286 in_addr v4inner{};
287 std::memcpy(&v4inner.s_addr, v6.s6_addr + 12, 4);
288 if (::inet_ntop(AF_INET, &v4inner, buf, sizeof(buf)))
289 return is_local_network(buf);
290 }
291
292 return false; // IPv6 pubblico
293 }
294 }
295
296 // 5) Hostname senza punti = nome NetBIOS / LAN tipico (es. "router",
297 // "nas", "printer") — lo trattiamo come locale per sicurezza.
298 if (h_lower.find('.') == std::string::npos) return true;
299
300 return false;
301 }
#define AF_INET
Definition bsc_ebpf.c:56
#define AF_INET6
Definition bsc_ebpf.c:57

References AF_INET, AF_INET6, and is_local_network().

Referenced by handle_client(), is_bank(), is_local_network(), is_payment(), and is_secure().

◆ is_payment()

bool DomainFilter::is_payment ( const std::string & host) const
inline

Definition at line 329 of file bank_filter.hpp.

329 {
330 if (is_local_network(host)) return false;
331 std::shared_lock lock(mutex_);
332 if (match(host, proxy_bypass_)) return false;
333 if (match(host, exclusions_)) return false;
334 if (match(host, payments_)) return true;
335 return match(host, get_builtin_payment_providers());
336 }
static const std::unordered_set< std::string > & get_builtin_payment_providers()
std::unordered_set< std::string > payments_

References exclusions_, get_builtin_payment_providers(), is_local_network(), match(), mutex_, payments_, and proxy_bypass_.

◆ is_proxy_bypass()

bool DomainFilter::is_proxy_bypass ( const std::string & host) const
inline

Definition at line 178 of file bank_filter.hpp.

178 {
179 std::shared_lock lock(mutex_);
180 // 1) match esatto/suffisso DNS (incluso IP letterale "93.55.84.43")
181 if (match(host, proxy_bypass_)) return true;
182 // 2) match CIDR (host deve essere un IP letterale che cade in un range)
183 return match_cidr(host, proxy_bypass_cidrs_);
184 }
static bool match_cidr(const std::string &host_raw, const std::vector< CidrEntry > &cidrs)
std::vector< CidrEntry > proxy_bypass_cidrs_

References match(), match_cidr(), mutex_, proxy_bypass_, and proxy_bypass_cidrs_.

Referenced by handle_client().

◆ is_secure()

bool DomainFilter::is_secure ( const std::string & host) const
inline

Definition at line 303 of file bank_filter.hpp.

303 {
304 if (is_local_network(host)) return false;
305 std::shared_lock lock(mutex_);
306
307 if (match(host, proxy_bypass_)) return false;
308 if (match(host, exclusions_)) return false;
309 if (match(host, banks_) || match(host, user_banks_) || match(host, payments_))
310 return true;
311 // Provider di pagamento noti: intercetta anche senza payments.json,
312 // così Stripe/PayPal/ecc. si aprono in sandbox e non nel browser.
313 return match(host, get_builtin_payment_providers());
314 }

References banks_, exclusions_, get_builtin_payment_providers(), is_local_network(), match(), mutex_, payments_, proxy_bypass_, and user_banks_.

Referenced by handle_client().

◆ is_trusted()

bool DomainFilter::is_trusted ( const std::string & host) const
inline

Definition at line 316 of file bank_filter.hpp.

316 {
317 std::shared_lock lock(mutex_);
318 return match(host, trusted_);
319 }
std::unordered_set< std::string > trusted_

References match(), mutex_, and trusted_.

◆ load()

void DomainFilter::load ( )
inline

Definition at line 115 of file bank_filter.hpp.

115 {
116 const char* home = std::getenv("HOME");
117 if (!home) {
118 std::cerr << _("[filter] HOME variable not set\n");
119 return;
120 }
121
122 namespace fs = std::filesystem;
123 fs::path data_base = fs::path(home) / ".local/share/BastionGuard";
124 fs::path config_base = fs::path(home) / ".config/BastionGuard";
125
126 std::unordered_set<std::string> new_banks;
127 std::unordered_set<std::string> new_payments;
128 std::unordered_set<std::string> new_trusted;
129 std::unordered_set<std::string> new_exclusions;
130 std::unordered_set<std::string> new_user_banks;
131 std::unordered_set<std::string> new_proxy_bypass;
132 std::vector<CidrEntry> new_proxy_bypass_cidrs;
133
134 load_file(data_base / "banks.json", new_banks, _("banks"));
135 load_file(config_base / "payments.json", new_payments, _("payments"));
136 load_file(config_base / "exclusions.json", new_exclusions, _("exclusions"));
137 load_file(config_base / "whitelist.json", new_user_banks, _("user-banks"));
138 load_file(config_base / "proxy_bypass.json", new_proxy_bypass, _("proxy-bypass"));
139
140 // Separiamo i CIDR (es. "93.55.84.0/24") dal set di stringhe esatte.
141 // Il set string-based serve solo per match esatto/suffisso; i CIDR
142 // vanno gestiti col bit-mask in match_cidr().
143 {
144 std::vector<std::string> to_strip;
145 for (const auto& entry : new_proxy_bypass) {
146 if (entry.find('/') == std::string::npos) continue;
147 CidrEntry ce;
148 if (parse_cidr(entry, ce)) {
149 new_proxy_bypass_cidrs.push_back(ce);
150 to_strip.push_back(entry);
151 }
152 }
153 for (const auto& s : to_strip) new_proxy_bypass.erase(s);
154 }
155
156 for (const auto& d : get_builtin_exclusions())
157 new_exclusions.insert(d);
158
159 {
160 std::unique_lock lock(mutex_);
161 banks_ = std::move(new_banks);
162 payments_ = std::move(new_payments);
163 trusted_ = std::move(new_trusted);
164 exclusions_ = std::move(new_exclusions);
165 user_banks_ = std::move(new_user_banks);
166 proxy_bypass_ = std::move(new_proxy_bypass);
167 proxy_bypass_cidrs_ = std::move(new_proxy_bypass_cidrs);
168
169 char msg[256];
170 std::snprintf(msg, sizeof(msg),
171 _("[filter] loaded %zu bank, %zu user-bank, %zu payment, %zu exclusion, %zu proxy-bypass (+%zu CIDR)\n"),
172 banks_.size(), user_banks_.size(), payments_.size(),
173 exclusions_.size(), proxy_bypass_.size(), proxy_bypass_cidrs_.size());
174 std::cerr << msg;
175 }
176 }
#define _(s)
static const std::unordered_set< std::string > & get_builtin_exclusions()
static bool parse_cidr(const std::string &s, CidrEntry &out)
static void load_file(const std::filesystem::path &path, std::unordered_set< std::string > &target, const char *label)

References _, banks_, exclusions_, get_builtin_exclusions(), load_file(), mutex_, parse_cidr(), payments_, proxy_bypass_, proxy_bypass_cidrs_, trusted_, and user_banks_.

Referenced by main().

◆ load_file()

void DomainFilter::load_file ( const std::filesystem::path & path,
std::unordered_set< std::string > & target,
const char * label )
inlinestaticprivate

Definition at line 508 of file bank_filter.hpp.

511 {
512 target.clear();
513
514 std::string json = read_file(path);
515 if (json.empty()) {
516 char msg[512];
517 std::snprintf(msg, sizeof(msg),
518 _("[filter] cannot read %s list: %s\n"),
519 label, path.c_str());
520 std::cerr << msg;
521 return;
522 }
523
524 target = extract_list(json);
525 }
nlohmann::json json
Definition Backend.cpp:2908
static std::unordered_set< std::string > extract_list(const std::string &json)
static std::string read_file(const std::filesystem::path &p)

References _, extract_list(), and read_file().

Referenced by load().

◆ match()

bool DomainFilter::match ( const std::string & host,
const std::unordered_set< std::string > & set )
inlinestaticprivate

Definition at line 527 of file bank_filter.hpp.

529 {
530 if (set.count(host)) return true;
531 if (set.count("." + host)) return true;
532 std::string h = host;
533
534 while (true) {
535 auto dot = h.find('.');
536 if (dot == std::string::npos) break;
537
538 std::string with_dot = h.substr(dot);
539 std::string without = h.substr(dot + 1);
540
541 if (without.find('.') != std::string::npos) {
542 if (set.count(with_dot)) return true;
543 if (set.count(without)) return true;
544 }
545
546 h = without;
547 }
548
549 return false;
550 }

Referenced by is_bank(), is_payment(), is_proxy_bypass(), is_secure(), and is_trusted().

◆ match_cidr()

bool DomainFilter::match_cidr ( const std::string & host_raw,
const std::vector< CidrEntry > & cidrs )
inlinestaticprivate

Definition at line 595 of file bank_filter.hpp.

597 {
598 if (cidrs.empty() || host_raw.empty()) return false;
599
600 // Strip bracket "[::1]" → "::1"
601 std::string h = host_raw;
602 if (h.size() >= 2 && h.front() == '[' && h.back() == ']')
603 h = h.substr(1, h.size() - 2);
604
605 std::uint8_t hbytes[16] = {0};
606 bool host_is_v6 = false;
607
608 in_addr v4{};
609 in6_addr v6{};
610 if (::inet_pton(AF_INET, h.c_str(), &v4) == 1) {
611 std::memcpy(hbytes, &v4.s_addr, 4);
612 host_is_v6 = false;
613 } else if (::inet_pton(AF_INET6, h.c_str(), &v6) == 1) {
614 std::memcpy(hbytes, v6.s6_addr, 16);
615 host_is_v6 = true;
616 } else {
617 // Hostname (non IP): non possiamo confrontare con CIDR senza DNS.
618 return false;
619 }
620
621 for (const auto& c : cidrs) {
622 if (c.is_v6 != host_is_v6) continue;
623 const int total_bits = c.is_v6 ? 128 : 32;
624 int rem = c.prefix;
625 bool ok = true;
626 for (int i = 0; i < (total_bits / 8) && rem > 0; ++i) {
627 const std::uint8_t mask =
628 (rem >= 8) ? 0xFF : static_cast<std::uint8_t>(0xFF << (8 - rem));
629 if ((hbytes[i] & mask) != (c.addr[i] & mask)) {
630 ok = false;
631 break;
632 }
633 rem -= 8;
634 }
635 if (ok) return true;
636 }
637 return false;
638 }

References AF_INET, and AF_INET6.

Referenced by is_proxy_bypass().

◆ parse_cidr()

bool DomainFilter::parse_cidr ( const std::string & s,
CidrEntry & out )
inlinestaticprivate

Definition at line 563 of file bank_filter.hpp.

563 {
564 auto slash = s.find('/');
565 if (slash == std::string::npos) return false;
566 const std::string addr = s.substr(0, slash);
567 const std::string mask = s.substr(slash + 1);
568 if (addr.empty() || mask.empty()) return false;
569
570 // mask numerica
571 for (char c : mask)
572 if (!std::isdigit(static_cast<unsigned char>(c))) return false;
573 const int m = std::atoi(mask.c_str());
574
575 in_addr v4{};
576 if (::inet_pton(AF_INET, addr.c_str(), &v4) == 1) {
577 if (m < 0 || m > 32) return false;
578 out.is_v6 = false;
579 std::memset(out.addr, 0, 16);
580 std::memcpy(out.addr, &v4.s_addr, 4);
581 out.prefix = m;
582 return true;
583 }
584 in6_addr v6{};
585 if (::inet_pton(AF_INET6, addr.c_str(), &v6) == 1) {
586 if (m < 0 || m > 128) return false;
587 out.is_v6 = true;
588 std::memcpy(out.addr, v6.s6_addr, 16);
589 out.prefix = m;
590 return true;
591 }
592 return false;
593 }

References DomainFilter::CidrEntry::addr, AF_INET, AF_INET6, DomainFilter::CidrEntry::is_v6, and DomainFilter::CidrEntry::prefix.

Referenced by load().

Member Data Documentation

◆ banks_

std::unordered_set<std::string> DomainFilter::banks_
private

Definition at line 341 of file bank_filter.hpp.

Referenced by is_bank(), is_secure(), and load().

◆ exclusions_

std::unordered_set<std::string> DomainFilter::exclusions_
private

Definition at line 345 of file bank_filter.hpp.

Referenced by is_bank(), is_payment(), is_secure(), and load().

◆ mutex_

std::shared_mutex DomainFilter::mutex_
mutableprivate

Definition at line 339 of file bank_filter.hpp.

Referenced by is_bank(), is_payment(), is_proxy_bypass(), is_secure(), is_trusted(), and load().

◆ payments_

std::unordered_set<std::string> DomainFilter::payments_
private

Definition at line 343 of file bank_filter.hpp.

Referenced by is_payment(), is_secure(), and load().

◆ proxy_bypass_

std::unordered_set<std::string> DomainFilter::proxy_bypass_
private

Definition at line 346 of file bank_filter.hpp.

Referenced by is_bank(), is_payment(), is_proxy_bypass(), is_secure(), and load().

◆ proxy_bypass_cidrs_

std::vector<CidrEntry> DomainFilter::proxy_bypass_cidrs_
private

Definition at line 354 of file bank_filter.hpp.

Referenced by is_proxy_bypass(), and load().

◆ trusted_

std::unordered_set<std::string> DomainFilter::trusted_
private

Definition at line 344 of file bank_filter.hpp.

Referenced by is_trusted(), and load().

◆ user_banks_

std::unordered_set<std::string> DomainFilter::user_banks_
private

Definition at line 342 of file bank_filter.hpp.

Referenced by is_bank(), is_secure(), and load().


The documentation for this class was generated from the following file: