⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 resolver.c

📁 基于sip协议的网络电话源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $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 + -