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

📄 sip_resolve.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: sip_resolve.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 <pjsip/sip_resolve.h>
#include <pjsip/sip_transport.h>
#include <pjsip/sip_errno.h>
#include <pjlib-util/errno.h>
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/ctype.h>
#include <pj/log.h>
#include <pj/pool.h>
#include <pj/rand.h>
#include <pj/string.h>


#define THIS_FILE   "sip_resolve.c"

#define ADDR_MAX_COUNT	    8

struct naptr_target
{
    pj_str_t		    target_name;    /**< NAPTR target name. */
    pjsip_transport_type_e  type;	    /**< Transport type.    */
    unsigned		    order;	    /**< Order		    */
    unsigned		    pref;	    /**< Preference.	    */
};

struct srv_target
{
    pjsip_transport_type_e  type;
    pj_str_t		    target_name;
    char		    target_buf[PJ_MAX_HOSTNAME];
    unsigned		    port;
    unsigned		    priority;
    unsigned		    weight;
    unsigned		    sum;
    unsigned		    addr_cnt;
    pj_in_addr		    addr[ADDR_MAX_COUNT];
};

struct query
{
    char		     objname[PJ_MAX_OBJ_NAME];

    pjsip_resolver_t	    *resolver;	    /**< Resolver SIP instance.	    */
    pj_dns_type		     dns_state;	    /**< DNS type being resolved.   */
    void		    *token;
    pjsip_resolver_callback *cb;
    pj_dns_async_query	    *object;
    pj_status_t		     last_error;

    /* Original request: */
    struct {
	pjsip_host_info	     target;
    } req;

    /* NAPTR records: */
    unsigned		     naptr_cnt;
    struct naptr_target	     naptr[8];

    /* SRV records and their resolved IP addresses: */
    unsigned		     srv_cnt;
    struct srv_target	     srv[PJSIP_MAX_RESOLVED_ADDRESSES];

    /* Number of hosts in SRV records that the IP address has been resolved */
    unsigned		     host_resolved;
};


struct pjsip_resolver_t
{
    pj_dns_resolver *res;
    unsigned	     job_id;
};

static void dns_callback(void *user_data,
			 pj_status_t status,
			 pj_dns_parsed_packet *response);


/*
 * Public API to create the resolver.
 */
PJ_DEF(pj_status_t) pjsip_resolver_create( pj_pool_t *pool,
					   pjsip_resolver_t **p_res)
{
    pjsip_resolver_t *resolver;

    PJ_ASSERT_RETURN(pool && p_res, PJ_EINVAL);
    resolver = pj_pool_zalloc(pool, sizeof(*resolver));
    *p_res = resolver;

    return PJ_SUCCESS;
}


/*
 * Public API to set the DNS resolver instance for the SIP resolver.
 */
PJ_DEF(pj_status_t) pjsip_resolver_set_resolver(pjsip_resolver_t *res,
						pj_dns_resolver *dns_res)
{
#if PJSIP_HAS_RESOLVER
    res->res = dns_res;
    return PJ_SUCCESS;
#else
    PJ_UNUSED_ARG(res);
    PJ_UNUSED_ARG(dns_res);
    pj_assert(!"Resolver is disabled (PJSIP_HAS_RESOLVER==0)");
    return PJ_EINVALIDOP;
#endif
}


/*
 * Public API to get the internal DNS resolver.
 */
PJ_DEF(pj_dns_resolver*) pjsip_resolver_get_resolver(pjsip_resolver_t *res)
{
    return res->res;
}


/*
 * Public API to create destroy the resolver
 */
PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver)
{
    if (resolver->res) {
#if PJSIP_HAS_RESOLVER
	pj_dns_resolver_destroy(resolver->res, PJ_FALSE);
#endif
	resolver->res = NULL;
    }
}

/*
 * Internal:
 *  determine if an address is a valid IP address.
 */
static int is_str_ip(const pj_str_t *host)
{
    const char *p = host->ptr;
    const char *end = ((const char*)host->ptr) + host->slen;

    while (p != end) {
	if (pj_isdigit(*p) || *p=='.') {
	    ++p;
	} else {
	    return 0;
	}
    }
    return 1;
}


/*
 * This is the main function for performing server resolution.
 */
PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
			    pj_pool_t *pool,
			    const pjsip_host_info *target,
			    void *token,
			    pjsip_resolver_callback *cb)
{
    pjsip_server_addresses svr_addr;
    pj_status_t status = PJ_SUCCESS;
    int is_ip_addr;
    struct query *query;
    pj_str_t srv_name;
    pjsip_transport_type_e type = target->type;

    /* Is it IP address or hostname?. */
    is_ip_addr = is_str_ip(&target->addr.host);

    /* Set the transport type if not explicitly specified. 
     * RFC 3263 section 4.1 specify rules to set up this.
     */
    if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
	if (is_ip_addr || (target->addr.port != 0)) {
#if PJ_HAS_TCP
	    if (target->flag & PJSIP_TRANSPORT_SECURE) 
	    {
		type = PJSIP_TRANSPORT_TLS;
	    } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) 
	    {
		type = PJSIP_TRANSPORT_TCP;
	    } else 
#endif
	    {
		type = PJSIP_TRANSPORT_UDP;
	    }
	} else {
	    /* No type or explicit port is specified, and the address is
	     * not IP address.
	     * In this case, full resolution must be performed.
	     * But we don't support it (yet).
	     */
	    type = PJSIP_TRANSPORT_UDP;
	}
    }


    /* If target is an IP address, or if resolver is not configured, 
     * we can just finish the resolution now using pj_gethostbyname()
     */
    if (is_ip_addr || resolver->res == NULL) {

	pj_in_addr ip_addr;
	pj_uint16_t srv_port;

	if (!is_ip_addr) {
	    PJ_LOG(5,(THIS_FILE, 
		      "DNS resolver not available, target '%.*s:%d' type=%s "
		      "will be resolved with gethostbyname()",
		      target->addr.host.slen,
		      target->addr.host.ptr,
		      target->addr.port,
		      pjsip_transport_get_type_name(target->type)));
	}

	/* Set the port number if not specified. */
	if (target->addr.port == 0) {
	   srv_port = (pj_uint16_t)
		      pjsip_transport_get_default_port_for_type(type);
	} else {
	   srv_port = (pj_uint16_t)target->addr.port;
	}

	/* This will eventually call pj_gethostbyname() if the host
	 * is not an IP address.
	 */
	status = pj_sockaddr_in_init((pj_sockaddr_in*)&svr_addr.entry[0].addr,
				      &target->addr.host, srv_port);
	if (status != PJ_SUCCESS)
	    goto on_error;

	/* Call the callback. */
	ip_addr = ((pj_sockaddr_in*)&svr_addr.entry[0].addr)->sin_addr;
	PJ_LOG(5,(THIS_FILE, 
		  "Target '%.*s:%d' type=%s resolved to "
		  "'%s:%d' type=%s",
		  (int)target->addr.host.slen,
		  target->addr.host.ptr,
		  target->addr.port,
		  pjsip_transport_get_type_name(target->type),
		  pj_inet_ntoa(ip_addr),
		  srv_port,
		  pjsip_transport_get_type_name(type)));
	svr_addr.count = 1;
	svr_addr.entry[0].priority = 0;
	svr_addr.entry[0].weight = 0;
	svr_addr.entry[0].type = type;
	svr_addr.entry[0].addr_len = sizeof(pj_sockaddr_in);
	(*cb)(status, token, &svr_addr);

	/* Done. */
	return;
    }

    /* Target is not an IP address so we need to resolve it. */
#if PJSIP_HAS_RESOLVER

    /* Build the query state */
    query = pj_pool_zalloc(pool, sizeof(struct query));
    pj_ansi_snprintf(query->objname, sizeof(query->objname), "rsvjob%X",
		     resolver->job_id++);
    query->resolver = resolver;
    query->token = token;
    query->cb = cb;
    query->req.target = *target;
    pj_strdup(pool, &query->req.target.addr.host, &target->addr.host);

    /* If port is not specified, start with SRV resolution
     * (should be with NAPTR, but we'll do that later)
     */
    PJ_TODO(SUPPORT_DNS_NAPTR);

    /* Build dummy NAPTR entry */
    query->naptr_cnt = 1;
    pj_bzero(&query->naptr[0], sizeof(query->naptr[0]));
    query->naptr[0].order = 0;
    query->naptr[0].pref = 0;
    query->naptr[0].type = type;
    query->naptr[0].target_name.ptr = 
	pj_pool_alloc(pool, target->addr.host.slen + 12);

    if (type == PJSIP_TRANSPORT_TLS)
	pj_strcpy2(&query->naptr[0].target_name, "_sips._tcp.");
    else if (type == PJSIP_TRANSPORT_TCP)
	pj_strcpy2(&query->naptr[0].target_name, "_sip._tcp.");
    else if (type == PJSIP_TRANSPORT_UDP)
	pj_strcpy2(&query->naptr[0].target_name, "_sip._udp.");
    else {
	pj_assert(!"Unknown transport type");
	pj_strcpy2(&query->naptr[0].target_name, "_sip._udp.");
    }
    pj_strcat(&query->naptr[0].target_name, &target->addr.host);


    /* Start DNS SRV or A resolution, depending on whether port is specified */
    if (target->addr.port == 0) {
	query->dns_state = PJ_DNS_TYPE_SRV;
	srv_name = query->naptr[0].target_name;

    } else {
	/* Otherwise if port is specified, start with A (or AAAA) host 
	 * resolution 
	 */
	query->dns_state = PJ_DNS_TYPE_A;

	/* Since we don't perform SRV resolution, pretend that we'ee already
	 * done so by inserting a dummy SRV record.
	 */

	query->srv_cnt = 1;
	pj_bzero(&query->srv[0], sizeof(query->srv[0]));
	query->srv[0].target_name = query->req.target.addr.host;
	query->srv[0].type = type;
	query->srv[0].port = query->req.target.addr.port;
	query->srv[0].priority = 0;
	query->srv[0].weight = 0;

	srv_name = query->srv[0].target_name;
    }

    /* Start the asynchronous query */
    PJ_LOG(5, (query->objname, 
	       "Starting async DNS %s query: target=%.*s, transport=%s, "
	       "port=%d",
	       pj_dns_get_type_name(query->dns_state),
	       (int)srv_name.slen, srv_name.ptr,
	       pjsip_transport_get_type_name(target->type),
	       target->addr.port));

    status = pj_dns_resolver_start_query(resolver->res, &srv_name, 
				         query->dns_state, 0, &dns_callback,
    					 query, &query->object);
    if (status != PJ_SUCCESS)
	goto on_error;

    return;

