Home --> Documentations --> PJLIB-UTIL Reference

DNS Asynchronous/Caching Resolution Engine

Data Structures

struct  pj_dns_settings
struct  pj_dns_a_record
struct  pj_dns_addr_record


typedef struct pj_dns_resolver pj_dns_resolver
typedef struct pj_dns_async_query pj_dns_async_query
typedef void pj_dns_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *response)


void pj_dns_settings_default (pj_dns_settings *s)
pj_status_t pj_dns_resolver_create (pj_pool_factory *pf, const char *name, unsigned options, pj_timer_heap_t *timer, pj_ioqueue_t *ioqueue, pj_dns_resolver **p_resolver)
pj_status_t pj_dns_resolver_set_ns (pj_dns_resolver *resolver, unsigned count, const pj_str_t servers[], const pj_uint16_t ports[])
pj_status_t pj_dns_resolver_get_settings (pj_dns_resolver *resolver, pj_dns_settings *st)
pj_status_t pj_dns_resolver_set_settings (pj_dns_resolver *resolver, const pj_dns_settings *st)
void pj_dns_resolver_handle_events (pj_dns_resolver *resolver, const pj_time_val *timeout)
pj_status_t pj_dns_resolver_destroy (pj_dns_resolver *resolver, pj_bool_t notify)
pj_status_t pj_dns_resolver_start_query (pj_dns_resolver *resolver, const pj_str_t *name, int type, unsigned options, pj_dns_callback *cb, void *user_data, pj_dns_async_query **p_query)
pj_status_t pj_dns_resolver_cancel_query (pj_dns_async_query *query, pj_bool_t notify)
pj_status_t pj_dns_parse_a_response (const pj_dns_parsed_packet *pkt, pj_dns_a_record *rec)
pj_status_t pj_dns_parse_addr_response (const pj_dns_parsed_packet *pkt, pj_dns_addr_record *rec)
pj_status_t pj_dns_resolver_add_entry (pj_dns_resolver *resolver, const pj_dns_parsed_packet *pkt, pj_bool_t set_ttl)
unsigned pj_dns_resolver_get_cached_count (pj_dns_resolver *resolver)
void pj_dns_resolver_dump (pj_dns_resolver *resolver, pj_bool_t detail)

Detailed Description

This module manages the host/server resolution by performing asynchronous DNS queries and caching the results in the cache. It uses PJLIB-UTIL low-level DNS parsing functions (see DNS and Asynchronous DNS Resolver) and currently supports several types of DNS resource records such as A record (typical query with gethostbyname()) and SRV record.


Asynchronous Query and Query Aggregation

The DNS queries are performed asychronously, with timeout setting configured on per resolver instance basis. Application can issue multiple asynchronous queries simultaneously. Subsequent queries to the same resource (name and DNS resource type) while existing query is still pending will be merged into one query, so that only one DNS request packet is issued.

Query Retransmission

Asynchronous query will be retransmitted if no response is received within the preconfigured time. Once maximum retransmission count is exceeded and no response is received, the query will time out and the callback will be called when error status.

Response Caching with TTL

The resolver instance caches the results returned by nameservers, to enhance the performance by minimizing the message round-trip to the server. The TTL of the cached resposne is calculated from minimum TTL value found across all resource record (RR) TTL in the response and further more it can be limited to some preconfigured maximum TTL in the resolver.

Response caching can be disabled by setting the maximum TTL value of the resolver to zero.

Parallel and Backup Name Servers

When the resolver is configured with multiple nameservers, initially the queries will be issued to multiple name servers simultaneously to probe which servers are not active. Once the probing stage is done, subsequent queries will be directed to only one ACTIVE server which provides the best response time.

Name servers are probed periodically to see which nameservers are active and which are down. This probing is done when a query is sent, thus no timer is needed to maintain this. Also probing will be done in parallel so that there would be no additional delay for the query.

Supported Resource Records

The low-level DNS parsing utility (see DNS and Asynchronous DNS Resolver) supports parsing of the following DNS resource records (RR):

  • DNS A record
  • DNS SRV record
  • DNS PTR record
  • DNS NS record
  • DNS CNAME record

For other types of record, application can parse the raw resource record data (rdata) from the parsed DNS packet (pj_dns_parsed_packet).

Using the Resolver

To use the resolver, application first creates the resolver instance by calling pj_dns_resolver_create(). If application already has its own timer and ioqueue instances, it can instruct the resolver to use these instances so that application does not need to poll the resolver periodically to process events. If application does not specify the timer and ioqueue instance for the resolver, an internal timer and ioqueue will be created by the resolver. And since the resolver does not create it's own thread, application MUST poll the resolver periodically by calling pj_dns_resolver_handle_events() to allow events (network and timer) to be processed.

