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