Skip to content
Scott Godin edited this page Jun 27, 2023 · 2 revisions

Table of Contents

Introduction

Why Asynchronous DNS

Development

ares_gethostbyname() and ares_gethostbyaddr() (but not ares_query()!) ares functions will look in the following locations for the hosts file if it fails to find the entry via a DNS lookup

  • On *nix systems - /etc/hosts
  • On *nix systems when ETC_INET is defined - /etc/inet/hosts
  • On Win32 systems - location of hosts file is retrieved from registry - typically: c:\Windows\System32\drivers\etc\hosts
  • On OS X systems - the NetInfoManager, in the machines directory
If you put the following line into /etc/resolv.conf:
   lookup f b

you are telling the functions to look in the hosts (f)ile before DNS(b) servers. (b is for bind).

For windows environments the resolv.conf file will be retrieved from the drive of the working directory of the resip executable: ie. d:\etc\resolv.conf

Note that reSIProcate in its current implementation uses only ares_query(). So, /etc/hosts is ignored.

ENUM Support

The rutil library has a basic level of support for ENUM queries, as do the DNS related classes under resip/stack. Currently the stack will atempt ENUM DNS resolution if an enum suffix is configured, and if the user part of the URI to resolve is at least 2 characters and starts with a '+' character. This is true for both sip(s): and tel: URI's.

Current Limitations:

  1. The stack allows setting of multiple ENUM suffixes, but only ever uses the first one in the list.
  2. If an ENUM query returns a regular expression, then it will not be processed properly. Only straight substitutions are currently supported.

DNS Cache Overview