Next, application MUST configure the nameservers to be used by the resolver, by calling pj_dns_resolver_set_ns().

Application performs asynchronous query by submitting the query with pj_dns_resolver_start_query(). Once the query completes (either successfully or times out), the callback will be called.

Application can cancel a pending query by calling pj_dns_resolver_cancel_query().

Resolver must be destroyed by calling pj_dns_resolver_destroy() to release all resources back to the system.

Resolver Limitations

Current implementation mainly suffers from a growing memory problem, which mainly is caused by the response caching. Although there is only one cache entry per {query, name} combination, these cache entry will never get deleted since there is no timer is created to invalidate these entries. So the more unique names being queried by application, there more enties will be created in the response cache.

Note that a single response entry will occupy about 600-700 bytes of pool memory (the PJ_DNS_RESOLVER_RES_BUF_SIZE value plus internal structure).

Application can work around this problem by doing one of these:

  • disable caching by setting PJ_DNS_RESOLVER_MAX_TTL and PJ_DNS_RESOLVER_INVALID_TTL to zero.
  • periodically query pj_dns_resolver_get_cached_count() and destroy- recreate the resolver to recycle the memory used by the resolver.

Note that future improvement may solve this problem by introducing expiration timer to the cached entries.


The PJLIB-UTIL resolver was built from the information in the following standards:

Typedef Documentation

◆ pj_dns_async_query

Opaque data type for asynchronous DNS query object.

◆ pj_dns_callback

typedef void pj_dns_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *response)

Type of asynchronous callback which will be called when the asynchronous query completes.

user_dataThe user data set by application when creating the asynchronous query.
statusStatus of the DNS resolution.
responseThe response packet received from the server. This argument may be NULL when status is not PJ_SUCCESS.

◆ pj_dns_resolver

Opaque data type for DNS resolver object.

Function Documentation

◆ pj_dns_parse_a_response()

