Skip to main content

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"

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

  1. ServerConfig::ApplyConfiguration() reads the <allow> setting during startup.
  2. Behavior depends on the configured value:
    • If <allow> is not provided: deny all
    • If <allow allowed="">: deny all
    • If <allow allowed="*">: allow all

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

OperationTimeNotes
Exact IP match<0.01msHash lookup
CIDR match<0.1msNetwork calculation
Cached hostname lookup<0.01msCache lookup
Uncached hostname lookup<5msDNS lookup
Cached reverse DNS<0.01msCache lookup
Uncached reverse DNS<10msReverse DNS lookup
Total filtering overhead<0.1msPer 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

  1. Hostnames are resolved at startup, so hostname IP changes require a restart.
  2. Only IPv4 addresses are currently supported.
  3. Filtering overhead is minimal.

Troubleshooting

Connection blocked unexpectedly

  1. Check the IP filtering configuration in hlquery.conf.
  2. Verify that the client IP matches an allowed IP, CIDR range, or hostname.
  3. Check server logs for blocked connection messages.
  4. Ensure hostnames resolve correctly.

Hostname not resolving

  1. Verify DNS with nslookup hostname.example.com.
  2. Check server logs for resolution errors.
  3. Use IP addresses directly if DNS is unreliable.

Testing

  1. Configure localhost-only access: <allow allowed="127.0.0.1">
  2. Restart the server
  3. Connect from localhost
  4. 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