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

📄 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 + -