IP Filtering
hlquery supports IP address filtering with DNS resolution to restrict access to the server.
Configuration
IP allow filtering is configured in hlquery.conf using the <allow> tag:
<allow allowed="*">
Parameters
allowed: Comma-separated list of allowed IP addresses and/or hostnames- Use
"*"to allow all IPs (default) - Use specific IPs:
"192.168.1.1,10.0.0.5" - Use hostnames (resolved via DNS):
"api.example.com,internal.server" - Mix IPs and hostnames:
"192.168.1.1,api.example.com,10.0.0.5"
- Use
Examples
Allow All IPs
<allow allowed="*">
Allows connections from any IP address.
Allow Only Localhost
<allow allowed="127.0.0.1">
Allows connections only from localhost.
Allow Specific IP Addresses
<allow allowed="192.168.1.1,10.0.0.5,172.16.0.10">
Allows only the listed IP addresses.
Allow Hostname
<allow allowed="api.example.com">
The hostname is resolved to one or more IP addresses at startup.
Mix IPs and Hostnames
<allow allowed="192.168.1.1,api.example.com,10.0.0.5,internal.server.local">
You can mix IP addresses and hostnames in the same configuration.
How It Works
Configuration Parsing
ServerConfig::ApplyConfiguration()reads the<allow>setting during startup.- Behavior depends on the configured value:
- If
<allow>is not provided: deny all - If
<allow allowed="">: deny all - If
<allow allowed="*">: allow all
- If
DNS Resolution Strategy
IP-only configuration
<allow allowed="192.168.1.1,10.0.0.5">
- DNS resolution is disabled
- No DNS overhead
- Logs show IP addresses only
Hostname configuration
<allow allowed="api.example.com,internal.server">
- Hostnames are resolved once at startup with
getaddrinfo() - Resolved IPs are cached
- Logs can include both IPs and hostnames
Wildcard hostname configuration
<allow allowed="*.example.com">
- Reverse DNS lookup is used on connection
- Results are cached
- Hostnames are matched against wildcard patterns
Mixed configuration
<allow allowed="192.168.1.1,api.example.com,192.168.1.0/24,*.internal.com">
- IPs are matched directly
- Hostnames are resolved at startup
- CIDR ranges are parsed and stored
- Wildcards use cached reverse DNS on connection
DNS Caching
Forward DNS cache
- Purpose: hostname to IP resolution
- Storage:
std::unordered_map<std::string, std::vector<std::string>> - Size limit: 1024 entries
Reverse DNS cache
- Purpose: IP to hostname resolution for wildcard matching
- Storage:
std::unordered_map<std::string, std::string> - Size limit: 1024 entries
Cache flushing
- DNS caches are flushed every hour
- This keeps hostname resolution reasonably fresh
Connection Filtering Flow
1. Client connects and server reads the client IP
2. Check whether IP filtering is enabled
3. Match exact IPs, CIDR ranges, and wildcard hostnames
4. If needed, resolve or reverse-resolve using the cache
5. Allow the connection or return HTTP 403
Thread Safety
- Cache access is protected by
cache_mutex_ - Reads use minimal locking
- Connection checks rely on fast hash lookups
Performance
| Operation | Time | Notes |
|---|---|---|
| Exact IP match | <0.01ms | Hash lookup |
| CIDR match | <0.1ms | Network calculation |
| Cached hostname lookup | <0.01ms | Cache lookup |
| Uncached hostname lookup | <5ms | DNS lookup |
| Cached reverse DNS | <0.01ms | Cache lookup |
| Uncached reverse DNS | <10ms | Reverse DNS lookup |
| Total filtering overhead | <0.1ms | Per connection |
Logging
IP filtering emits logs for:
- Initialization
- Hostname resolution
- Blocked connections
- Accepted connections at debug level
Example log output
[ip_allow] IP allow filter initialized with 3 IP(s), 0 CIDR range(s), 1 hostname(s), 0 wildcard hostname(s) (DNS resolution enabled)
[ip_allow] Resolved hostname api.example.com -> 192.168.1.100
[httpserver] Connection blocked from IP: 192.168.1.200 (unauthorized.example.com) (IP not allowed)
[httpserver] Accepted HTTP connection from 192.168.1.100:54321 (api.example.com)
If DNS is disabled, logs include IP addresses only.
Security Considerations
- Hostnames are resolved at startup, so hostname IP changes require a restart.
- Only IPv4 addresses are currently supported.
- Filtering overhead is minimal.
Troubleshooting
Connection blocked unexpectedly
- Check the IP filtering configuration in
hlquery.conf. - Verify that the client IP matches an allowed IP, CIDR range, or hostname.
- Check server logs for blocked connection messages.
- Ensure hostnames resolve correctly.
Hostname not resolving
- Verify DNS with
nslookup hostname.example.com. - Check server logs for resolution errors.
- Use IP addresses directly if DNS is unreliable.
Testing
- Configure localhost-only access:
<allow allowed="127.0.0.1"> - Restart the server
- Connect from localhost
- Confirm a different machine is blocked
API
The IP filter can also be accessed programmatically:
if (Instance->IPFilter->IsEnabled()) {
// IP filtering is active
}
if (Instance->IPFilter->IsAllowed("192.168.1.1")) {
// IP is allowed
}
auto allowed_ips = Instance->IPFilter->GetAllowedIPs();
Future Enhancements
- IPv6 support
- Dynamic reloading without restart
- Per-endpoint IP filtering
- Rate limiting per IP
- IP whitelist and blacklist files