#else /* PJSIP_HAS_RESOLVER */
    PJ_UNUSED_ARG(pool);
    PJ_UNUSED_ARG(query);
    PJ_UNUSED_ARG(srv_name);
#endif /* PJSIP_HAS_RESOLVER */

on_error:
    if (status != PJ_SUCCESS) {
	char errmsg[PJ_ERR_MSG_SIZE];
	PJ_LOG(4,(THIS_FILE, "Failed to resolve '%.*s'. Err=%d (%s)",
			     (int)target->addr.host.slen,
			     target->addr.host.ptr,
			     status,
			     pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
	(*cb)(status, token, NULL);
	return;
    }
}

/*
 * The rest of the code should only get compiled when resolver is enabled
 */
#if PJSIP_HAS_RESOLVER

#define SWAP(type,ptr1,ptr2)	if (ptr1 != ptr2) { \
				  type tmp; \
				  pj_memcpy(&tmp, ptr1, sizeof(type)); \
				  pj_memcpy(ptr1, ptr2, sizeof(type)); \
				  (ptr1)->target_name.ptr = (ptr1)->target_buf; \
				  pj_memcpy(ptr2, &tmp, sizeof(type)); \
				  (ptr2)->target_name.ptr = (ptr2)->target_buf; \
				} else {}

/* Build server entries in the query based on received SRV response */
static void build_server_entries(struct query *query, 
				 pj_dns_parsed_packet *response)
{
    unsigned i;
    unsigned naptr_id;

    /* Find NAPTR target which corresponds to this SRV target */
    for (naptr_id=0; naptr_id < query->naptr_cnt; ++naptr_id) {
	if (pj_stricmp(&query->naptr[naptr_id].target_name,
		       &response->ans[0].name)==0)
	    break;
    }
    if (naptr_id == query->naptr_cnt) {
	PJ_LOG(4,(query->objname, 
		  "Unable to find NAPTR record for SRV name %.*s!",
		  (int)response->ans[0].name.slen, 
		  response->ans[0].name.ptr));
	return;
    }


    /* Save the Resource Records in DNS answer into SRV targets. */
    query->srv_cnt = 0;
    for (i=0; i<response->hdr.anscount && 
	      query->srv_cnt < PJSIP_MAX_RESOLVED_ADDRESSES; ++i) 
    {
	pj_dns_parsed_rr *rr = &response->ans[i];
	struct srv_target *srv = &query->srv[query->srv_cnt];

	if (rr->type != PJ_DNS_TYPE_SRV) {
	    PJ_LOG(4,(query->objname, 
		      "Received non SRV answer for SRV query!"));
	    continue;
	}

	if (rr->rdata.srv.target.slen > PJ_MAX_HOSTNAME) {
	    PJ_LOG(4,(query->objname, "Hostname is too long!"));
	    continue;
	}

	/* Build the SRV entry for RR */
	pj_bzero(srv, sizeof(*srv));
	pj_memcpy(srv->target_buf, rr->rdata.srv.target.ptr, 
		  rr->rdata.srv.target.slen);
	srv->target_name.ptr = srv->target_buf;
	srv->target_name.slen = rr->rdata.srv.target.slen;
	srv->type = query->naptr[naptr_id].type;
	srv->port = rr->rdata.srv.port;
	srv->priority = rr->rdata.srv.prio;
	srv->weight = rr->rdata.srv.weight;
	
	++query->srv_cnt;
    }

    /* First pass: 
     *	order the entries based on priority.
     */
    for (i=0; i<query->srv_cnt-1; ++i) {
	unsigned min = i, j;
	for (j=i+1; j<query->srv_cnt; ++j) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -