?? resolver.c
字號:
/* $Id: resolver.c 1033 2007-03-02 14:51:03Z bennylp $ *//* * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * 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 */#include <pjlib-util/resolver.h>#include <pjlib-util/errno.h>#include <pj/assert.h>#include <pj/ctype.h>#include <pj/except.h>#include <pj/hash.h>#include <pj/ioqueue.h>#include <pj/log.h>#include <pj/os.h>#include <pj/pool.h>#include <pj/pool_buf.h>#include <pj/string.h>#include <pj/sock.h>#include <pj/timer.h>#define THIS_FILE "resolver.c"/* Check that maximum DNS nameservers is not too large. * This has got todo with the datatype to index the nameserver in the query. */#if PJ_DNS_RESOLVER_MAX_NS > 256# error "PJ_DNS_RESOLVER_MAX_NS is too large (max=256)"#endif#define RES_HASH_TABLE_SIZE 127 /**< Hash table size (must be 2^n-1 */#define PORT 53 /**< Default NS port. */#define Q_HASH_TABLE_SIZE 127 /**< Query hash table size */#define TIMER_SIZE 127 /**< Initial number of timers. */#define MAX_FD 3 /**< Maximum internal sockets. */#define RES_BUF_SZ PJ_DNS_RESOLVER_RES_BUF_SIZE#define UDPSZ PJ_DNS_RESOLVER_MAX_UDP_SIZE#define TMP_SZ PJ_DNS_RESOLVER_TMP_BUF_SIZE/* Nameserver state */enum ns_state{ STATE_PROBING, STATE_ACTIVE, STATE_BAD,};/* * Each nameserver entry. * A name server is identified by its socket address (IP and port). * Each NS will have a flag to indicate whether it's properly functioning. */struct nameserver{ pj_sockaddr_in addr; /**< Server address. */ enum ns_state state; /**< Nameserver state. */ pj_time_val state_expiry; /**< Time set next state. */ pj_time_val rt_delay; /**< Response time. */ /* For calculating rt_delay: */ pj_uint16_t q_id; /**< Query ID. */ pj_time_val sent_time; /**< Time this query is sent. */};/* Child query list head * See comments on pj_dns_async_query below. */struct query_head{ PJ_DECL_LIST_MEMBER(pj_dns_async_query);};/* Key to look for outstanding query and/or cached response */struct res_key{ pj_uint16_t qtype; /**< Query type. */ char name[PJ_MAX_HOSTNAME]; /**< Name being queried */};/* * This represents each asynchronous query entry. * This entry will be put in two hash tables, the first one keyed on the DNS * transaction ID to match response with the query, and the second one keyed * on "res_key" structure above to match a new request against outstanding * requests. * * An asynchronous entry may have child entries; child entries are subsequent * queries to the same resource while there is pending query on the same * DNS resource name and type. When a query has child entries, once the * response is received (or error occurs), the response will trigger callback * invocations for all childs entries. * * Note: when application cancels the query, the callback member will be * set to NULL, but for simplicity, the query will be let running. */struct pj_dns_async_query{ PJ_DECL_LIST_MEMBER(pj_dns_async_query); /**< List member. */ pj_dns_resolver *resolver; /**< The resolver instance. */ pj_uint16_t id; /**< Transaction ID. */ unsigned transmit_cnt; /**< Number of transmissions. */ struct res_key key; /**< Key to index this query. */ char hbufid[PJ_HASH_ENTRY_SIZE]; /**< Hash buffer 1 */ char hbufkey[PJ_HASH_ENTRY_SIZE]; /**< Hash buffer 2 */ pj_timer_entry timer_entry; /**< Timer to manage timeouts */ unsigned options; /**< Query options. */ void *user_data; /**< Application data. */ pj_dns_callback *cb; /**< Callback to be called. */ struct query_head child_head; /**< Child queries list head. */};/* This structure is used to keep cached response entry. * The cache is a hash table keyed on "res_key" structure above. */struct cached_res{ PJ_DECL_LIST_MEMBER(struct cached_res); struct res_key key; /**< Resource key. */ char buf[RES_BUF_SZ];/**< Resource buffer. */ char hbuf[PJ_HASH_ENTRY_SIZE]; /**< Hash buffer */ pj_time_val expiry_time; /**< Expiration time. */ pj_dns_parsed_packet *pkt; /**< The response packet. */};/* Resolver entry */struct pj_dns_resolver{ pj_str_t name; /**< Resolver instance name for id. */ /* Internals */ pj_pool_t *pool; /**< Internal pool. */ pj_mutex_t *mutex; /**< Mutex protection. */ pj_bool_t own_timer; /**< Do we own timer? */ pj_timer_heap_t *timer; /**< Timer instance. */ pj_bool_t own_ioqueue; /**< Do we own ioqueue? */ pj_ioqueue_t *ioqueue; /**< Ioqueue instance. */ char tmp_pool[TMP_SZ];/**< Temporary pool buffer. */ /* Socket */ pj_sock_t udp_sock; /**< UDP socket. */ pj_ioqueue_key_t *udp_key; /**< UDP socket ioqueue key. */ unsigned char udp_rx_pkt[UDPSZ];/**< UDP receive buffer. */ unsigned char udp_tx_pkt[UDPSZ];/**< UDP receive buffer. */ pj_ssize_t udp_len; /**< Length of received packet. */ pj_ioqueue_op_key_t udp_op_key; /**< UDP read operation key. */ pj_sockaddr_in udp_src_addr; /**< Source address of packet */ int udp_addr_len; /**< Source address length. */ /* Settings */ pj_dns_settings settings; /**< Resolver settings. */ /* Nameservers */ unsigned ns_count; /**< Number of name servers. */ struct nameserver ns[PJ_DNS_RESOLVER_MAX_NS]; /**< Array of NS. */ /* Last DNS transaction ID used. */ pj_uint16_t last_id; /* Hash table for cached response */ pj_hash_table_t *hrescache; /**< Cached response in hash table */ /* Cached response free list */ struct cached_res res_free_nodes; /* Pending asynchronous query, hashed by transaction ID. */ pj_hash_table_t *hquerybyid; /* Pending asynchronous query, hashed by "res_key" */ pj_hash_table_t *hquerybyres; /* Query entries free list */ struct query_head query_free_nodes;};/* Callback from ioqueue when packet is received */static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read);/* Callback to be called when query has timed out */static void on_timeout( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry);/* Select which nameserver to use */static pj_status_t select_nameservers(pj_dns_resolver *resolver, unsigned *count, unsigned servers[]);/* * Create the resolver. */PJ_DEF(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_pool_t *pool; pj_dns_resolver *resv; pj_ioqueue_callback socket_cb; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(pf && p_resolver, PJ_EINVAL); if (name == NULL) name = THIS_FILE; /* Create and initialize resolver instance */ pool = pj_pool_create(pf, name, 4000, 4000, NULL); if (!pool) return PJ_ENOMEM; /* Create pool and name */ resv = pj_pool_zalloc(pool, sizeof(struct pj_dns_resolver)); resv->pool = pool; resv->udp_sock = PJ_INVALID_SOCKET; pj_strdup2_with_null(pool, &resv->name, name); /* Create the mutex */ status = pj_mutex_create_recursive(pool, name, &resv->mutex); if (status != PJ_SUCCESS) goto on_error; /* Timer, ioqueue, and settings */ resv->timer = timer; resv->ioqueue = ioqueue; resv->last_id = 1; resv->settings.options = options; resv->settings.qretr_delay = PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY; resv->settings.qretr_count = PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT; resv->settings.cache_max_ttl = PJ_DNS_RESOLVER_MAX_TTL; /* Create the timer heap if one is not specified */ if (resv->timer == NULL) { status = pj_timer_heap_create(pool, TIMER_SIZE, &resv->timer); if (status != PJ_SUCCESS) goto on_error; } /* Create the ioqueue if one is not specified */ if (resv->ioqueue == NULL) { status = pj_ioqueue_create(pool, MAX_FD, &resv->ioqueue); if (status != PJ_SUCCESS) goto on_error; } /* Response cache hash table and item list */ resv->hrescache = pj_hash_create(pool, RES_HASH_TABLE_SIZE); pj_list_init(&resv->res_free_nodes); /* Query hash table and free list. */ resv->hquerybyid = pj_hash_create(pool, Q_HASH_TABLE_SIZE); resv->hquerybyres = pj_hash_create(pool, Q_HASH_TABLE_SIZE); pj_list_init(&resv->query_free_nodes); /* Create the UDP socket */ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &resv->udp_sock); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&socket_cb, sizeof(socket_cb)); socket_cb.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock(pool, resv->ioqueue, resv->udp_sock, resv, &socket_cb, &resv->udp_key); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&resv->udp_op_key, sizeof(resv->udp_op_key)); /* Start asynchronous read to the UDP socket */ resv->udp_len = sizeof(resv->udp_rx_pkt); resv->udp_addr_len = sizeof(resv->udp_src_addr); status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_key, resv->udp_rx_pkt, &resv->udp_len, PJ_IOQUEUE_ALWAYS_ASYNC, &resv->udp_src_addr, &resv->udp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Looks like everything is okay */ *p_resolver = resv; return PJ_SUCCESS;on_error: pj_dns_resolver_destroy(resv, PJ_FALSE); return status;}/* * Destroy DNS resolver instance. */PJ_DEF(pj_status_t) pj_dns_resolver_destroy( pj_dns_resolver *resolver, pj_bool_t notify){ PJ_ASSERT_RETURN(resolver, PJ_EINVAL); if (notify) { /* * Notify pending queries if requested. */ pj_hash_iterator_t it_buf, *it; it = pj_hash_first(resolver->hquerybyid, &it_buf); while (it) { pj_dns_async_query *q = pj_hash_this(resolver->hquerybyid, it); pj_dns_async_query *cq; if (q->cb) (*q->cb)(q->user_data, PJ_ECANCELLED, NULL); cq = q->child_head.next; while (cq != (pj_dns_async_query*)&q->child_head) { if (cq->cb) (*cq->cb)(cq->user_data, PJ_ECANCELLED, NULL); cq = cq->next; } it = pj_hash_next(resolver->hquerybyid, it); } } if (resolver->own_timer && resolver->timer) { pj_timer_heap_destroy(resolver->timer); resolver->timer = NULL; } if (resolver->own_ioqueue && resolver->ioqueue) { pj_ioqueue_destroy(resolver->ioqueue); resolver->ioqueue = NULL; } if (resolver->udp_key != NULL) { pj_ioqueue_unregister(resolver->udp_key); resolver->udp_key = NULL; resolver->udp_sock = PJ_INVALID_SOCKET; } else if (resolver->udp_sock != PJ_INVALID_SOCKET) { pj_sock_close(resolver->udp_sock); resolver->udp_sock = PJ_INVALID_SOCKET; } if (resolver->mutex) { pj_mutex_destroy(resolver->mutex); resolver->mutex = NULL; } if (resolver->pool) { pj_pool_t *pool = resolver->pool; resolver->pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS;}/* * Configure name servers for the DNS resolver. */PJ_DEF(pj_status_t) pj_dns_resolver_set_ns( pj_dns_resolver *resolver, unsigned count, const pj_str_t servers[], const pj_uint16_t ports[]){ unsigned i; pj_time_val now; pj_status_t status; PJ_ASSERT_RETURN(resolver && count && servers, PJ_EINVAL); pj_mutex_lock(resolver->mutex); if (count > PJ_DNS_RESOLVER_MAX_NS) count = PJ_DNS_RESOLVER_MAX_NS; resolver->ns_count = 0; pj_bzero(resolver->ns, sizeof(resolver->ns)); pj_gettimeofday(&now); for (i=0; i<count; ++i) { struct nameserver *ns = &resolver->ns[i]; status = pj_sockaddr_in_init(&ns->addr, &servers[i], (pj_uint16_t)(ports ? ports[i] : PORT)); if (status != PJ_SUCCESS) { pj_mutex_unlock(resolver->mutex); return PJLIB_UTIL_EDNSINNSADDR; } ns->state = STATE_ACTIVE; ns->state_expiry = now; ns->rt_delay.sec = 10; } resolver->ns_count = count; pj_mutex_unlock(resolver->mutex); return PJ_SUCCESS;}/* * Modify the resolver settings. */PJ_DEF(pj_status_t) pj_dns_resolver_set_settings(pj_dns_resolver *resolver, const pj_dns_settings *st){ PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL); pj_mutex_lock(resolver->mutex); pj_memcpy(&resolver->settings, st, sizeof(*st)); pj_mutex_unlock(resolver->mutex); return PJ_SUCCESS;}/* * Get the resolver current settings. */PJ_DEF(pj_status_t) pj_dns_resolver_get_settings( pj_dns_resolver *resolver, pj_dns_settings *st){ PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL); pj_mutex_lock(resolver->mutex); pj_memcpy(st, &resolver->settings, sizeof(*st)); pj_mutex_unlock(resolver->mutex); return PJ_SUCCESS;}/* * Poll for events from the resolver. */PJ_DEF(void) pj_dns_resolver_handle_events(pj_dns_resolver *resolver, const pj_time_val *timeout){
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -