From 36413704c1ff24a8e38e0fcf47c9a9c87621e71a Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Sun, 8 Oct 2006 12:39:34 +0000 Subject: Major addition to support DNS SRV resolution in PJSIP: - added DNS asynchronous/caching resolver engine in PJLIB-UTIL (resolver.[hc]) - modified SIP resolver (sip_resolve.c) to properly perform DNS SRV/A resolution when DNS resolution is enabled. - added dns_test.c in PJSIP-TEST for testing the SIP resolver. - added nameserver configuration in PJSUA-LIB - added "--nameserver" option in PJSUA. - updated project/Makefiles and doxygen documentation. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@753 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib-util/include/pjlib-util.h | 1 + pjlib-util/include/pjlib-util/dns.h | 164 +++++++--- pjlib-util/include/pjlib-util/errno.h | 113 ++++++- pjlib-util/include/pjlib-util/resolver.h | 520 +++++++++++++++++++++++++++++++ 4 files changed, 756 insertions(+), 42 deletions(-) create mode 100644 pjlib-util/include/pjlib-util/resolver.h (limited to 'pjlib-util/include') diff --git a/pjlib-util/include/pjlib-util.h b/pjlib-util/include/pjlib-util.h index 741578fd..3ac8c50b 100644 --- a/pjlib-util/include/pjlib-util.h +++ b/pjlib-util/include/pjlib-util.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/pjlib-util/include/pjlib-util/dns.h b/pjlib-util/include/pjlib-util/dns.h index cb6a28f2..d431b0da 100644 --- a/pjlib-util/include/pjlib-util/dns.h +++ b/pjlib-util/include/pjlib-util/dns.h @@ -22,7 +22,7 @@ /** * @file dns.h - * @brief Asynchronous DNS Name Resolution/resolver + * @brief Low level DNS message parsing and packetization. */ #include @@ -31,16 +31,35 @@ PJ_BEGIN_DECL /** - * @defgroup PJ_DNS_RESOLVER Asynchronous DNS Name Resolution/resolver + * @defgroup PJ_DNS Low-level DNS message parsing and packetization * @ingroup PJ * @{ * - * This module provides services to performing asynchronous DNS resolution. + * This module provides low-level services to parse and packetize DNS queries + * and responses. The functions support building a DNS query packet and parse + * the data in the DNS response. + * + * To create a DNS query packet, application should call #pj_dns_make_query() + * function, specifying the desired DNS query type, the name to be resolved, + * and the buffer where the DNS packet will be built into. + * + * When incoming DNS query or response packet arrives, application can use + * #pj_dns_parse_packet() to parse the TCP/UDP payload into parsed DNS packet + * structure. + * + * This module does not provide any networking functionalities to send or + * receive DNS packets. This functionality should be provided by higher layer + * modules such as @ref PJ_DNS_RESOLVER. */ +enum +{ + PJ_DNS_CLASS_IN = 1 /**< DNS class IN. */ +}; + /** * This enumeration describes standard DNS record types as described by - * RFC 1035. + * RFC 1035, RFC 2782, and others. */ typedef enum pj_dns_type { @@ -158,31 +177,69 @@ typedef struct pj_dns_hdr #define PJ_DNS_GET_QR(val) (((val) & PJ_DNS_SET_QR(1)) >> 15) +/** + * These constants describe DNS RCODEs. Application can fold these constants + * into PJLIB pj_status_t namespace by calling #PJ_STATUS_FROM_DNS_RCODE() + * macro. + */ +typedef enum pj_dns_rcode +{ + PJ_DNS_RCODE_FORMERR = 1, /**< Format error. */ + PJ_DNS_RCODE_SERVFAIL = 2, /**< Server failure. */ + PJ_DNS_RCODE_NXDOMAIN = 3, /**< Name Error. */ + PJ_DNS_RCODE_NOTIMPL = 4, /**< Not Implemented. */ + PJ_DNS_RCODE_REFUSED = 5, /**< Refused. */ + PJ_DNS_RCODE_YXDOMAIN = 6, /**< The name exists. */ + PJ_DNS_RCODE_YXRRSET = 7, /**< The RRset (name, type) exists. */ + PJ_DNS_RCODE_NXRRSET = 8, /**< The RRset (name, type) doesn't exist*/ + PJ_DNS_RCODE_NOTAUTH = 9, /**< Not authorized. */ + PJ_DNS_RCODE_NOTZONE = 10 /**< The zone specified is not a zone. */ + +} pj_dns_rcode; + /** - * This constants describes record types in the DNS packet. + * This constant specifies the maximum names to keep in the temporary name + * table when performing name compression scheme when duplicating DNS packet + * (the #pj_dns_packet_dup() function). + * + * Generally name compression is desired, since it saves some memory (see + * PJ_DNS_RESOLVER_RES_BUF_SIZE setting). However it comes at the expense of + * a little processing overhead to perform name scanning and also a little + * bit more stack usage (8 bytes per entry on 32bit platform). + * + * Default: 16 */ -typedef enum pj_dns_rec_type +#ifndef PJ_DNS_MAX_NAMES_IN_NAMETABLE +# define PJ_DNS_MAX_NAMES_IN_NAMETABLE 16 +#endif + + +/** + * This structure describes a DNS query record. + */ +typedef struct pj_dns_parsed_query { - DNS_QUERY_REC, /**< The record is a query record */ - DNS_RR_REC, /**< The record is resource record */ - DNS_NS_REC, /**< The record is name server record */ - DNS_REC_AR, /**< The record is additional RR. */ -} pj_dns_rec_type; + pj_str_t name; /**< The domain in the query. */ + pj_uint16_t type; /**< Type of the query (pj_dns_type) */ + pj_uint16_t dnsclass; /**< Network class (PJ_DNS_CLASS_IN=1) */ +} pj_dns_parsed_query; /** - * This structure describes a Resource Record parsed from the DNS response. + * This structure describes a Resource Record parsed from the DNS packet. * All integral values are in host byte order. */ typedef struct pj_dns_parsed_rr { pj_str_t name; /**< The domain name which this rec pertains. */ pj_uint16_t type; /**< RR type code. */ - pj_uint16_t class_; /**< Class of data (normally 1, for IN). */ + pj_uint16_t dnsclass; /**< Class of data (PJ_DNS_CLASS_IN=1). */ pj_uint32_t ttl; /**< Time to live. */ pj_uint16_t rdlength; /**< Resource data length. */ - void *data; /**< Pointer to the raw resource data. */ + void *data; /**< Pointer to the raw resource data, only + when the type is not known. If it is known, + the data will be put in rdata below. */ /** For resource types that are recognized/supported by this library, * the parsed resource data will be placed in this rdata union. @@ -223,21 +280,29 @@ typedef struct pj_dns_parsed_rr /** - * This structure describes the response parsed from the raw DNS response. - * Note that all integral values in the parsed response are represented in + * This structure describes the parsed repersentation of the raw DNS packet. + * Note that all integral values in the parsed packet are represented in * host byte order. */ -typedef struct pj_dns_parsed_response +typedef struct pj_dns_parsed_packet { - pj_dns_hdr hdr; /**< Pointer to DNS hdr, in host byte order */ - pj_dns_parsed_rr *ans; /**< Array of DNS RR answer. */ - pj_dns_parsed_rr *ns; /**< Array of NS record in the answer. */ - pj_dns_parsed_rr *arr; /**< Array of additional RR answer. */ -} pj_dns_parsed_response; + pj_dns_hdr hdr; /**< Pointer to DNS hdr, in host byte order */ + pj_dns_parsed_query *q; /**< Array of DNS queries. */ + pj_dns_parsed_rr *ans; /**< Array of DNS RR answer. */ + pj_dns_parsed_rr *ns; /**< Array of NS record in the answer. */ + pj_dns_parsed_rr *arr; /**< Array of additional RR answer. */ +} pj_dns_parsed_packet; /** - * Create DNS query packet to resolve the specified names. + * Create DNS query packet to resolve the specified names. This function + * can be used to build any types of DNS query, such as A record or DNS SRV + * record. + * + * Application specifies the type of record and the name to be queried, + * and the function will build the DNS query packet into the buffer + * specified. Once the packet is successfully built, application can send + * the packet via TCP or UDP connection. * * @param packet The buffer to put the DNS query packet. * @param size On input, it specifies the size of the buffer. @@ -245,7 +310,7 @@ typedef struct pj_dns_parsed_response * the DNS query packet. * @param id DNS query ID to associate DNS response with the * query. - * @param qtype DNS type of record to be queried. + * @param qtype DNS type of record to be queried (see #pj_dns_type). * @param name Name to be queried from the DNS server. * * @return PJ_SUCCESS on success, or the appropriate error code. @@ -253,30 +318,55 @@ typedef struct pj_dns_parsed_response PJ_DECL(pj_status_t) pj_dns_make_query(void *packet, unsigned *size, pj_uint16_t id, - pj_dns_type qtype, + int qtype, const pj_str_t *name); /** - * Parse raw DNS response packet into DNS response structure. + * Parse raw DNS packet into parsed DNS packet structure. This function is + * able to parse few DNS resource records such as A record, PTR record, + * CNAME record, NS record, and SRV record. * - * @param pool Pool to allocate memory for the parsed response. - * @param packet - * @param size - * @param p_res + * @param pool Pool to allocate memory for the parsed packet. + * @param packet Pointer to the DNS packet (the TCP/UDP payload of + * the raw packet). + * @param size The size of the DNS packet. + * @param p_res Pointer to store the resulting parsed packet. * * @return PJ_SUCCESS on success, or the appropriate error code. */ -PJ_DECL(pj_status_t) pj_dns_parse_response(pj_pool_t *pool, - const void *packet, - unsigned size, - pj_dns_parsed_response **p_res); +PJ_DECL(pj_status_t) pj_dns_parse_packet(pj_pool_t *pool, + const void *packet, + unsigned size, + pj_dns_parsed_packet **p_res); + +/** + * Duplicate DNS packet. + * + * @param pool The pool to allocate memory for the duplicated packet. + * @param p The DNS packet to be cloned. + * @p_dst Pointer to store the cloned DNS packet. + */ +PJ_DECL(void) pj_dns_packet_dup(pj_pool_t *pool, + const pj_dns_parsed_packet*p, + pj_dns_parsed_packet **p_dst); + + +/** + * Utility function to get the type name string of the specified DNS type. + * + * @param type DNS type (see #pj_dns_type). + * + * @return String name of the type (e.g. "A", "SRV", etc.). + */ +PJ_DECL(const char *) pj_dns_get_type_name(int type); + /** - * Dump DNS response to standard log. + * Dump DNS packet to standard log. * - * @param res The DNS response. + * @param res The DNS packet. */ -PJ_DECL(void) pj_dns_dump_response(const pj_dns_parsed_response *res); +PJ_DECL(void) pj_dns_dump_packet(const pj_dns_parsed_packet *res); /** diff --git a/pjlib-util/include/pjlib-util/errno.h b/pjlib-util/include/pjlib-util/errno.h index 45e25f97..589fcb63 100644 --- a/pjlib-util/include/pjlib-util/errno.h +++ b/pjlib-util/include/pjlib-util/errno.h @@ -1,4 +1,4 @@ -/* $Id */ +/* $Id$ */ /* * Copyright (C)2003-2006 Benny Prijono * @@ -107,34 +107,137 @@ ***********************************************************/ /** * @hideinitializer - * Outgoing DNS query packet buffer is too small. + * DNS query packet buffer is too small. * This error occurs when the user supplied buffer for creating DNS * query (#pj_dns_make_query() function) is too small. */ #define PJLIB_UTIL_EDNSQRYTOOSMALL (PJLIB_UTIL_ERRNO_START+40) /* 320040 */ /** * @hideinitializer - * Invalid packet length in DNS response. + * Invalid DNS packet length. * This error occurs when the received DNS response packet does not * match all the fields length. */ #define PJLIB_UTIL_EDNSINSIZE (PJLIB_UTIL_ERRNO_START+41) /* 320041 */ /** * @hideinitializer - * Invalid class in DNS response. + * Invalid DNS class. * This error occurs when the received DNS response contains network * class other than IN (Internet). */ #define PJLIB_UTIL_EDNSINCLASS (PJLIB_UTIL_ERRNO_START+42) /* 320042 */ /** * @hideinitializer - * Invalid name pointer in DNS response. + * Invalid DNS name pointer. * This error occurs when parsing the compressed names inside DNS * response packet, when the name pointer points to an invalid address * or the parsing has triggerred too much recursion. */ #define PJLIB_UTIL_EDNSINNAMEPTR (PJLIB_UTIL_ERRNO_START+43) /* 320043 */ +/** + * @hideinitializer + * Invalid DNS nameserver address. If hostname was specified for nameserver + * address, this error means that the function was unable to resolve + * the nameserver hostname. + */ +#define PJLIB_UTIL_EDNSINNSADDR (PJLIB_UTIL_ERRNO_START+44) /* 320044 */ +/** + * @hideinitializer + * No nameserver is in DNS resolver. No nameserver is configured in the + * resolver. + */ +#define PJLIB_UTIL_EDNSNONS (PJLIB_UTIL_ERRNO_START+45) /* 320045 */ +/** + * @hideinitializer + * No working DNS nameserver. All nameservers have been queried, + * but none was able to serve any DNS requests. These "bad" nameservers + * will be re-tested again for "goodness" after some period. + */ +#define PJLIB_UTIL_EDNSNOWORKINGNS (PJLIB_UTIL_ERRNO_START+46) /* 320046 */ +/** + * @hideinitializer + * No answer record in the DNS response. + */ +#define PJLIB_UTIL_EDNSNOANSWERREC (PJLIB_UTIL_ERRNO_START+47) /* 320047 */ + +/* DNS ERRORS MAPPED FROM RCODE: */ + +/** + * Start of error code mapped from DNS RCODE + */ +#define PJLIB_UTIL_DNS_RCODE_START (PJLIB_UTIL_ERRNO_START+50) /* 320050 */ + +/** + * Map DNS RCODE status into pj_status_t. + */ +#define PJ_STATUS_FROM_DNS_RCODE(rcode) (rcode==0 ? PJ_SUCCESS : \ + PJLIB_UTIL_DNS_RCODE_START+rcode) +/** + * @hideinitializer + * Format error - The name server was unable to interpret the query. + * This corresponds to DNS RCODE 1. + */ +#define PJLIB_UTIL_EDNS_FORMERR PJ_STATUS_FROM_DNS_RCODE(1) /* 320051 */ +/** + * @hideinitializer + * Server failure - The name server was unable to process this query due to a + * problem with the name server. + * This corresponds to DNS RCODE 2. + */ +#define PJLIB_UTIL_EDNS_SERVFAIL PJ_STATUS_FROM_DNS_RCODE(2) /* 320052 */ +/** + * @hideinitializer + * Name Error - Meaningful only for responses from an authoritative name + * server, this code signifies that the domain name referenced in the query + * does not exist. + * This corresponds to DNS RCODE 3. + */ +#define PJLIB_UTIL_EDNS_NXDOMAIN PJ_STATUS_FROM_DNS_RCODE(3) /* 320053 */ +/** + * @hideinitializer + * Not Implemented - The name server does not support the requested kind of + * query. + * This corresponds to DNS RCODE 4. + */ +#define PJLIB_UTIL_EDNS_NOTIMPL PJ_STATUS_FROM_DNS_RCODE(4) /* 320054 */ +/** + * @hideinitializer + * Refused - The name server refuses to perform the specified operation for + * policy reasons. + * This corresponds to DNS RCODE 5. + */ +#define PJLIB_UTIL_EDNS_REFUSED PJ_STATUS_FROM_DNS_RCODE(5) /* 320055 */ +/** + * @hideinitializer + * The name exists. + * This corresponds to DNS RCODE 6. + */ +#define PJLIB_UTIL_EDNS_YXDOMAIN PJ_STATUS_FROM_DNS_RCODE(6) /* 320056 */ +/** + * @hideinitializer + * The RRset (name, type) exists. + * This corresponds to DNS RCODE 7. + */ +#define PJLIB_UTIL_EDNS_YXRRSET PJ_STATUS_FROM_DNS_RCODE(7) /* 320057 */ +/** + * @hideinitializer + * The RRset (name, type) does not exist. + * This corresponds to DNS RCODE 8. + */ +#define PJLIB_UTIL_EDNS_NXRRSET PJ_STATUS_FROM_DNS_RCODE(8) /* 320058 */ +/** + * @hideinitializer + * The requestor is not authorized to perform this operation. + * This corresponds to DNS RCODE 9. + */ +#define PJLIB_UTIL_EDNS_NOTAUTH PJ_STATUS_FROM_DNS_RCODE(9) /* 320059 */ +/** + * @hideinitializer + * The zone specified is not a zone. + * This corresponds to DNS RCODE 10. + */ +#define PJLIB_UTIL_EDNS_NOTZONE PJ_STATUS_FROM_DNS_RCODE(10)/* 320060 */ #endif /* __PJLIB_UTIL_ERRNO_H__ */ diff --git a/pjlib-util/include/pjlib-util/resolver.h b/pjlib-util/include/pjlib-util/resolver.h new file mode 100644 index 00000000..471b5717 --- /dev/null +++ b/pjlib-util/include/pjlib-util/resolver.h @@ -0,0 +1,520 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJLIB_UTIL_RESOLVER_H__ +#define __PJLIB_UTIL_RESOLVER_H__ + +/** + * @file resolver.h + * @brief Asynchronous DNS resolver + */ +#include + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_DNS_RESOLVER Asynchronous DNS Server Resolution + * @ingroup PJLIB_UTIL + * @{ + * + * 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 @ref PJ_DNS) and currently supports + * several types of DNS resource records such as A record (typical query with + * gethostbyname()) and SRV record. + * + * \section PJ_DNS_RESOLVER_FEATURES Features + * + * \subsection PJ_DNS_RESOLVER_FEATURES_ASYNC 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. + * + * \subsection PJ_DNS_RESOLVER_FEATURES_RETRANSMISSION 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. + * + * \subsection PJ_DNS_RESOLVER_FEATURES_CACHING 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. + * + * \subsection PJ_DNS_RESOLVER_FEATURES_PARALLEL 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. + * + * + * \subsection PJ_DNS_RESOLVER_FEATURES_REC Supported Resource Records + * + * The low-level DNS parsing utility (see @ref PJ_DNS) 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). + * + * + * \section PJ_DNS_RESOLVER_USING 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. + * + * + * \section PJ_DNS_RESOLVER_LIMITATIONS 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. + * + * 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. + * + * + * \section PJ_DNS_RESOLVER_REFERENCE Reference + * + * The PJLIB-UTIL resolver was built from the information in the following + * standards: + * - RFC 1035: "Domain names - implementation and specification" + * - RFC 2782: "A DNS RR for specifying the location of services (DNS SRV)" + */ + + +/* + * CONFIGURATIONS + */ + +/** + * Maximum numbers of DNS nameservers that can be configured in resolver. + */ +#ifndef PJ_DNS_RESOLVER_MAX_NS +# define PJ_DNS_RESOLVER_MAX_NS 16 +#endif + + +/** + * Default retransmission delay, in miliseconds. The combination of + * retransmission delay and count determines the query timeout. + * + * Default: 2000 (2 seconds, according to RFC 1035) + */ +#ifndef PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY +# define PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY 2000 +#endif + + +/** + * Maximum number of transmissions before timeout is declared for + * the query. + * + * Default: 2 + */ +#ifndef PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT +# define PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT 5 +#endif + + +/** + * Maximum life-time of DNS response in the resolver response cache, + * in seconds. If the value is zero, then DNS response caching will be + * disabled. + * + * Default is 300 seconds (5 minutes). + * + * @see PJ_DNS_RESOLVER_INVALID_TTL + */ +#ifndef PJ_DNS_RESOLVER_MAX_TTL +# define PJ_DNS_RESOLVER_MAX_TTL (5*60) +#endif + +/** + * The life-time of invalid DNS response in the resolver response cache. + * An invalid DNS response is a response without an answer. These + * responses can be put in the cache too to minimize message round-trip. + * + * Default: 0 (which means, invalid DNS response will not be cached). + * + * @see PJ_DNS_RESOLVER_MAX_TTL + */ +#ifndef PJ_DNS_RESOLVER_INVALID_TTL +# define PJ_DNS_RESOLVER_INVALID_TTL 60 +#endif + +/** + * The interval on which nameservers which are known to be good to be + * probed again to determine whether they are still good. Note that + * this applies to both active nameserver (the one currently being used) + * and idle nameservers (good nameservers that are not currently selected). + * The probing to query the "goodness" of nameservers involves sending + * the same query to multiple servers, so it's probably not a good idea + * to send this probing too often. + * + * Default: 600 (ten minutes) + * + * @see PJ_DNS_RESOLVER_BAD_NS_TTL + */ +#ifndef PJ_DNS_RESOLVER_GOOD_NS_TTL +# define PJ_DNS_RESOLVER_GOOD_NS_TTL (10*60) +#endif + +/** + * The interval on which nameservers which known to be bad to be probed + * again to determine whether it is still bad. + * + * Default: 600 (ten minutes) + * + * @see PJ_DNS_RESOLVER_GOOD_NS_TTL + */ +#ifndef PJ_DNS_RESOLVER_BAD_NS_TTL +# define PJ_DNS_RESOLVER_BAD_NS_TTL (1*60) +#endif + + +/** + * Maximum size of UDP packet. RFC 1035 states that maximum size of + * DNS packet carried over UDP is 512 bytes. + * + * Default: 512 byes + */ +#ifndef PJ_DNS_RESOLVER_MAX_UDP_SIZE +# define PJ_DNS_RESOLVER_MAX_UDP_SIZE 512 +#endif + + +/** + * Size of memory pool allocated for each individual DNS response cache. + * This value here should be more or less the same as maximum UDP packet + * size (PJ_DNS_RESOLVER_MAX_UDP_SIZE), since the DNS replicator function + * (#pj_dns_packet_dup()) is also capable of performing name compressions. + * + * Default: 512 (as a broad guidance, 400 is good for 4 SRV entries). + */ +#ifndef PJ_DNS_RESOLVER_RES_BUF_SIZE +# define PJ_DNS_RESOLVER_RES_BUF_SIZE 512 +#endif + + + +/** + * Opaque data type for DNS resolver object. + */ +typedef struct pj_dns_resolver pj_dns_resolver; + +/** + * Opaque data type for asynchronous DNS query object. + */ +typedef struct pj_dns_async_query pj_dns_async_query; + +/** + * Type of asynchronous callback which will be called when the asynchronous + * query completes. + * + * @param user_data The user data set by application when creating the + * asynchronous query. + * @param status Status of the DNS resolution. + * @param response The response packet received from the server. This + * argument may be NULL when status is not PJ_SUCCESS. + */ +typedef void pj_dns_callback(void *user_data, + pj_status_t status, + pj_dns_parsed_packet *response); + + +/** + * This structure describes resolver settings. + */ +typedef struct pj_dns_settings +{ + unsigned options; /**< Options flags. */ + unsigned qretr_delay; /**< Query retransmit delay in msec. */ + unsigned qretr_count; /**< Query maximum retransmission count. */ + unsigned cache_max_ttl; /**< Maximum TTL for cached responses. If the + value is zero, caching is disabled. */ +} pj_dns_settings; + + +/** + * 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. + * + * @param pf Pool factory where the memory pool will be created from. + * @param name Optional resolver name to identify the instance in + * the log. + * @param options Optional options, must be zero for now. + * @param timer Optional 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. + * @param ioqueue Optional 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. + * @param p_resolver Pointer to receive the resolver instance. + * + * @return PJ_SUCCESS on success, or the appropriate error code, + */ +PJ_DECL(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); + + +/** + * 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. + * + * @param resolver The resolver instance. + * @param count Number of name servers in the array. + * @param servers Array of name server IP addresses or hostnames. If + * hostname is specified, the hostname must be resolvable + * with pj_gethostbyname(). + * @param ports Optional array of ports. If this argument is NULL, + * the nameserver will use default port. + * + * @return PJ_SUCCESS on success, or the appropriate error code, + */ +PJ_DECL(pj_status_t) pj_dns_resolver_set_ns(pj_dns_resolver *resolver, + unsigned count, + const pj_str_t servers[], + const pj_uint16_t ports[]); + + +/** + * Get the resolver current settings. + * + * @param resolver The resolver instance. + * @param st Buffer to be filled up with resolver settings. + * + * @return The query timeout setting, in seconds. + */ +PJ_DECL(pj_status_t) pj_dns_resolver_get_settings(pj_dns_resolver *resolver, + 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. + * + * @param resolver The resolver instance. + * @param st The resolver settings. + * + * @return PJ_SUCCESS on success, or the appropriate error code, + */ +PJ_DECL(pj_status_t) pj_dns_resolver_set_settings(pj_dns_resolver *resolver, + const pj_dns_settings *st); + + +/** + * 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 \a timer or + * \a ioqueue argument in #pj_dns_resolver_create()). + * + * @param resolver The resolver instance. + * @param timeout Maximum time to wait for event occurence. If this + * argument is NULL, this function will wait forever + * until events occur. + */ +PJ_DECL(void) pj_dns_resolver_handle_events(pj_dns_resolver *resolver, + const pj_time_val *timeout); + + +/** + * Destroy DNS resolver instance. + * + * @param resolver The resolver object to be destryed + * @param notify If non-zero, all pending asynchronous queries will be + * cancelled and its callback will be called. If FALSE, + * then no callback will be called. + * + * @return PJ_SUCCESS on success, or the appropriate error code, + */ +PJ_DECL(pj_status_t) pj_dns_resolver_destroy(pj_dns_resolver *resolver, + pj_bool_t notify); + + +/** + * 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 \a 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 \a p_query + * argument is not NULL, the value will be set to NULL since no new query + * is started. + * + * @param resolver The resolver object. + * @param name The name to be resolved. + * @param type The type of resource (see #pj_dns_type constants). + * @param options Optional options, must be zero for now. + * @param cb Callback to be called when the query completes, + * either successfully or with failure. + * @param user_data Arbitrary user data to be associated with the query, + * and which will be given back in the callback. + * @param p_query Optional 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. + * + * @return PJ_SUCCESS if either an asynchronous query has been + * started successfully or response cache is available and + * the user callback has been called. + */ +PJ_DECL(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); + +/** + * Cancel a pending query. + * + * @param query The pending asynchronous query to be cancelled. + * @param notify If non-zero, the callback will be called with failure + * status to notify that the query has been cancelled. + * + * @return PJ_SUCCESS on success, or the appropriate error code, + */ +PJ_DECL(pj_status_t) pj_dns_resolver_cancel_query(pj_dns_async_query *query, + pj_bool_t notify); + + +/** + * 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. + * + * @param resolver The resolver instance. + * @param pkt DNS packet to be added to the DNS cache. If the packet + * matches existing entry, it will update the entry. + * @param set_ttl If 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. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_dns_resolver_add_entry(pj_dns_resolver *resolver, + const pj_dns_parsed_packet *pkt, + pj_bool_t set_ttl); + +/** + * Get the total number of response in the response cache. + * + * @param resolver The resolver instance. + * + * @return Current number of entries being stored in the response + * cache. + */ +PJ_DECL(unsigned) pj_dns_resolver_get_cached_count(pj_dns_resolver *resolver); + + +/** + * Dump resolver state to the log. + * + * @param resolver The resolver instance. + * @param detail Will print detailed entries. + */ +PJ_DECL(void) pj_dns_resolver_dump(pj_dns_resolver *resolver, + pj_bool_t detail); + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJLIB_UTIL_RESOLVER_H__ */ + -- cgit v1.2.3