dns-proxy, test-dns — Domain Name System (DNS) proxy


dns-proxy [-hv] [-d dbglev] -f cfgfile

test-dns [-hv] [-d dbglev] -f cfgfile [-t test_expr]


The dns-proxy provides proxying service for the Domain Name System (RFC 1034+1035 and some of their extensions).

The proxy handles incoming requests in four basic modes:

Request is denied.

Request is not processed at all. Administrator can choose from several ways of request denial.

Request is faked.

Request is responded without querying any other server. The faked response is set by administrator.

Request is forwarded.

Request is resent to one from group of servers defined by the administrator. This mode is typical for

  • forwarding queries from host not fully connected to the internet

  • forwarding queries to server hidden in private network

  • forwarding zone transfers across the firewall.

Request is resolved.

This is fully functional mode when the proxy tries to recursively resolve the request form the scratch. It starts from the root servers and accepts only authoritative answers until it gets the final authoritative answer.

The proxy is not designed as a server, neither for external queries to our domain, nor as a local caching name server. Typical scenario is that all clients ask another server in the internal network and this server queries the proxy (if needed) and caches the answers. That's why the proxy does not cache answers, there is only a small cache of name servers for internal purpose only.

The proxy behaves like a server for the client and vice versa, with full syntax and semantics verification (see below, protection against cache-poisoning). When querying other servers, dns-proxy uses random source port and ID (protection against reply-spoofing).

The proxy completely reconstructs answers from servers and limits the size of reply sent to client (configurable by ptr-reply-size and adr-reply-size items). This feature protects clients against to attack based on buggy resolver routines.

The proxy usually runs as two processes: the single child process manages all the sessions and the parent process manages the child and restarts it after a failure. You can learn more in udpserver(7) manual page, although the dns-proxy does not use the udpserver library, in fact. However, it uses the same operation logic.

Format of the proxy configuration file is described in dns-proxy.cfg(5). For the dns-proxy, any host in configuration file must be specified by its address, not by its name.

Program test-dns tests syntax and partially semantics of configuration; for test expression syntax, see test-expr(5).

Startup and Configuration

The proxy reads its configuration file and starts listening on specified IP sockets (address/port couples), as specified in the listen-on configuration section (see listen-on(5)). Proxy listens for both UDP and TCP protocols.

If support of transparent connections (see transparency(7)) is requested by item transparent in section listen-on, the corresponding NAT redirections are established during proxy startup and removed upon exit. However, transparent connections are in fact not supported by this proxy, decisions about servers are made according to the proxy configuration, not by original destination.


In the case of UDP requests redirected to the proxy by more general NAT rules, the real destination is neither being detected nor used in ACL selection.

In the resolve mode, the proxy checks each address being stored into cache whether it matches to one of addresses, the proxy listens on. If it does, the record is not stored, so that this does not lead to infinite loop. For this reason, listening addresses (for port 53) must be specified explicitly, expression []:53 is not allowed.

DNS Subset

Current version of the dns-proxy implements following subset of protocol:


    QUERY  RFC 1035
    NOTIFY RFC 1996

Requests with unimplemented OPCODEs are replied with the NotImp response code. Requests with unknown OPCODEs are replied with the FormErr response code.


    IN     RFC 1035
    *      RFC 1035

Requests with query resource record (RR) of unimplemented CLASS are replied with the NotImp response code, unknown classes cause the FormErr response code. The '*' requests are converted to IN, resolved and then sent to the client with authority flag set to 0.


    A          RFC 1035
    AAAA       RFC 3596
    AFSDB      RFC 1183
    AXFR       RFC 1035
    CNAME      RFC 1035
    DNSKEY     RFC 4034
    DS         RFC 4034
    HINFO      RFC 1035
    IXFR       RFC 1995
    MX         RFC 1035
    NS         RFC 1035
    NSEC       RFC 4034
    NSEC3      RFC 5155
    OPT        RFC 6891
    PTR        RFC 1035
    RRSIG      RFC 4034
    SOA        RFC 1035
    SPF        RFC 4408
    SRV        RFC 2782
    SSHFP      RFC 4255
    TXT        RFC 1035
    *          RFC 1035