pj_status_t pj_dns_parse_a_response ( const pj_dns_parsed_packet pkt,
pj_dns_a_record rec 

A utility function to parse a DNS response containing A records into DNS A record.

pktThe DNS response packet.
recThe structure to be initialized with the parsed DNS A record from the packet.
PJ_SUCCESS if response can be parsed successfully.

◆ pj_dns_parse_addr_response()

pj_status_t pj_dns_parse_addr_response ( const pj_dns_parsed_packet pkt,
pj_dns_addr_record rec 

A utility function to parse a DNS response containing AAAA records into DNS AAAA record.

pktThe DNS response packet.
recThe structure to be initialized with the parsed DNS AAAA record from the packet.
PJ_SUCCESS if response can be parsed successfully.

◆ pj_dns_resolver_add_entry()

pj_status_t pj_dns_resolver_add_entry ( pj_dns_resolver resolver,
const pj_dns_parsed_packet pkt,
pj_bool_t  set_ttl 

Put the specified DNS packet into DNS cache. This function is mainly used for testing the resolver, however it can also be used to inject entries into the resolver.

The packet MUST contain either answer section or query section so that it can be indexed.

resolverThe resolver instance.
pktDNS packet to be added to the DNS cache. If the packet matches existing entry, it will update the entry.
set_ttlIf the value is PJ_FALSE, the entry will not expire (so use with care). Otherwise cache expiration will be calculated based on the TTL of the answeres.
PJ_SUCCESS on success, or the appropriate error code.

◆ pj_dns_resolver_cancel_query()

pj_status_t pj_dns_resolver_cancel_query ( pj_dns_async_query query,
pj_bool_t  notify 

Cancel a pending query.

queryThe pending asynchronous query to be cancelled.
notifyIf non-zero, the callback will be called with failure status to notify that the query has been cancelled.
PJ_SUCCESS on success, or the appropriate error code,

◆ pj_dns_resolver_create()

pj_status_t pj_dns_resolver_create ( pj_pool_factory pf,
const char *  name,
unsigned  options,
pj_timer_heap_t timer,
pj_ioqueue_t ioqueue,
pj_dns_resolver **  p_resolver 

Create DNS resolver instance. After the resolver is created, application MUST configure the nameservers with pj_dns_resolver_set_ns().

When creating the resolver, application may specify both timer heap and ioqueue instance, so that it doesn't need to poll the resolver periodically.

pfPool factory where the memory pool will be created from.
nameOptional resolver name to identify the instance in the log.
optionsOptional options, must be zero for now.
timerOptional timer heap instance to be used by the resolver. If timer heap is not specified, an internal timer will be created, and application would need to poll the resolver periodically.
ioqueueOptional I/O Queue instance to be used by the resolver. If ioqueue is not specified, an internal one will be created, and application would need to poll the resolver periodically.
p_resolverPointer to receive the resolver instance.
PJ_SUCCESS on success, or the appropriate error code,

◆ pj_dns_resolver_destroy()

pj_status_t pj_dns_resolver_destroy ( pj_dns_resolver resolver,
pj_bool_t  notify 

Destroy DNS resolver instance.

resolverThe resolver object to be destryed
notifyIf non-zero, all pending asynchronous queries will be cancelled and its callback will be called. If FALSE, then no callback will be called.
PJ_SUCCESS on success, or the appropriate error code,

◆ pj_dns_resolver_dump()

void pj_dns_resolver_dump ( pj_dns_resolver resolver,
pj_bool_t  detail 

Dump resolver state to the log.

resolverThe resolver instance.
detailWill print detailed entries.

◆ pj_dns_resolver_get_cached_count()

unsigned pj_dns_resolver_get_cached_count ( pj_dns_resolver resolver)

Get the total number of response in the response cache.

resolverThe resolver instance.
Current number of entries being stored in the response cache.

◆ pj_dns_resolver_get_settings()

pj_status_t pj_dns_resolver_get_settings ( pj_dns_resolver resolver,
pj_dns_settings st 

Get the resolver current settings.

resolverThe resolver instance.
stBuffer to be filled up with resolver settings.
The query timeout setting, in seconds.

◆ pj_dns_resolver_handle_events()

void pj_dns_resolver_handle_events ( pj_dns_resolver resolver,
const pj_time_val timeout 

Poll for events from the resolver. This function MUST be called periodically when the resolver is using it's own timer or ioqueue (in other words, when NULL is specified as either timer or ioqueue argument in pj_dns_resolver_create()).

resolverThe resolver instance.
timeoutMaximum time to wait for event occurence. If this argument is NULL, this function will wait forever until events occur.

◆ pj_dns_resolver_set_ns()

pj_status_t pj_dns_resolver_set_ns ( pj_dns_resolver resolver,
unsigned  count,
const pj_str_t  servers[],
const pj_uint16_t  ports[] 

Update the name servers for the DNS resolver. The name servers MUST be configured before any resolution can be done. The order of nameservers specifies their priority; the first name server will be tried first before the next in the list.

resolverThe resolver instance.
countNumber of name servers in the array.
serversArray of name server IP addresses or hostnames. If hostname is specified, the hostname must be resolvable with pj_gethostbyname().
portsOptional array of ports. If this argument is NULL, the nameserver will use default port.
PJ_SUCCESS on success, or the appropriate error code,

◆ pj_dns_resolver_set_settings()

pj_status_t pj_dns_resolver_set_settings ( pj_dns_resolver resolver,
const pj_dns_settings st 

Modify the resolver settings. Application should initialize the settings by retrieving current settings first before applying new settings, to ensure that all fields are initialized properly.

resolverThe resolver instance.
stThe resolver settings.
PJ_SUCCESS on success, or the appropriate error code,

◆ pj_dns_resolver_start_query()

pj_status_t pj_dns_resolver_start_query ( pj_dns_resolver resolver,
const pj_str_t name,
int  type,
unsigned  options,
pj_dns_callback cb,
void *  user_data,
pj_dns_async_query **  p_query 

Create and start asynchronous DNS query for a single resource. Depending on whether response cache is available, this function will either start an asynchronous DNS query or call the callback immediately.

If response is not available in the cache, an asynchronous query will be started, and callback will be called at some time later when the query completes. If p_query argument is not NULL, it will be filled with the asynchronous query object.

If response is available in the cache, the callback will be called immediately before this function returns. In this case, if p_query argument is not NULL, the value will be set to NULL since no new query is started.

resolverThe resolver object.
nameThe name to be resolved.
typeThe type of resource (see pj_dns_type constants).
optionsOptional options, must be zero for now.
cbCallback to be called when the query completes, either successfully or with failure.
user_dataArbitrary user data to be associated with the query, and which will be given back in the callback.
p_queryOptional pointer to receive the query object, if one was started. If this pointer is specified, a NULL may be returned if response cache is available immediately.
PJ_SUCCESS if either an asynchronous query has been started successfully or response cache is available and the user callback has been called.

◆ pj_dns_settings_default()

void pj_dns_settings_default ( pj_dns_settings s)

Set default values to the DNS settings.

sThe DNS settings to be initialized.


PJLIB-UTIL Open Source, small footprint, and portable asynchronous/caching DNS resolver, text scanner, STUN client, and XML library
Copyright (C) 2006-2009 Teluu Inc.