reSIProcate DNS layer has a built-in cache for individual DNS resource records. On top of the cache resip supports three different markers to help determine which DNS entries to use: whitelisted, greylisted and blacklisted.

  • whitelisted => stack will always use that tuple, regardless of the availability of others
  • greylisted => stack will only send to that tuple unless no other tuples remain (if all tuples are greylisted, they're all fair game)
  • blacklisted => stack will refuse to send to that tuple
Generally, resip greylists on transport failure or transaction timeout, blacklists on a 503 with a Retry-After, and if useDnsVip is enabled when creating the SipStack, we whitelist whenever a transaction gets a response (this can help a client to re-use the same SBC/proxy for every request they send - some people feel this is a bad approach, but it is disabled at compile time by default). This means that in cases where a server is unreachable, resip will not full-on refuse to try to contact it again, since it is only greylisted. If the server _tells_ us to not contact it again for 10 seconds, we will honor that request, because that results in a blacklist.

DNS Record Failover

resip will try multiple DNS results if there are failures detected with the highest priority result before the SIP transaction times out. Such failures include:

  • transport selector failed to find a transport / route to the destination
  • TCP/TLS connection failed
  • transport write(send) failed
Failover will occur first within host (A/AAAA) records, if multiple records were returned from the DNS server. Once that is exhausted, if multiple SRV records were retrieved, then we will failover to the next highest priority SRV record. If NAPTR records are deployed, then will failover to other NAPTR records only if the NAPTR ordering is the same amongst the various records.
 Note:  For a standard 408 timeout error that occurs after 32-seconds (typically over UDP), it 
        doesn't make sense for the stack to try another DNS entry, since the 32 second transaction 
        time has already expired.  Stack users expect some form of response within 32-seconds after 
        issuing a request.  For requests that 408 after 32 seconds, the application will need to be 
        responsible for re-issuing the request if it is desired.
 Note:  When using the resiprocate stack on Windows and using the WSAPoll processing engine (default 
        mode for repro), failover on TCP connection failures will not occur due to a bug in the WSAPoll 
        implementation.  The bug causes TCP connection failures to only be noticed if another message is 
        attempted on the transport before the 32 second transaction timeout.  Thus TCP connection failures 
        can end up taking the entire 32 seconds transaction time up, thus making SRV failover for this 
        transaction not possible.  See the checkConnectionTimedout call in 
        TcpBaseTransport::processAllWriteRequests for more information.

Notes on NAPTR/transport Failover

Resiprocate allows fallback to another NAPTR record, when multiple NAPTR records have equivalent NAPTR order. Ordering of these results are then done using the NAPTR preference value. This behavoir is not explicitly called out in RFC3263, however it is not explicitly forbidden either. RFC 2915 (NAPTR DNS records) has the following statements on the order and preference fields in a NAPTR record that support this behavior:

  • the client MUST NOT consider any NAPTRs with a higher value for order
  • a client MAY look at records with higher preference values if it has a good reason to do so
 Note: failover from UDP to another protocol is unlikely to happen in many scenarios, due to the 
       fact that a UDP failure is often not detected until the SIP transaction has already timed 
       out, and a 408 response is issued.

Example 1:

 ;         order pref flags service    regexp replacement 
  IN NAPTR 50    50   "s"   "SIPS+D2T" ""     _sips._tcp.example.com. 
  IN NAPTR 90    50   "s"   "SIP+D2T"  ""     _sip._tcp.example.com 
  IN NAPTR 100   50   "s"   "SIP+D2U"  ""     _sip._udp.example.com.

Will result in a single SRV lookup of _sips._tcp.example.com, assuming the client supports TLS.

Example 2 (Note all NAPTR records have equal ordering):

 ;         order pref flags service    regexp replacement 
  IN NAPTR 50    50   "s"   "SIPS+D2T" ""     _sips._tcp.example.com. 
  IN NAPTR 50    60   "s"   "SIP+D2T"  ""     _sip._tcp.example.com 
  IN NAPTR 50    70   "s"   "SIP+D2U"  ""     _sip._udp.example.com.

Will result in three SRV lookups: _sips._tcp.example.com, _sip._tcp.example.com and _sip._udp.example.com with the SRV results sorted in the listed order (NAPTR pref), then sorted by SRV ordering, assuming the client supports TLS, TCP and UDP.

DNS Resolution Lookup Logic

  • if URI has a plain IP address specified, then do NOT use DNS resolution, check if address is blacklisted, and return immediately
  • if URI has transport parameter specified, then skip NAPTR query
  • if URI has port specified, then skip NAPTR and SRV query
  • if URI is a SIPS URI then only process "SIPS" NAPTR records and _sips._udp or _sips._tcp SRV records
  • if NAPTR results are returned, then issue an SRV query for each record NAPTR record for which we support the transport type, and who's order matches the highest ordered and supported record
  • if no NAPTR results are returned, then issue and SRV query for every transport type that we support
  • if SRV results are returned, then order the results according to spec, and issue an A or AAAA record query for the highest priority record
  • if A/AAAA record query is successful, then any blacklisted results are removed from the set, greylisted results are stored in a seperate list and are only tried if all other non-blacklisted results have failed
  • once a A/AAAA, SRV, NAPTR or CNAME record query is issued to the lower DNS layer, the lower DNS layer first checks in it's cache so see if it needs to go the wire for the answer

DNS Lookup Marking Details

Blacklist

  • blacklists are tracked internally by the TupleMarkManager class
  • indexed by tuple: ie. transport type, family, ipaddr, port, and lookup domain name
  • tuples are blacklisted when receiving a 503 response with a Retry-After header
  • tuple is blacklisted for the amount of time specified in the 503 response Retry-After header
  • host record is removed from any whitelist when tuple is blacklisted

Greylist

  • greylists are tracked internally by the TupleMarkManager class
  • indexed by tuple: ie. transport type, family, ipaddr, port, and lookup domain name
  • greylist occurs when we have a transport failure or when we receive an internally generated 408 (not from the wire)
  • greylist duration is currently hardcoded to 32 seconds
  • host record is removed from any whitelist when tuple is greylisted

Whitelist

  • disabled by default - pass useDnsVip as true when creating the SipStack to enable
  • tracked by RRVip class
  • RRVip stores one entry in the VIP table for each DNS query in the path (ie. NAPTR->SRV->A/AAAA)
  • DNS path is whitelisted when we receive any response that doesn't result in a grey list or a black list
  • if a particular DNS lookup is in the whitelist (NAPTR, SRV or A/AAAA), then it will be given priority over all other records of the same type for the lookup value, the next time a DNS query is tried. This is accomplished by transforming the results from the DNS server or cache such that the priority or order of the other hihger priority records are artificially reduced.

Other Dns APIs/Settings

Cache Settings

  • mStack->getDnsStub().setDnsCacheTTL(X) - specifies a minimum TTL in minutes (?not sure why minutes?) - default is 10 seconds. If server provided value for TTL is lower, we use this value instead. Note: this API should be called before the stack process loop is started and not after the stack is up and running.
  • mStack->getDnsStub().setDnsCacheSize(X) - specifies the maximum number of entries we will store in the cache. The default is 512 entries. Note: this API should be called before the stack process loop is started and not after the stack is up and running.

Other Cache API's

  • mStack->logDnsCache() - output the current cache settings to the logging subsystem as a WarningLog. Note: it is safe to call this while the stack is processing.
  • mStack->clearDnsCache() - clear the DNS cache. Has no effect on the blacklist, greylist or whitelist. Note: it is safe to call this while the stack is processing.
Clone this wiki locally