Requests with query RR of unimplemented TYPE are by default replied with the NotImp response code. This behavior can be changed in the configuration (ACL settings). Requests to unknown TYPE are replied with the FormErr response code.


Every request is stored into a table item containing all necessary information. Size of this table must be specified in configuration (requests-table-size item) and it is recommended to reserve a little more items then estimated number of parallel requests. Some requests processed in resolve mode can generate so called internal requests (see below) that occupy table items, too.

Besides number of requests, number of simultaneously opened sockets is monitored. The maximum of sockets must be specified in the configuration (sockets-table-size item).

There are two kinds of internal requests:

CACHE requests

If the proxy is to ask some server, address of which is not in the cache, it generates an internal requests with A and AAAA query for the name of the server. This request is handled in the same way as the original query with the exception that the result is stored in the cache.

Choosing just IPv4 or IPv6 protocol when querying servers can be done by the server-proto item. The default is using both of them, or using just the IPv4 when no system interface has an IPv6 address defined.

CNAME requests

If the answer got from a server contains CNAME RR and no (trusted) RR for the canonical name, proxy generates an internal request with the same query RR type and query name equal to the canonical name received. This request is handled in the same way as the original query with the exception that the result is added to the previously received RRs. If the internal request fails to complete the resolution, the original request is replied by the ServFail response code.

Both types of internal requests suspend processing of the original request (originator) until the internal request is completed. If another request is to generate a new internal request of the same subject as another running one, no new internal request is created and the originator is suspended waiting for the first internal request, too. After (successful or unsuccessful) completion of internal request, all originators are waked up.

Similar principle is used also for client requests. If a request with the same parameters (query name, query type, EDNS UDP payload etc.) is already being processed, new request is also suspended, waiting for the result of the previous request.

Both types of internal requests can also generate new internal requests. For instance, in following definition:

    domain1 IN NS ns.domain2
    domain2 IN NS ns.domain3

a request to the domain1 will generate request to the ns.domain2 name and solution of this will result to a new CACHE request to ns.domain3. Proxy detects an infinite loop, if occurs. Proxy also limits number of internal requests generated by one internal request in the row (item internal-request-depth) to prevent DoS attack by means of non-infinite but very long loop of references.

Both types of internal requests respect the resolving policy according to the query name and type given by the configuration. For every new request, the request-acl list (see below) is searched through and the new ACL defines operation to be used.

If no free request table item is available,

  • incoming UDP requests are replied with the ServFail response code

  • incoming TCP requests are rejected by closing the connection

  • internal requests of any type fail and these failures are propagated to all originators.

Access Control Lists

The proxy uses two layers of ACL (see access-control(7)) named session-acl and request-acl.

When a request arrives, configuration is consulted, proper session-acl is selected and according to it, request is served or not.

Subsequently, protocol-specific parameters of query is checked against set of request-acl entry conditions and proper mode of operation is selected.

Additionally to the general Kernun ACL concept, request-acl brings new entry condition items:


This item contains a set of regular expressions and/or strings describing names, querying for which is to be dealt by this request-acl. When using the regexp form, you have to respect the dot placed to the end of every name before request processing. The queried name matches a member of the set if

  • matches the regexp (regexp case) or

  • is part of the domain (string case).


  • Regular expression /^[^.]*\.tns\.cz\.$/ matches queries to all hosts in the domain

  • String matches e.g. queries to, or, but not


This item can define subset of DNS operation codes and RR types that is to be dealt by this request-acl.

By these two items, a detail selection of request-acl can be done to set special handling for different tasks like regular queries, zone transfers, server notifications etc.

If no matching ACL is found, request is replied by the Refused response code. If ACL is found, query type and class are checked. Requests with classes other than IN and * (ANY class) are rejected with response code NotImp.

As we stated above, there are several possible proxy operations. The proxy decides among them by matching query RR type against a set of special request-acl items query/notify. The first matching item is used and the operation is executed. If no proper item is found, request is rejected with the Refused response code.

