Tristan Matthews | 0a329cc | 2013-07-17 13:20:14 -0400 | [diff] [blame] | 1 | /* $Id$ */ |
| 2 | /* |
| 3 | * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) |
| 4 | * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 19 | */ |
| 20 | #ifndef __PJLIB_UTIL_RESOLVER_H__ |
| 21 | #define __PJLIB_UTIL_RESOLVER_H__ |
| 22 | |
| 23 | /** |
| 24 | * @file resolver.h |
| 25 | * @brief Asynchronous DNS resolver |
| 26 | */ |
| 27 | #include <pjlib-util/dns.h> |
| 28 | |
| 29 | |
| 30 | PJ_BEGIN_DECL |
| 31 | |
| 32 | |
| 33 | /** |
| 34 | * @defgroup PJ_DNS_RESOLVER DNS Asynchronous/Caching Resolution Engine |
| 35 | * @ingroup PJ_DNS |
| 36 | * @{ |
| 37 | * |
| 38 | * This module manages the host/server resolution by performing asynchronous |
| 39 | * DNS queries and caching the results in the cache. It uses PJLIB-UTIL |
| 40 | * low-level DNS parsing functions (see @ref PJ_DNS) and currently supports |
| 41 | * several types of DNS resource records such as A record (typical query with |
| 42 | * gethostbyname()) and SRV record. |
| 43 | * |
| 44 | * \section PJ_DNS_RESOLVER_FEATURES Features |
| 45 | * |
| 46 | * \subsection PJ_DNS_RESOLVER_FEATURES_ASYNC Asynchronous Query and Query Aggregation |
| 47 | * |
| 48 | * The DNS queries are performed asychronously, with timeout setting |
| 49 | * configured on per resolver instance basis. Application can issue multiple |
| 50 | * asynchronous queries simultaneously. Subsequent queries to the same resource |
| 51 | * (name and DNS resource type) while existing query is still pending will be |
| 52 | * merged into one query, so that only one DNS request packet is issued. |
| 53 | * |
| 54 | * \subsection PJ_DNS_RESOLVER_FEATURES_RETRANSMISSION Query Retransmission |
| 55 | * |
| 56 | * Asynchronous query will be retransmitted if no response is received |
| 57 | * within the preconfigured time. Once maximum retransmission count is |
| 58 | * exceeded and no response is received, the query will time out and the |
| 59 | * callback will be called when error status. |
| 60 | * |
| 61 | * \subsection PJ_DNS_RESOLVER_FEATURES_CACHING Response Caching with TTL |
| 62 | * |
| 63 | * The resolver instance caches the results returned by nameservers, to |
| 64 | * enhance the performance by minimizing the message round-trip to the server. |
| 65 | * The TTL of the cached resposne is calculated from minimum TTL value found |
| 66 | * across all resource record (RR) TTL in the response and further more it can |
| 67 | * be limited to some preconfigured maximum TTL in the resolver. |
| 68 | * |
| 69 | * Response caching can be disabled by setting the maximum TTL value of the |
| 70 | * resolver to zero. |
| 71 | * |
| 72 | * \subsection PJ_DNS_RESOLVER_FEATURES_PARALLEL Parallel and Backup Name Servers |
| 73 | * |
| 74 | * When the resolver is configured with multiple nameservers, initially the |
| 75 | * queries will be issued to multiple name servers simultaneously to probe |
| 76 | * which servers are not active. Once the probing stage is done, subsequent |
| 77 | * queries will be directed to only one ACTIVE server which provides the best |
| 78 | * response time. |
| 79 | * |
| 80 | * Name servers are probed periodically to see which nameservers are active |
| 81 | * and which are down. This probing is done when a query is sent, thus no |
| 82 | * timer is needed to maintain this. Also probing will be done in parallel |
| 83 | * so that there would be no additional delay for the query. |
| 84 | * |
| 85 | * |
| 86 | * \subsection PJ_DNS_RESOLVER_FEATURES_REC Supported Resource Records |
| 87 | * |
| 88 | * The low-level DNS parsing utility (see @ref PJ_DNS) supports parsing of |
| 89 | * the following DNS resource records (RR): |
| 90 | * - DNS A record |
| 91 | * - DNS SRV record |
| 92 | * - DNS PTR record |
| 93 | * - DNS NS record |
| 94 | * - DNS CNAME record |
| 95 | * |
| 96 | * For other types of record, application can parse the raw resource |
| 97 | * record data (rdata) from the parsed DNS packet (#pj_dns_parsed_packet). |
| 98 | * |
| 99 | * |
| 100 | * \section PJ_DNS_RESOLVER_USING Using the Resolver |
| 101 | * |
| 102 | * To use the resolver, application first creates the resolver instance by |
| 103 | * calling #pj_dns_resolver_create(). If application already has its own |
| 104 | * timer and ioqueue instances, it can instruct the resolver to use these |
| 105 | * instances so that application does not need to poll the resolver |
| 106 | * periodically to process events. If application does not specify the |
| 107 | * timer and ioqueue instance for the resolver, an internal timer and |
| 108 | * ioqueue will be created by the resolver. And since the resolver does not |
| 109 | * create it's own thread, application MUST poll the resolver periodically |
| 110 | * by calling #pj_dns_resolver_handle_events() to allow events (network and |
| 111 | * timer) to be processed. |
| 112 | * |
| 113 | * Next, application MUST configure the nameservers to be used by the |
| 114 | * resolver, by calling #pj_dns_resolver_set_ns(). |
| 115 | * |
| 116 | * Application performs asynchronous query by submitting the query with |
| 117 | * #pj_dns_resolver_start_query(). Once the query completes (either |
| 118 | * successfully or times out), the callback will be called. |
| 119 | * |
| 120 | * Application can cancel a pending query by calling #pj_dns_resolver_cancel_query(). |
| 121 | * |
| 122 | * Resolver must be destroyed by calling #pj_dns_resolver_destroy() to |
| 123 | * release all resources back to the system. |
| 124 | * |
| 125 | * |
| 126 | * \section PJ_DNS_RESOLVER_LIMITATIONS Resolver Limitations |
| 127 | * |
| 128 | * Current implementation mainly suffers from a growing memory problem, |
| 129 | * which mainly is caused by the response caching. Although there is only |
| 130 | * one cache entry per {query, name} combination, these cache entry will |
| 131 | * never get deleted since there is no timer is created to invalidate these |
| 132 | * entries. So the more unique names being queried by application, there more |
| 133 | * enties will be created in the response cache. |
| 134 | * |
| 135 | * Note that a single response entry will occupy about 600-700 bytes of |
| 136 | * pool memory (the PJ_DNS_RESOLVER_RES_BUF_SIZE value plus internal |
| 137 | * structure). |
| 138 | * |
| 139 | * Application can work around this problem by doing one of these: |
| 140 | * - disable caching by setting PJ_DNS_RESOLVER_MAX_TTL and |
| 141 | * PJ_DNS_RESOLVER_INVALID_TTL to zero. |
| 142 | * - periodically query #pj_dns_resolver_get_cached_count() and destroy- |
| 143 | * recreate the resolver to recycle the memory used by the resolver. |
| 144 | * |
| 145 | * Note that future improvement may solve this problem by introducing |
| 146 | * expiration timer to the cached entries. |
| 147 | * |
| 148 | * |
| 149 | * \section PJ_DNS_RESOLVER_REFERENCE Reference |
| 150 | * |
| 151 | * The PJLIB-UTIL resolver was built from the information in the following |
| 152 | * standards: |
| 153 | * - <A HREF="http://www.faqs.org/rfcs/rfc1035.html"> |
| 154 | * RFC 1035: "Domain names - implementation and specification"</A> |
| 155 | * - <A HREF="http://www.faqs.org/rfcs/rfc2782.html"> |
| 156 | * RFC 2782: "A DNS RR for specifying the location of services (DNS SRV)" |
| 157 | * </A> |
| 158 | */ |
| 159 | |
| 160 | |
| 161 | |
| 162 | /** |
| 163 | * Opaque data type for DNS resolver object. |
| 164 | */ |
| 165 | typedef struct pj_dns_resolver pj_dns_resolver; |
| 166 | |
| 167 | /** |
| 168 | * Opaque data type for asynchronous DNS query object. |
| 169 | */ |
| 170 | typedef struct pj_dns_async_query pj_dns_async_query; |
| 171 | |
| 172 | /** |
| 173 | * Type of asynchronous callback which will be called when the asynchronous |
| 174 | * query completes. |
| 175 | * |
| 176 | * @param user_data The user data set by application when creating the |
| 177 | * asynchronous query. |
| 178 | * @param status Status of the DNS resolution. |
| 179 | * @param response The response packet received from the server. This |
| 180 | * argument may be NULL when status is not PJ_SUCCESS. |
| 181 | */ |
| 182 | typedef void pj_dns_callback(void *user_data, |
| 183 | pj_status_t status, |
| 184 | pj_dns_parsed_packet *response); |
| 185 | |
| 186 | |
| 187 | /** |
| 188 | * This structure describes resolver settings. |
| 189 | */ |
| 190 | typedef struct pj_dns_settings |
| 191 | { |
| 192 | unsigned options; /**< Options flags. */ |
| 193 | unsigned qretr_delay; /**< Query retransmit delay in msec. */ |
| 194 | unsigned qretr_count; /**< Query maximum retransmission count. */ |
| 195 | unsigned cache_max_ttl; /**< Maximum TTL for cached responses. If the |
| 196 | value is zero, caching is disabled. */ |
| 197 | unsigned good_ns_ttl; /**< See #PJ_DNS_RESOLVER_GOOD_NS_TTL */ |
| 198 | unsigned bad_ns_ttl; /**< See #PJ_DNS_RESOLVER_BAD_NS_TTL */ |
| 199 | } pj_dns_settings; |
| 200 | |
| 201 | |
| 202 | /** |
| 203 | * This structure represents DNS A record, as the result of parsing |
| 204 | * DNS response packet using #pj_dns_parse_a_response(). |
| 205 | */ |
| 206 | typedef struct pj_dns_a_record |
| 207 | { |
| 208 | /** The target name being queried. */ |
| 209 | pj_str_t name; |
| 210 | |
| 211 | /** If target name corresponds to a CNAME entry, the alias contains |
| 212 | * the value of the CNAME entry, otherwise it will be empty. |
| 213 | */ |
| 214 | pj_str_t alias; |
| 215 | |
| 216 | /** Number of IP addresses. */ |
| 217 | unsigned addr_count; |
| 218 | |
| 219 | /** IP addresses of the host found in the response */ |
| 220 | pj_in_addr addr[PJ_DNS_MAX_IP_IN_A_REC]; |
| 221 | |
| 222 | /** Internal buffer for hostname and alias. */ |
| 223 | char buf_[128]; |
| 224 | |
| 225 | } pj_dns_a_record; |
| 226 | |
| 227 | |
| 228 | /** |
| 229 | * Set default values to the DNS settings. |
| 230 | * |
| 231 | * @param s The DNS settings to be initialized. |
| 232 | */ |
| 233 | PJ_DECL(void) pj_dns_settings_default(pj_dns_settings *s); |
| 234 | |
| 235 | |
| 236 | /** |
| 237 | * Create DNS resolver instance. After the resolver is created, application |
| 238 | * MUST configure the nameservers with #pj_dns_resolver_set_ns(). |
| 239 | * |
| 240 | * When creating the resolver, application may specify both timer heap |
| 241 | * and ioqueue instance, so that it doesn't need to poll the resolver |
| 242 | * periodically. |
| 243 | * |
| 244 | * @param pf Pool factory where the memory pool will be created from. |
| 245 | * @param name Optional resolver name to identify the instance in |
| 246 | * the log. |
| 247 | * @param options Optional options, must be zero for now. |
| 248 | * @param timer Optional timer heap instance to be used by the resolver. |
| 249 | * If timer heap is not specified, an internal timer will be |
| 250 | * created, and application would need to poll the resolver |
| 251 | * periodically. |
| 252 | * @param ioqueue Optional I/O Queue instance to be used by the resolver. |
| 253 | * If ioqueue is not specified, an internal one will be |
| 254 | * created, and application would need to poll the resolver |
| 255 | * periodically. |
| 256 | * @param p_resolver Pointer to receive the resolver instance. |
| 257 | * |
| 258 | * @return PJ_SUCCESS on success, or the appropriate error code, |
| 259 | */ |
| 260 | PJ_DECL(pj_status_t) pj_dns_resolver_create(pj_pool_factory *pf, |
| 261 | const char *name, |
| 262 | unsigned options, |
| 263 | pj_timer_heap_t *timer, |
| 264 | pj_ioqueue_t *ioqueue, |
| 265 | pj_dns_resolver **p_resolver); |
| 266 | |
| 267 | |
| 268 | /** |
| 269 | * Update the name servers for the DNS resolver. The name servers MUST be |
| 270 | * configured before any resolution can be done. The order of nameservers |
| 271 | * specifies their priority; the first name server will be tried first |
| 272 | * before the next in the list. |
| 273 | * |
| 274 | * @param resolver The resolver instance. |
| 275 | * @param count Number of name servers in the array. |
| 276 | * @param servers Array of name server IP addresses or hostnames. If |
| 277 | * hostname is specified, the hostname must be resolvable |
| 278 | * with pj_gethostbyname(). |
| 279 | * @param ports Optional array of ports. If this argument is NULL, |
| 280 | * the nameserver will use default port. |
| 281 | * |
| 282 | * @return PJ_SUCCESS on success, or the appropriate error code, |
| 283 | */ |
| 284 | PJ_DECL(pj_status_t) pj_dns_resolver_set_ns(pj_dns_resolver *resolver, |
| 285 | unsigned count, |
| 286 | const pj_str_t servers[], |
| 287 | const pj_uint16_t ports[]); |
| 288 | |
| 289 | |
| 290 | /** |
| 291 | * Get the resolver current settings. |
| 292 | * |
| 293 | * @param resolver The resolver instance. |
| 294 | * @param st Buffer to be filled up with resolver settings. |
| 295 | * |
| 296 | * @return The query timeout setting, in seconds. |
| 297 | */ |
| 298 | PJ_DECL(pj_status_t) pj_dns_resolver_get_settings(pj_dns_resolver *resolver, |
| 299 | pj_dns_settings *st); |
| 300 | |
| 301 | |
| 302 | /** |
| 303 | * Modify the resolver settings. Application should initialize the settings |
| 304 | * by retrieving current settings first before applying new settings, to |
| 305 | * ensure that all fields are initialized properly. |
| 306 | * |
| 307 | * @param resolver The resolver instance. |
| 308 | * @param st The resolver settings. |
| 309 | * |
| 310 | * @return PJ_SUCCESS on success, or the appropriate error code, |
| 311 | */ |
| 312 | PJ_DECL(pj_status_t) pj_dns_resolver_set_settings(pj_dns_resolver *resolver, |
| 313 | const pj_dns_settings *st); |
| 314 | |
| 315 | |
| 316 | /** |
| 317 | * Poll for events from the resolver. This function MUST be called |
| 318 | * periodically when the resolver is using it's own timer or ioqueue |
| 319 | * (in other words, when NULL is specified as either \a timer or |
| 320 | * \a ioqueue argument in #pj_dns_resolver_create()). |
| 321 | * |
| 322 | * @param resolver The resolver instance. |
| 323 | * @param timeout Maximum time to wait for event occurence. If this |
| 324 | * argument is NULL, this function will wait forever |
| 325 | * until events occur. |
| 326 | */ |
| 327 | PJ_DECL(void) pj_dns_resolver_handle_events(pj_dns_resolver *resolver, |
| 328 | const pj_time_val *timeout); |
| 329 | |
| 330 | |
| 331 | /** |
| 332 | * Destroy DNS resolver instance. |
| 333 | * |
| 334 | * @param resolver The resolver object to be destryed |
| 335 | * @param notify If non-zero, all pending asynchronous queries will be |
| 336 | * cancelled and its callback will be called. If FALSE, |
| 337 | * then no callback will be called. |
| 338 | * |
| 339 | * @return PJ_SUCCESS on success, or the appropriate error code, |
| 340 | */ |
| 341 | PJ_DECL(pj_status_t) pj_dns_resolver_destroy(pj_dns_resolver *resolver, |
| 342 | pj_bool_t notify); |
| 343 | |
| 344 | |
| 345 | /** |
| 346 | * Create and start asynchronous DNS query for a single resource. Depending |
| 347 | * on whether response cache is available, this function will either start |
| 348 | * an asynchronous DNS query or call the callback immediately. |
| 349 | * |
| 350 | * If response is not available in the cache, an asynchronous query will be |
| 351 | * started, and callback will be called at some time later when the query |
| 352 | * completes. If \a p_query argument is not NULL, it will be filled with |
| 353 | * the asynchronous query object. |
| 354 | * |
| 355 | * If response is available in the cache, the callback will be called |
| 356 | * immediately before this function returns. In this case, if \a p_query |
| 357 | * argument is not NULL, the value will be set to NULL since no new query |
| 358 | * is started. |
| 359 | * |
| 360 | * @param resolver The resolver object. |
| 361 | * @param name The name to be resolved. |
| 362 | * @param type The type of resource (see #pj_dns_type constants). |
| 363 | * @param options Optional options, must be zero for now. |
| 364 | * @param cb Callback to be called when the query completes, |
| 365 | * either successfully or with failure. |
| 366 | * @param user_data Arbitrary user data to be associated with the query, |
| 367 | * and which will be given back in the callback. |
| 368 | * @param p_query Optional pointer to receive the query object, if one |
| 369 | * was started. If this pointer is specified, a NULL may |
| 370 | * be returned if response cache is available immediately. |
| 371 | * |
| 372 | * @return PJ_SUCCESS if either an asynchronous query has been |
| 373 | * started successfully or response cache is available and |
| 374 | * the user callback has been called. |
| 375 | */ |
| 376 | PJ_DECL(pj_status_t) pj_dns_resolver_start_query(pj_dns_resolver *resolver, |
| 377 | const pj_str_t *name, |
| 378 | int type, |
| 379 | unsigned options, |
| 380 | pj_dns_callback *cb, |
| 381 | void *user_data, |
| 382 | pj_dns_async_query **p_query); |
| 383 | |
| 384 | /** |
| 385 | * Cancel a pending query. |
| 386 | * |
| 387 | * @param query The pending asynchronous query to be cancelled. |
| 388 | * @param notify If non-zero, the callback will be called with failure |
| 389 | * status to notify that the query has been cancelled. |
| 390 | * |
| 391 | * @return PJ_SUCCESS on success, or the appropriate error code, |
| 392 | */ |
| 393 | PJ_DECL(pj_status_t) pj_dns_resolver_cancel_query(pj_dns_async_query *query, |
| 394 | pj_bool_t notify); |
| 395 | |
| 396 | /** |
| 397 | * A utility function to parse a DNS response containing A records into |
| 398 | * DNS A record. |
| 399 | * |
| 400 | * @param pkt The DNS response packet. |
| 401 | * @param rec The structure to be initialized with the parsed |
| 402 | * DNS A record from the packet. |
| 403 | * |
| 404 | * @return PJ_SUCCESS if response can be parsed successfully. |
| 405 | */ |
| 406 | PJ_DECL(pj_status_t) pj_dns_parse_a_response(const pj_dns_parsed_packet *pkt, |
| 407 | pj_dns_a_record *rec); |
| 408 | |
| 409 | |
| 410 | /** |
| 411 | * Put the specified DNS packet into DNS cache. This function is mainly used |
| 412 | * for testing the resolver, however it can also be used to inject entries |
| 413 | * into the resolver. |
| 414 | * |
| 415 | * The packet MUST contain either answer section or query section so that |
| 416 | * it can be indexed. |
| 417 | * |
| 418 | * @param resolver The resolver instance. |
| 419 | * @param pkt DNS packet to be added to the DNS cache. If the packet |
| 420 | * matches existing entry, it will update the entry. |
| 421 | * @param set_ttl If the value is PJ_FALSE, the entry will not expire |
| 422 | * (so use with care). Otherwise cache expiration will be |
| 423 | * calculated based on the TTL of the answeres. |
| 424 | * |
| 425 | * @return PJ_SUCCESS on success, or the appropriate error code. |
| 426 | */ |
| 427 | PJ_DECL(pj_status_t) pj_dns_resolver_add_entry(pj_dns_resolver *resolver, |
| 428 | const pj_dns_parsed_packet *pkt, |
| 429 | pj_bool_t set_ttl); |
| 430 | |
| 431 | /** |
| 432 | * Get the total number of response in the response cache. |
| 433 | * |
| 434 | * @param resolver The resolver instance. |
| 435 | * |
| 436 | * @return Current number of entries being stored in the response |
| 437 | * cache. |
| 438 | */ |
| 439 | PJ_DECL(unsigned) pj_dns_resolver_get_cached_count(pj_dns_resolver *resolver); |
| 440 | |
| 441 | |
| 442 | /** |
| 443 | * Dump resolver state to the log. |
| 444 | * |
| 445 | * @param resolver The resolver instance. |
| 446 | * @param detail Will print detailed entries. |
| 447 | */ |
| 448 | PJ_DECL(void) pj_dns_resolver_dump(pj_dns_resolver *resolver, |
| 449 | pj_bool_t detail); |
| 450 | |
| 451 | |
| 452 | /** |
| 453 | * @} |
| 454 | */ |
| 455 | |
| 456 | PJ_END_DECL |
| 457 | |
| 458 | |
| 459 | #endif /* __PJLIB_UTIL_RESOLVER_H__ */ |
| 460 | |