Skip to content
API Documentation
BastionGuard™
ProxyDetect.cpp
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
7#include "ProxyDetect.hpp"
8
9#include <algorithm>
10#include <cctype>
11#include <cstdlib>
12#include <filesystem>
13#include <fstream>
14#include <mutex>
15#include <sstream>
16
17namespace bsc::ui_util {
18namespace fs = std::filesystem;
19namespace {
20
21std::string to_lower(std::string s) {
22 std::transform(s.begin(), s.end(), s.begin(),
23 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
24 return s;
25}
26
27std::string trim(std::string s) {
28 auto not_space = [](unsigned char c) { return !std::isspace(c); };
29 s.erase(s.begin(), std::find_if(s.begin(), s.end(), not_space));
30 s.erase(std::find_if(s.rbegin(), s.rend(), not_space).base(), s.end());
31 // Rimuovi eventuali quote
32 if (s.size() >= 2 && (s.front() == '"' || s.front() == '\'')) {
33 if (s.back() == s.front()) s = s.substr(1, s.size() - 2);
34 }
35 return s;
36}
37
38// Parse URL del tipo http://host:port, socks5://user:pass@host:port, ecc.
39// Non abbiamo una libreria URL: parsing manuale robusto a casi comuni.
40bool parse_proxy_url(const std::string& raw, ProxyEndpoint& out) {
41 auto url = trim(raw);
42 if (url.empty()) return false;
43
44 // scheme://
45 auto scheme_end = url.find("://");
46 if (scheme_end != std::string::npos) {
47 out.scheme = to_lower(url.substr(0, scheme_end));
48 url = url.substr(scheme_end + 3);
49 } else {
50 // host:port senza scheme
51 out.scheme = "http";
52 }
53
54 // Rimuovi user:pass@
55 auto at = url.find('@');
56 if (at != std::string::npos) url = url.substr(at + 1);
57
58 // Taglia path dopo /
59 auto slash = url.find('/');
60 if (slash != std::string::npos) url = url.substr(0, slash);
61 if (url.empty()) return false;
62
63 // host:port (gestisce anche [::1]:8080 per IPv6)
64 if (url.front() == '[') {
65 auto rb = url.find(']');
66 if (rb == std::string::npos) return false;
67 out.host = url.substr(1, rb - 1);
68 if (rb + 1 < url.size() && url[rb + 1] == ':') {
69 try { out.port = static_cast<std::uint16_t>(std::stoul(url.substr(rb + 2))); }
70 catch (...) { return false; }
71 }
72 } else {
73 auto colon = url.find(':');
74 if (colon == std::string::npos) {
75 out.host = url;
76 out.port = (out.scheme == "https") ? 443 : 8080;
77 } else {
78 out.host = url.substr(0, colon);
79 try { out.port = static_cast<std::uint16_t>(std::stoul(url.substr(colon + 1))); }
80 catch (...) { return false; }
81 }
82 }
83
84 if (out.host.empty() || out.port == 0) return false;
85 return true;
86}
87
88void collect_env_proxies(std::vector<ProxyEndpoint>& out) {
89 // Le variabili sono case-insensitive su POSIX: cerchiamo entrambe le forme.
90 static const char* kVars[] = {
91 "http_proxy", "HTTP_PROXY",
92 "https_proxy", "HTTPS_PROXY",
93 "all_proxy", "ALL_PROXY",
94 "socks_proxy", "SOCKS_PROXY",
95 };
96 for (const char* var : kVars) {
97 const char* v = std::getenv(var);
98 if (!v || !*v) continue;
100 if (parse_proxy_url(v, ep)) out.push_back(std::move(ep));
101 }
102}
103
104void collect_file_proxies(const fs::path& path, std::vector<ProxyEndpoint>& out) {
105 std::error_code ec;
106 if (!fs::exists(path, ec)) return;
107 std::ifstream f(path);
108 if (!f) return;
109 std::string line;
110 while (std::getline(f, line)) {
111 const auto low = to_lower(line);
112 // Match key=value dove key contiene "_proxy"
113 auto eq = line.find('=');
114 if (eq == std::string::npos) continue;
115 const auto key = to_lower(trim(line.substr(0, eq)));
116 if (key.find("_proxy") == std::string::npos) continue;
117 // Evita ignore_proxy/no_proxy
118 if (key.find("no_proxy") != std::string::npos) continue;
119 ProxyEndpoint ep;
120 auto val = trim(line.substr(eq + 1));
121 // Spesso sono nella forma 'export KEY="val"' — il trim toglie quote.
122 // Per le righe 'export foo=bar' il trim della key ci ha lasciato
123 // "export foo" — accettiamo comunque.
124 if (parse_proxy_url(val, ep)) out.push_back(std::move(ep));
125 }
126}
127
128// Deduplica (host, port, scheme).
129void dedup(std::vector<ProxyEndpoint>& v) {
130 std::sort(v.begin(), v.end(), [](const ProxyEndpoint& a, const ProxyEndpoint& b) {
131 if (a.host != b.host) return a.host < b.host;
132 if (a.port != b.port) return a.port < b.port;
133 return a.scheme < b.scheme;
134 });
135 v.erase(std::unique(v.begin(), v.end(),
136 [](const ProxyEndpoint& a, const ProxyEndpoint& b) {
137 return a.host == b.host && a.port == b.port && a.scheme == b.scheme;
138 }), v.end());
139}
140
141bool host_equals(const std::string& a, const std::string& b) {
142 const auto la = to_lower(a);
143 const auto lb = to_lower(b);
144 if (la == lb) return true;
145 // Alias localhost
146 static const std::vector<std::string> localhosts = {
147 "localhost", "127.0.0.1", "::1", "0.0.0.0"
148 };
149 const bool a_is_local = std::find(localhosts.begin(), localhosts.end(), la) != localhosts.end();
150 const bool b_is_local = std::find(localhosts.begin(), localhosts.end(), lb) != localhosts.end();
151 return a_is_local && b_is_local;
152}
153
154} // namespace
155
156const std::vector<ProxyEndpoint>& detected_proxies() {
157 static std::once_flag once;
158 static std::vector<ProxyEndpoint> cache;
159 std::call_once(once, []() {
160 collect_env_proxies(cache);
161 collect_file_proxies("/etc/environment", cache);
162 if (const char* home = std::getenv("HOME")) {
163 collect_file_proxies(fs::path(home) / ".profile", cache);
164 collect_file_proxies(fs::path(home) / ".bashrc", cache);
165 }
166 dedup(cache);
167 });
168 return cache;
169}
170
171bool matches_configured_proxy(const std::string& host, std::uint16_t port) {
172 for (const auto& p : detected_proxies()) {
173 if (p.port == port && host_equals(host, p.host)) return true;
174 }
175 return false;
176}
177
178} // namespace bsc::ui_util
bool host_equals(const std::string &a, const std::string &b)
bool parse_proxy_url(const std::string &raw, ProxyEndpoint &out)
void dedup(std::vector< ProxyEndpoint > &v)
void collect_file_proxies(const fs::path &path, std::vector< ProxyEndpoint > &out)
void collect_env_proxies(std::vector< ProxyEndpoint > &out)
const std::vector< ProxyEndpoint > & detected_proxies()
bool matches_configured_proxy(const std::string &host, std::uint16_t port)