In case of resolve and forward operations, request is resent (with a new, random ID) to a new server. The set of possible responders is defined in a global section of ns-list type. Each received reply RR is then checked against a set of special ACL items called reply in the same manner as queried RR is checked. The reply items can tune handling of particular RR (permit, remove), so as even predefine reaction to the whole request (abort, deny). If no proper reply item is found, request is rejected with the Refused response code. If permit action is required for non-implemented RR, record is removed.

After filtering the response from server, other proper RRs (given by special fake items) can be added to the answer. The same set of items is used for reply construction in case of fake operation. Fake RRs are placed into the answer in the order of appearance in the configuration.

After completing all answer RRs, reply is completely reconstructed and sent to the client. The resolved requests will have the authority (AA) flag cleared while for the forwarded requests, the admin can choose whether to preserve or clear the flag (see the request-acl.query.clear-aa definition in dns-proxy(5)).

If the response has the NXDomain response code, or the NoError response code with no answer (AN) records, and if this status was caused by the proxy (e.g. due to denying query or filtering response), the proxy will add, by default, a SOA record with proper TTL for successful negative caching in clients. This behavior can be configured by the neg-resp-ttl item of the particular request-acl.


If the authoritative answer in resolve operation is not available, request is replied with the ServFail response code. There are some situations where this approach is not applicable. For instance, queries to domain end by non-authoritative answer. We recommend using a special request-acl for this case, forwarding requests of this type directly to proper name servers.

Syntax and Semantics Verification

Besides Security Policy application, the proxy checks both queries and replies to correctness in sense of relevant RFCs.


Names can be at most 254 bytes long, every label can be at most 63 bytes long. Labels can contain only alphanumerical characters and a hyphen ('-'). We allow also underscore ('_') and slash ('/') because they are commonly used.


Request ID of the answer is checked to be equal to the ID of query. Query (QD) section of the answer is checked to be equal to the query. Every answer (AN) RR must be relevant to the query or to the previous RR. Every authority (NS) RR must be relevant to the query or to the canonical name of some AN RR. Every additional (AR) RR must be relevant to some AN or NS RR.


Every server is introduced into the cache as an authoritative name server for some domain. So, all answer RRs received from this server are trusted only if they belong to this domain or some subdomain. This criteria may cause an infinite loop when two or more domains refer each other without having any regular glue record (i.e. nameserver in own domain or a subdomain). If you need to accept such a domain, you must make an ACL for this domain forwarding requests directly to proper nameservers.


Name server cache is used for increased efficiency, namely for repeated queries to the same domain (TLDs, resending query via TCP, resolution of CNAMEs etc.). Root servers for different network zones are defined in ns-list configuration sections, each zone has a separated cache zone named by the name of ns-list section. All other name servers and their addresses are introduced to the cache as a result of authoritative answers. Authoritative server (in sense of dns-proxy) is either a root server or a server delegated by some authoritative server for a parent domain (already being in the cache). All new RRs are used with respect of their time to live (TTL) value. When the minimal of TTLs of name servers (both their NS and address records) for a domain expires, the domain item is unusable and it is removed from the cache at nearest cleanup. Similarly, when the minimal of TTLs of addresses for a host expires, the host item is unusable and it is removed from the cache at nearest cleanup.

Some properties of the cache are configurable in a special cache section:


sets the period (in seconds) of cache cleaning up. After the period, all items that were not used within the period and all expired items are removed.


sets the maximum number of domains (not individual NS RRs) stored in the cache. This value should be at least as large as requests-table-size.


sets the maximum number of hosts (not individual address RRs) stored in the cache. This value should be at least approximately five times larger than requests-table-size.

If any maximum is reached, a non-periodical cleanup is started. This cleanup removes all items currently not used.

Server Selection Algorithm

When a domain name is to be resolved, the longest match search in the cache is done. After it, the new best server for the domain found is chosen. Server comparison criteria:

  1. Resolved, but never contacted servers.

  2. Unresolved servers, never tried to be resolved.

  3. Responding servers (sorted by response time rounded to entire seconds).

  4. Non-resolved or non-responding servers (sorted by time of the last attempt).

