📄 sip_resolve.c
字号:
/* $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 + -