116 const char* home = std::getenv(
"HOME");
118 std::cerr <<
_(
"[filter] HOME variable not set\n");
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";
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;
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"));
144 std::vector<std::string> to_strip;
145 for (
const auto& entry : new_proxy_bypass) {
146 if (entry.find(
'/') == std::string::npos)
continue;
149 new_proxy_bypass_cidrs.push_back(ce);
150 to_strip.push_back(entry);
153 for (
const auto& s : to_strip) new_proxy_bypass.erase(s);
157 new_exclusions.insert(d);
160 std::unique_lock lock(
mutex_);
161 banks_ = std::move(new_banks);
170 std::snprintf(msg,
sizeof(msg),
171 _(
"[filter] loaded %zu bank, %zu user-bank, %zu payment, %zu exclusion, %zu proxy-bypass (+%zu CIDR)\n"),
209 if (host_raw.empty())
return false;
212 std::string h = host_raw;
213 if (h.size() >= 2 && h.front() ==
'[' && h.back() ==
']')
214 h = h.substr(1, h.size() - 2);
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)); });
222 if (h_lower ==
"localhost")
return true;
223 if (h_lower ==
"ip6-localhost")
return true;
224 if (h_lower ==
"ip6-loopback")
return true;
227 static const char* local_suffixes[] = {
228 ".local",
".localhost",
".lan",
".intranet",
229 ".internal",
".private",
".corp",
".home",
".home.arpa",
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) {
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;
249 if (b1 == 127)
return true;
251 if (b1 == 10)
return true;
253 if (b1 == 172 && (b2 >= 16 && b2 <= 31))
return true;
255 if (b1 == 192 && b2 == 168)
return true;
257 if (b1 == 169 && b2 == 254)
return true;
259 if (b1 == 100 && (b2 & 0xC0) == 0x40)
return true;
261 if (b1 == 0)
return true;
270 if (::inet_pton(
AF_INET6, h_lower.c_str(), &v6) == 1) {
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;
276 if (v6.s6_addr[0] == 0xFE && (v6.s6_addr[1] & 0xC0) == 0x80)
return true;
279 if ((v6.s6_addr[0] & 0xFE) == 0xFC)
return true;
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];
287 std::memcpy(&v4inner.s_addr, v6.s6_addr + 12, 4);
288 if (::inet_ntop(
AF_INET, &v4inner, buf,
sizeof(buf)))
298 if (h_lower.find(
'.') == std::string::npos)
return true;
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;
572 if (!std::isdigit(
static_cast<unsigned char>(c)))
return false;
573 const int m = std::atoi(mask.c_str());
576 if (::inet_pton(
AF_INET, addr.c_str(), &v4) == 1) {
577 if (m < 0 || m > 32)
return false;
579 std::memset(out.
addr, 0, 16);
580 std::memcpy(out.
addr, &v4.s_addr, 4);
585 if (::inet_pton(
AF_INET6, addr.c_str(), &v6) == 1) {
586 if (m < 0 || m > 128)
return false;
588 std::memcpy(out.
addr, v6.s6_addr, 16);
596 const std::vector<CidrEntry>& cidrs)
598 if (cidrs.empty() || host_raw.empty())
return false;
601 std::string h = host_raw;
602 if (h.size() >= 2 && h.front() ==
'[' && h.back() ==
']')
603 h = h.substr(1, h.size() - 2);
605 std::uint8_t hbytes[16] = {0};
606 bool host_is_v6 =
false;
610 if (::inet_pton(
AF_INET, h.c_str(), &v4) == 1) {
611 std::memcpy(hbytes, &v4.s_addr, 4);
613 }
else if (::inet_pton(
AF_INET6, h.c_str(), &v6) == 1) {
614 std::memcpy(hbytes, v6.s6_addr, 16);
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;
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)) {