This algorithm guarantees a primitive "load balancing" and error recovery of multiple servers.

When querying for an EDNS request, the EDNS servers have priority. If a server responds FormErr to an EDNS query, the non-EDNS query is repeated immediately.

First of all, the selected server is queried with a very short (1 sec.) timeout. When this timeout fires, the query is simply repeated to avoid errors caused by loosing UDP packets.

Then the queried server has a longer timeout for the response. Each server has its own timeout stored in the cache. Starting value of this timeout is set by query-timeout configuration directive. Each time the server does not respond within the timeout, the timeout doubles, up to server-dead value. When the timeout reaches this value, server is marked as dead and a new attempt to contact it cannot be done until server-retry seconds period. If the server responds within the timeout, his timeout for the next attempt is set to his response time plus query-timeout.

The number of attempts per one query is hardcoded to eight (regardless of which servers were queried). However, typically this number is not reached before firing the request-timeout (see below).

The same mechanism is used for selection among forwarders. That's why forwarders lists are also stored in the cache (every list in its separated zone).

The timeout for the whole request processing (including resolving of generated internal requests etc.) is also set in the configuration (request-timeout) and if reached, request is replied with the ServFail response code.

Zone Transfers

Zone transfers need some more special handling. First of all, the requests are typically addressed by originators directly to a conrete server. That's why either the transparent mode (if public addresses are used), or non-transparent mode to a dedicated address/port on firewall (in the case of server on a private address) should be used. Also, besides QUERY operation, the NOTIFY operation should be permitted.

Moreover, the own transfers (responses to the AXFR/IXFR queries) should be sent by servers either separated (i.e. more DNS messages with one RR in each one) or aggregated (i.e. all RRs in one DNS message). The proxy can force one of these methods, or keep the incoming format according to the xfr-format configuration directive.

Configuration example:

  ns-list MASTER {
    server ns.x.y.z [];
  ns-list SLAVE {
    server sns.x.y.z [];
  dns-proxy ZONE-TRANSFERS {
    request-acl TO-MASTER {
      to non-transparent []; # special external fw adr
      query { axfr, ixfr } forward MASTER;
    request-acl TO-SLAVE {
      to transparent [];
      notify forward SLAVE;

Common Kernun Features

The proxy uses common Kernun mechanism for listening on its sockets, optionally changing root directory and running with alternative user privileges. For more detailed information, see application(5) and listen-on(5).

The proxy uses common Kernun mechanism for network input/output operations. Configuration allows for specifying several parameters like buffer sizes and timeouts, both for client and server connections. They can be included in client-conn and server-conn configuration sections, respectively. For more detailed information, see netio(7).

The proxy uses common Kernun mechanism for logging. For more detailed information, see logging(7). For every request, one REQUEST (DNSP-860-I) message is logged (besides one or two ACL messages - DNSP-810-I and DNSP-820-I). Every log message has process ID suffix equal to the index of request being currently processed.

The proxy, in fact, does not use common Kernun mechanism for name resolving (see resolving(7) manual page), because it does not use DNS names at all. In spite of that, the item use-resolver remains in the dns-proxy configuration for compatibility with other proxies (and thus e.g. ability to use common cml(8) variables).


The dns-proxy handles following signals:


Log level increasing.


Log level decreasing.


Operation status logging; parent process logs info about all children, child process dumps cache content and requests table content.


Immediate termination; proxy immediately closes all connections and terminates.

Program options

The program options are as follows:


Print usage information and exit.


Display version information and exit.

-d dbglev

Set debuging level to a specific number. Permitted values are 3 through to 9, 3 being the least and 9 the most verbose. See logging(7) for details. This setting is relevant only till configuration reading is finished.

-f cfgfile

Read cfgfile for configuration information.

-t test-expr

Test configuration according to given expression. Format of the test-expr is described in test-expr(5).


Currently, the dns-proxy doesn't implement following features:

  • more queries (QD RRs) in one request

  • wildcard ('*') queries.

See Also


This man page is a part of Kernun Firewall.
Copyright © 2000–2023 Trusted Network Solutions, a. s.
All rights reserved.