📄 sip_resolve.c
字号:
if (query->srv[j].priority < query->srv[min].priority)
min = j;
}
SWAP(struct srv_target, &query->srv[i], &query->srv[min]);
}
/* Second pass:
* pick one host among hosts with the same priority, according
* to its weight. The idea is when one server fails, client should
* contact the next server with higher priority rather than contacting
* server with the same priority as the failed one.
*
* The algorithm for selecting server among servers with the same
* priority is described in RFC 2782.
*/
for (i=0; i<query->srv_cnt; ++i) {
unsigned j, count=1, sum;
/* Calculate running sum for servers with the same priority */
sum = query->srv[i].sum = query->srv[i].weight;
for (j=i+1; j<query->srv_cnt &&
query->srv[j].priority == query->srv[i].priority; ++j)
{
sum += query->srv[j].weight;
query->srv[j].sum = sum;
++count;
}
if (count > 1) {
unsigned r;
/* Elect one random number between zero and the total sum of
* weight (inclusive).
*/
r = pj_rand() % (sum + 1);
/* Select the first server which running sum is greater than or
* equal to the random number.
*/
for (j=i; j<i+count; ++j) {
if (query->srv[j].sum >= r)
break;
}
/* Must have selected one! */
pj_assert(j != i+count);
/* Put this entry in front (of entries with same priority) */
SWAP(struct srv_target, &query->srv[i], &query->srv[j]);
/* Remove all other entries (of the same priority) */
while (count > 1) {
pj_array_erase(query->srv, sizeof(struct srv_target),
query->srv_cnt, i+1);
--count;
--query->srv_cnt;
}
}
}
/* Since we've been moving around SRV entries, update the pointers
* in target_name.
*/
for (i=0; i<query->srv_cnt; ++i) {
query->srv[i].target_name.ptr = query->srv[i].target_buf;
}
/* Check for Additional Info section if A records are available, and
* fill in the IP address (so that we won't need to resolve the A
* record with another DNS query).
*/
for (i=0; i<response->hdr.arcount; ++i) {
pj_dns_parsed_rr *rr = &response->arr[i];
unsigned j;
if (rr->type != PJ_DNS_TYPE_A)
continue;
/* Yippeaiyee!! There is an "A" record!
* Update the IP address of the corresponding SRV record.
*/
for (j=0; j<query->srv_cnt; ++j) {
if (pj_stricmp(&rr->name, &query->srv[j].target_name)==0) {
unsigned cnt = query->srv[j].addr_cnt;
query->srv[j].addr[cnt].s_addr = rr->rdata.a.ip_addr.s_addr;
++query->srv[j].addr_cnt;
++query->host_resolved;
break;
}
}
/* Not valid message; SRV entry might have been deleted in
* server selection process.
*/
/*
if (j == query->srv_cnt) {
PJ_LOG(4,(query->objname,
"Received DNS SRV answer with A record, but "
"couldn't find matching name (name=%.*s)",
(int)rr->name.slen,
rr->name.ptr));
}
*/
}
/* Rescan again the name specified in the SRV record to see if IP
* address is specified as the target name (unlikely, but well, who
* knows..).
*/
for (i=0; i<query->srv_cnt; ++i) {
pj_in_addr addr;
if (query->srv[i].addr_cnt != 0) {
/* IP address already resolved */
continue;
}
if (pj_inet_aton(&query->srv[i].target_name, &addr) != 0) {
query->srv[i].addr[query->srv[i].addr_cnt++] = addr;
++query->host_resolved;
}
}
/* Print resolved entries to the log */
PJ_LOG(5,(query->objname,
"SRV query for %.*s completed, "
"%d of %d total entries selected%c",
(int)query->naptr[naptr_id].target_name.slen,
query->naptr[naptr_id].target_name.ptr,
query->srv_cnt,
response->hdr.anscount,
(query->srv_cnt ? ':' : ' ')));
for (i=0; i<query->srv_cnt; ++i) {
const char *addr;
if (query->srv[i].addr_cnt != 0)
addr = pj_inet_ntoa(query->srv[i].addr[0]);
else
addr = "-";
PJ_LOG(5,(query->objname,
" %d: SRV %d %d %d %.*s (%s)",
i, query->srv[i].priority,
query->srv[i].weight,
query->srv[i].port,
(int)query->srv[i].target_name.slen,
query->srv[i].target_name.ptr,
addr));
}
}
/* Start DNS A record queries for all SRV records in the query structure */
static pj_status_t resolve_hostnames(struct query *query)
{
unsigned i;
pj_status_t err=PJ_SUCCESS, status;
query->dns_state = PJ_DNS_TYPE_A;
for (i=0; i<query->srv_cnt; ++i) {
PJ_LOG(5, (query->objname,
"Starting async DNS A query for %.*s",
(int)query->srv[i].target_name.slen,
query->srv[i].target_name.ptr));
status = pj_dns_resolver_start_query(query->resolver->res,
&query->srv[i].target_name,
PJ_DNS_TYPE_A, 0,
&dns_callback,
query, NULL);
if (status != PJ_SUCCESS) {
query->host_resolved++;
err = status;
}
}
return (query->host_resolved == query->srv_cnt) ? err : PJ_SUCCESS;
}
/*
* This callback is called by PJLIB-UTIL DNS resolver when asynchronous
* query has completed (successfully or with error).
*/
static void dns_callback(void *user_data,
pj_status_t status,
pj_dns_parsed_packet *pkt)
{
struct query *query = user_data;
unsigned i;
/* Proceed to next stage */
if (query->dns_state == PJ_DNS_TYPE_SRV) {
/* We are getting SRV response */
if (status == PJ_SUCCESS && pkt->hdr.anscount != 0) {
/* Got SRV response, build server entry. If A records are available
* in additional records section of the DNS response, save them too.
*/
build_server_entries(query, pkt);
} else if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
unsigned naptr_id;
/* Update query last error */
query->last_error = status;
/* Find which NAPTR target has not got SRV records */
for (naptr_id=0; naptr_id < query->naptr_cnt; ++naptr_id) {
for (i=0; i<query->srv_cnt; ++i) {
if (query->srv[i].type == query->naptr[naptr_id].type)
break;
}
if (i == query->srv_cnt)
break;
}
if (naptr_id == query->naptr_cnt) {
/* Strangely all NAPTR records seem to already have SRV
* records! This is quite unexpected, by anyway lets set
* the naptr_id to zero just in case.
*/
pj_assert(!"Strange");
naptr_id = 0;
}
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(query->objname,
"DNS SRV resolution failed for %.*s: %s",
(int)query->naptr[naptr_id].target_name.slen,
query->naptr[naptr_id].target_name.ptr,
errmsg));
}
/* If we can't build SRV record, assume the original target is
* an A record.
*/
if (query->srv_cnt == 0) {
/* Looks like we aren't getting any SRV responses.
* Resolve the original target as A record by creating a
* single "dummy" srv record and start the hostname resolution.
*/
unsigned naptr_id;
/* Find which NAPTR target has not got SRV records */
for (naptr_id=0; naptr_id < query->naptr_cnt; ++naptr_id) {
for (i=0; i<query->srv_cnt; ++i) {
if (query->srv[i].type == query->naptr[naptr_id].type)
break;
}
if (i == query->srv_cnt)
break;
}
if (naptr_id == query->naptr_cnt) {
/* Strangely all NAPTR records seem to already have SRV
* records! This is quite unexpected, by anyway lets set
* the naptr_id to zero just in case.
*/
pj_assert(!"Strange");
naptr_id = 0;
}
PJ_LOG(4, (query->objname,
"DNS SRV resolution failed for %.*s, trying "
"resolving A record for %.*s",
(int)query->naptr[naptr_id].target_name.slen,
query->naptr[naptr_id].target_name.ptr,
(int)query->req.target.addr.host.slen,
query->req.target.addr.host.ptr));
/* Create a "dummy" srv record using the original target */
i = query->srv_cnt++;
pj_bzero(&query->srv[i], sizeof(query->srv[i]));
query->srv[i].target_name = query->req.target.addr.host;
query->srv[i].type = query->naptr[naptr_id].type;
query->srv[i].priority = 0;
query->srv[i].weight = 0;
query->srv[i].port = query->req.target.addr.port;
if (query->srv[i].port == 0) {
query->srv[i].port = (pj_uint16_t)
pjsip_transport_get_default_port_for_type(query->srv[i].type);
}
}
/* Resolve server hostnames (DNS A record) for hosts which don't have
* A record yet.
*/
if (query->host_resolved != query->srv_cnt) {
status = resolve_hostnames(query);
if (status != PJ_SUCCESS)
goto on_error;
/* Must return now. Callback may have been called and query
* may have been destroyed.
*/
return;
}
} else if (query->dns_state == PJ_DNS_TYPE_A) {
/* Check that we really have answer */
if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
/* Update IP address of the corresponding hostname */
for (i=0; i<query->srv_cnt; ++i) {
if (pj_stricmp(&pkt->ans[0].name,
&query->srv[i].target_name)==0)
{
break;
}
}
if (i == query->srv_cnt) {
PJ_LOG(4,(query->objname,
"Received answer to DNS A request with no matching "
"SRV record! The unknown name is %.*s",
(int)pkt->ans[0].name.slen, pkt->ans[0].name.ptr));
} else {
unsigned j;
query->srv[i].addr[query->srv[i].addr_cnt++].s_addr =
pkt->ans[0].rdata.a.ip_addr.s_addr;
PJ_LOG(5,(query->objname,
"DNS A for %.*s: %s",
(int)query->srv[i].target_name.slen,
query->srv[i].target_name.ptr,
pj_inet_ntoa(pkt->ans[0].rdata.a.ip_addr)));
/* Check for multiple IP addresses */
for (j=1; j<pkt->hdr.anscount &&
query->srv[i].addr_cnt < ADDR_MAX_COUNT; ++j)
{
query->srv[i].addr[query->srv[i].addr_cnt++].s_addr =
pkt->ans[j].rdata.a.ip_addr.s_addr;
PJ_LOG(5,(query->objname,
"Additional DNS A for %.*s: %s",
(int)query->srv[i].target_name.slen,
query->srv[i].target_name.ptr,
pj_inet_ntoa(pkt->ans[j].rdata.a.ip_addr)));
}
}
} else if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
/* Update last error */
query->last_error = status;
/* Log error */
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s",
errmsg));
}
++query->host_resolved;
} else {
pj_assert(!"Unexpected state!");
query->last_error = status = PJ_EINVALIDOP;
goto on_error;
}
/* Check if all hosts have been resolved */
if (query->host_resolved == query->srv_cnt) {
/* Got all answers, build server addresses */
pjsip_server_addresses svr_addr;
svr_addr.count = 0;
for (i=0; i<query->srv_cnt; ++i) {
unsigned j;
/* Do we have IP address for this server? */
/* This log is redundant really.
if (query->srv[i].addr_cnt == 0) {
PJ_LOG(5,(query->objname,
" SRV target %.*s:%d does not have IP address!",
(int)query->srv[i].target_name.slen,
query->srv[i].target_name.ptr,
query->srv[i].port));
continue;
}
*/
for (j=0; j<query->srv[i].addr_cnt; ++j) {
unsigned idx = svr_addr.count;
pj_sockaddr_in *addr;
svr_addr.entry[idx].type = query->srv[i].type;
svr_addr.entry[idx].priority = query->srv[i].priority;
svr_addr.entry[idx].weight = query->srv[i].weight;
svr_addr.entry[idx].addr_len = sizeof(pj_sockaddr_in);
addr = (pj_sockaddr_in*)&svr_addr.entry[idx].addr;
pj_bzero(addr, sizeof(pj_sockaddr_in));
addr->sin_family = PJ_AF_INET;
addr->sin_addr = query->srv[i].addr[j];
addr->sin_port = pj_htons((pj_uint16_t)query->srv[i].port);
++svr_addr.count;
}
}
PJ_LOG(5,(query->objname,
"Server resolution complete, %d server entry(s) found",
svr_addr.count));
if (svr_addr.count > 0)
status = PJ_SUCCESS;
else {
status = query->last_error;
if (status == PJ_SUCCESS)
status = PJLIB_UTIL_EDNSNOANSWERREC;
}
/* Call the callback */
(*query->cb)(status, query->token, &svr_addr);
}
return;
on_error:
/* Check for failure */
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
PJ_LOG(4,(query->objname,
"DNS %s record resolution error for '%.*s'."
" Err=%d (%s)",
pj_dns_get_type_name(query->dns_state),
(int)query->req.target.addr.host.slen,
query->req.target.addr.host.ptr,
status,
pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
(*query->cb)(status, query->token, NULL);
return;
}
}
#endif /* PJSIP_HAS_RESOLVER */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -