In this project, DNS lookup and reliability mechanism in RFC 3263 is implemented for CINEMA multimedia and IP telephony server.
Locating SIP Servers correctly and efficiently is very important for
SIP User Agents and proxies.
RFC 3263 [1], Session Initiation Protocol (SIP):
Locating SIP Servers,
explains the details of sip server
finding.
In this project, I improved the current SIP User Agents by adding
the capability of querying NAPTR records
for the domains. NAPTR records used for finding the preferred service
addresses. It is used to list the preferred
SRV records in SIP domain. For example, cs domain of columbia
university have these NAPTR records:
domain
order preference
cs.columbia.edu. 3600 IN NAPTR 60
40 "s" "SIP+D2U" ""
_sip._udp.cs.columbia.edu.
cs.columbia.edu. 3600 IN NAPTR 60
60 "s" "SIP+D2T" ""
_sip._tcp.cs.columbia.edu.
cs.columbia.edu. 3600 IN NAPTR 50
50 "s" "SIPS+D2T" ""
_sip._tcp.cs.columbia.edu.
According to [2], these records have to processed
in a order following these rules.
Order
A 16-bit unsigned integer specifying the
order in which the NAPTR records MUST be processed
to ensure the correct ordering of
rules. Low numbers are processed before high numbers, and once a
NAPTR is found whose rule "matches" the
target, the client MUST NOT consider any NAPTRs
with a higher value for order (except as
noted below for the Flags field).
Preference
A 16-bit unsigned integer that specifies
the order in which NAPTR records with equal "order"
values SHOULD be processed, low numbers
being processed before high numbers. This is similar to
the preference field in an MX record,
and is used so domain administrators can direct clients towards
more capable hosts or lighter weight
protocols. A client MAY look at records with higher preference
values if it has a good reason to do so
such as not understanding the preferred protocol or service.
The important difference between Order
and Preference is that once a match is found the client
MUST NOT consider records with a
different Order but they MAY process records with the
same Order but different Preferences.
I.e., Preference is used to give weight to rules that are
considered the same from an authority
standpoint but not from a simple load balancing standpoint.
According to above directions we can say that cs.columbia.edu domain
prefers secure sip
communication over TCP (SIPS+D2T). And then sip protocol over UDP and
at last sip over TCP.
This paper organized such that, first related
work is provided, then the architecture
is given and at last
measurements is provided.
Before the using of NAPTR records, SIP User Agents and proxies finds
their targets via SRV records.
The address of the SRV query is the concatenation of service, transport
protocol and domain. For example,
for sip service over TCP for cs.columbia.edu domain the query address
is _sip._tcp.cs.columbia.edu. These
"_" prefix is used not to conflict with a real server address. The
query of _sip._tcp.cs.columbia.edu returns the
following results.
_sip._tcp.cs.columbia.edu. 3412 IN
SRV 2 0 5060 conductor.cs.columbia.edu.
_sip._tcp.cs.columbia.edu. 3412 IN
SRV 0 0 5060 phone.cs.columbia.edu.
_sip._tcp.cs.columbia.edu. 3412 IN
SRV 1 0 5060 sip2.cs.columbia.edu.
This results suggests to the queerer that, first connect to the
phone:5060, then sip2:5060 and at last
conductor:5060. According to RFC 2782 [3], A client
MUST attempt to contact the target host with
the lowest-numbered priority it can reach; target hosts with the same
priority SHOULD be tried in an
order defined by the weight field.
SRV records permits the remote user to find a domain address and
port number for a specific service and
protocol such as SIP over TCP. However, it does not say anything about
what does this domain prefers, which
service over which protocol. NAPTR solves this problem by listing the
services and protocols in an ordered fashion.
In this project, I changed three files, rr.h, res_parse.c and
servers.c.
rr.h
First of all, The NAPTR query type and number is defined.
#ifndef T_NAPTR
#define T_NAPTR 35
#endif
Then NAPTR structure is defined according to RFC 2915 [2].
struct s_NAPTR
{
u_short order;
u_short preference;
char *flags;
char *service;
char *regexp;
char *replacement;
};
res_parse.c
This file parse the query result, NAPTR query parsing capability is
added.
case T_NAPTR: /* Service location */
rd->naptr.order = _getshort(*cpp);
*cpp += sizeof(u_short);
rd->naptr.preference = _getshort(*cpp);
*cpp += sizeof(u_short);
rd->naptr.flags = expand_charstring(cpp, msg);
rd->naptr.service = expand_charstring(cpp, msg);
rd->naptr.regexp = expand_charstring(cpp, msg);
rd->naptr.replacement = expand_cdname(cpp, msg);
break;
servers.c
This file finds the domain names given a service (SIP,SIPS) and a
protocol (TCP,UDP).
First of all, a method which sorts the query results according to the
RFC 2915 [2], is added.
/*
* Sort function: by order (lowest first) and then preference (lowest
* first). For equal preference, UDP wins.
*/
static int rr_sort(const s_rr **rr1, const s_rr **rr2)
{
if ((*rr1)->rdata.naptr.order > (*rr2)->rdata.naptr.order)
return 1;
if ((*rr1)->rdata.naptr.order < (*rr2)->rdata.naptr.order)
return -1;
if ((*rr1)->rdata.naptr.preference > (*rr2)->rdata.naptr.preference)
return 1;
if ((*rr1)->rdata.naptr.preference < (*rr2)->rdata.naptr.preference)
return -1;
return 0;
} /* rr_sort */
And then NAPTR querires added. The following code first queries the
domain with a NAPTR
record and then tries to match the preferred SRV addresses and clients
selections. For example,
if clients wants to use udp and sip, then it tries to find the
"SIP+D2U" record. If client does not
say anythning, default options, then this code selects the first
record. If first record is a sips record
and the client does not support TLS, then code goes to the second one.
naptrres = NULL;
/* New-style (RFC 2782) format */
sprintf(naptr_name, "%s", domain);
debug(DEBUG_MISC, "server_hosts", "Querying resolver for NAPTR %s\n", naptr_name);
if (res_search(naptr_name, C_IN, T_NAPTR, msg, sizeof (msg)) != -1) {
naptrres = res_parse((char *)&msg);
}
/* RFC 3263
* IF NO transport protocol or port is specified, and the target is not
* a numeric IP address, the client should perform a NAPTR query(Page6).
*/
if (naptrres != NULL&&port<0) {
naptr_rr = naptrres->answer;
qsort(naptr_rr,naptrres->header.ancount,4,(int (*)(const void*, const void*)) rr_sort);
sprintf(service_short_name,"NONE");
size_t cmp_len = 3;
debug(DEBUG_MISC,"server_hosts","type=%d port=%d\n",type,port);
if (((type == 0 && (abs(port) == DEFAULT_TLS_PORT)) || type == SOCK_TLS)
|| strcasecmp(service,"sips") == 0)
{
sprintf(service_short_name,"SIPS+D2T");
cmp_len=4;
}
else if (type == 0 && strcasecmp(service,"sip") == 0) {
sprintf(service_short_name, "SIP+D2U");
cmp_len = 3;
}
else if ((type == SOCK_DGRAM && strcasecmp(service,"sip") == 0 )) {
sprintf(service_short_name,"SIP+D2U");
cmp_len = 7;
}
else if ((type == SOCK_STREAM && strcasecmp(service,"sip") == 0 )){
sprintf(service_short_name,"SIP+D2T");
cmp_len = 7;
}
debug(DEBUG_MISC, "server_hosts", "service name is %s, com-len=%d,
service is %s\n", service_short_name, cmp_len, service);
int order = -1;
for (i = 0; i < naptrres->header.ancount; i++)
{
if ((naptr_rr[i]->type == T_NAPTR) && (naptr_rr[i]->class == C_IN)
&& (strcasecmp(naptr_name, naptr_rr[i]->name) == 0))
{
debug(DEBUG_MISC, "server_hosts", "Found service: %d. %s\n",
i,naptr_rr[i]->rdata.naptr.service );
if ( strncasecmp(service_short_name, naptr_rr[i]->rdata.naptr.service, cmp_len) != 0)
{
debug(DEBUG_MISC, "server_hosts", "Skipping %s record\n",
naptr_rr[i]->rdata.naptr.service);
continue;
}
else {
if (order>0 && naptr_rr[i]->rdata.naptr.order>order)
break;
order = naptr_rr[i]->rdata.naptr.order;
int sock_type = SOCK_DGRAM;
if (strcasecmp(naptr_rr[i]->rdata.naptr.service,"SIP+D2U") == 0)
sock_type = SOCK_DGRAM;
else if (strcasecmp(naptr_rr[i]->rdata.naptr.service,"SIPS+D2T") == 0)
sock_type = SOCK_TLS;
else if (strcasecmp(naptr_rr[i]->rdata.naptr.service,"SIP+D2T") == 0)
sock_type = SOCK_STREAM;
res = NULL;
sprintf(name, naptr_rr[i]->rdata.naptr.replacement);
debug(DEBUG_MISC, "server_hosts",
"NAPTR Record Found. Querying resolver for SRV for %s\n", name);
if (res_search(name, C_IN, T_SRV, msg, sizeof (msg)) != -1) {
res = res_parse((char *)&msg);
}
if (res != NULL) {
rr = res->answer;
int z;
debug(DEBUG_MISC, "server_hosts", "%d SRV for %s\n", res->header.ancount,name);
for (z = 0; z < res->header.ancount; z++) {
if ((rr[z]->type == T_SRV) && (rr[z]->class == C_IN)
&& (strcasecmp(name, rr[z]->name) == 0))
{
server = server_addr(server, rr[z]->rdata.srv.target,
sock_type, rr[z]->rdata.srv.port,
rr[z]->rdata.srv.priority - 65536,
rr[z]->rdata.srv.weight * drand48(),
entries, 0
);
}
}//for
res_free(res);
}//if (res != NULL)
}//else
}// if ((naptr_rr[i]->type == T_NAPTR) && (naptr_rr[i]->class == C_IN)
}//for (i = 0; i < naptrres->header.ancount; i++)
res_free(naptrres);
} // if (naptrres != NULL&&port<0)
We tested the new additions with different services (sip,sips) and protocols (tcp,udp):
invite transport=tcp sip:aaa@cs.columbia.edu => picks TCP
invite transport=udp sip:aaa@cs.columbia.edu => picks UDP
invite sip:aaa@cs.columbia.edu => picks UDP
invite sips:aaa@cs.columbia.edu => picks SIPS+D2T and then TCP.
invite sip:aaa@iptel.org => no NAPTR, but SRV. works.