1. Overview
The WebProbe module provides lightweight, local-only URL evaluation for the dnsmasq edition of BastionGuard. Its role is to decide—quickly and deterministically—whether a given URL should be:
- Allowed unchanged (normal navigation), or
- Redirected to a local warning page hosted by LocalWarningServer
In this simplified build, WebProbe relies on:
- A local host blocklist (file-based)
- A short-lived hot cache for verdict reuse
- A bank-domain allow policy via
Backend::instance().isBankDomain() - Optional DNS-based signaling via
AsyncDNSResolver::resolve_cached()(e.g., dnsmasq mapping to127.0.0.2)
Remote reputation checks (e.g., Google Safe Browsing) are intentionally out of scope for this edition.
2. Responsibilities and Scope
This module is responsible for:
- Extracting and normalizing the host component from a raw URL or host string
- Loading a persistent blocklist from known locations
- Evaluating hosts against explicit rules and wildcard subdomain patterns
- Maintaining a short-lived in-memory cache to reduce repeated work
- Building a deterministic redirect URL to a local warning server
- Exposing a callback registration hook for GUI notification integration
3. Internal Data Model and Caching
3.1 Blocklist Storage
Blocked hosts are stored in-memory as:
static std::unordered_set<std::string> g_blocklist
Entries are normalized to lowercase and converted to ASCII (Punycode) when applicable (IDNA), ensuring consistent matching across internationalized domains.
3.2 Hot Verdict Cache (TTL)
To reduce repeated evaluation work during bursts of navigation requests, WebProbe uses a “hot cache” with a short TTL:
static std::unordered_map<std::string, std::pair<bool,uint64_t>> g_hotstatic constexpr uint64_t TTL_MS = 15000(15 seconds)
The cache key is the normalized host. The cached value contains:
- blocked flag (
true= redirect to warning) - expiration timestamp in milliseconds
Concurrency is handled using a shared mutex:
static std::shared_mutex g_hot_mtx
Reads use std::shared_lock, writes use std::unique_lock.
4. Time Base Utility
Time is computed using a monotonic clock to avoid wall-clock issues:
now_ms()usesstd::chrono::steady_clock
This makes TTL expiration robust against system time changes.
5. Path Expansion and IDNA Normalization
5.1 User Path Expansion
Blocklist lookup supports user-relative paths through expand_user_path(rel), which prefixes the user home directory from:
Glib::get_home_dir()
5.2 IDNA ASCII Conversion
To properly support internationalized domain names (IDN), WebProbe converts hosts to ASCII using libidn2:
to_ascii_idna(host)usesidn2_lookup_ul()
If conversion fails, the original host string is returned unchanged.
6. Blocklist Loading and Matching
6.1 Blocklist File Locations
On initialization (and on demand reload), WebProbe loads blocked host patterns from the first available path among:
- Optional hint passed to the constructor
- User path:
~/.local/share/BastionGuard/blocklist_hosts.txt - System path:
/etc/BastionGuard/blocklist_hosts.txt
On success, the module logs the chosen source and the number of domains loaded. If no file is found, it logs that no blocklist is available.
6.2 Entry Parsing
While loading:
- Whitespace is removed from each line
- Empty lines are ignored
- Domains are normalized:
- IDNA ASCII conversion (Punycode)
- Lowercasing
6.3 Wildcard Pattern Matching
WebProbe supports a limited wildcard syntax for subdomains:
*.example.com
Matching rules:
host == example.commatchessub.example.commatches- Only the
*.prefix form is recognized (no full glob patterns)
If a pattern does not start with *., matching falls back to exact equality.
7. URL Host Extraction
The extract_host(raw) routine normalizes the input and extracts a host using:
- Whitespace trimming
- Scheme stripping (
http://,https://,ftp://) - Credential stripping (
user@host) - Removal of path/query/fragment suffix
- Numeric port stripping (
:443) - Trailing dot removal
- IDNA ASCII conversion and lowercasing
This ensures that the evaluation pipeline operates on a stable canonical host string.
8. Local Warning Redirect Builder
8.1 Redirect URL Format
When a URL is blocked, WebProbe redirects the navigation to a local warning endpoint:
http://127.0.0.2:<warning_port>/?target=<original_url>
The warning port is supplied via constructor (warning_port_). The original URL is minimally encoded (spaces mapped to %20).
9. Core Decision Logic
9.1 Main Entry Point
The decision engine is implemented by:
check_url(url)
It returns:
- The original
urlif allowed, or - The local warning redirect URL if blocked
9.2 Evaluation Order
The evaluation pipeline is deliberately ordered for performance and policy correctness:
- Input validation (empty URL / empty host → allow)
- Hot cache lookup (return cached allow/block decision if not expired)
- Bank-domain allow (always allow via
Backend::instance().isBankDomain(host)) - Blocklist match (explicit patterns → block)
- DNS signal check (cached DNS resolution returns
127.0.0.2→ block) - Default allow (store allow verdict in hot cache)
9.3 DNS-Based Blocking Signal
Even if the host is not explicitly in the blocklist, WebProbe checks the local resolver cache:
AsyncDNSResolver::instance().resolve_cached(host)
If the cached IP equals 127.0.0.2, the URL is treated as blocked and redirected to the warning page. This enables an enforcement strategy where dnsmasq maps undesirable domains to a local sink address that triggers the UI warning page flow.
10. GUI Notification Integration
10.1 Callback Registration
The module exposes a callback hook for UI integration:
register_callback(NotifyCb cb)
The callback assignment is protected by an instance-level shared mutex:
std::shared_mutex mutex_(member)
This allows GUI layers to register for notifications in a thread-safe manner. The simplified excerpt provided defines the registration mechanism; callback invocation is expected elsewhere in the full class implementation.
11. Blocklist Reload
Blocklists can be reloaded at runtime using:
reload_blocklist(path)
This re-executes the same multi-path lookup logic and repopulates the in-memory g_blocklist set.
12. Runtime and Security Considerations
- Local-only enforcement: no remote reputation queries are executed in this edition.
- Performance: the 15-second hot cache minimizes repeated matching and resolver lookups during navigation bursts.
- Policy hardening: bank domains are explicitly allowed before any blocking check to avoid accidental checkout disruption.
- IDN safety: IDNA conversion reduces homograph and normalization inconsistencies in matching logic.
- Deterministic behavior: fixed evaluation order ensures predictable results across modules.
- Encoding limitation: redirect encoding replaces spaces only; if full URL encoding is required, a dedicated encoder should be introduced.
- Thread safety: shared mutexes protect hot cache access; blocklist access is protected by a standard mutex.