DNS for Reliability

Omer Boyaci  
Columbia University
New York, NY 10027
USA
omer@omerboyaci.com

Abstract

In this project, DNS lookup and reliability mechanism in RFC 3263 is implemented for CINEMA multimedia and IP telephony server.

Introduction

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.

Related Work

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.

Architecture

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)

Measurements

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.

References

1
J. Rosenberg, H. Schulzrinne, RFC 3263 Session Initiation Protocol (SIP): Locating SIP Servers, IETF, June 2000
2
M. Mealling, R. Daniel, RFC 2915 The Naming Authority Pointer (NAPTR) DNS Resource Record, IETF, Sep 2000
3
A. Gulbrandsen et al., RFC 2782 A DNS RR for specifying the location of services (DNS SRV), IETF, Feb 2000