The PJSIP library now used by Asterisk to provide SIP support has included basic SIP DNS support for quite some time. However through using it ourselves and from feedback from the community we determined that it was not as feature rich as we would like and as part of Asterisk 14 we set about improving it.
We looked at various options such as extending the support in PJSIP itself, writing our own, and using third party libraries. After much discussion we decided to make a pluggable solution like much of Asterisk. This pluggable solution is called Core DNS. Core DNS is a common API for use throughout Asterisk that provides rich DNS support and helper constructs on top of it to make using DNS properly easier.
An API is nothing without an implementation though. Asterisk 14 will ship with two resolvers: unbound and system. The unbound resolver provides a more complete DNS experience including DNSSEC support but requires an external library, libunbound. The system resolver uses system primitives available on every system and provides the full Core DNS API but does not have extra features such as DNSSEC. Either of these can be used for Core DNS to function. If you do not have the libunbound library installed then the system resolver will be used automatically.
From a PJSIP perspective we needed to extend it to allow external DNS resolution to be used instead of the built-in DNS support in PJSIP. This was a change contributed upstream to the maintainers of PJSIP, Teluu, that allows an external callback to be registered which is expected to perform the required DNS lookup and provide a result. This is available in recent releases and as a result you must be running a recent PJSIP in order to have it available.
Putting all of these together we wrote a custom DNS resolver for PJSIP that uses the Asterisk Core DNS API for performing DNS resolution. This acts as a bridge between the two and provides complete DNS support for PJSIP. What’s that gain you, though? Well…
Better support for the following DNS records!
A NAPTR record on a domain provides a mapping of services to servers that can handle them for the domain. It also adds in an order and preference so you can control what server (and protocol) a client uses. A provider may configure their records such that they prefer TLS, followed by TCP, followed by UDP.
chan_pjsip will now look for NAPTR records and use them according to what transports are configured on the system and the order and preference.
An SRV record provides information about where a service can be reached (including port) and also includes its own priority and weight for managing where clients connect to. In deployments a NAPTR record will usually exist that then points to SRV records and ultimately the AAAA and A records.
chan_pjsip will now look for SRV records based on what transports are configured on the system.
An AAAA record is for an IPv6 address.
chan_pjsip will now look for AAAA records if IPv6 is configured on a transport.
An A record is for an IPv4 address.
chan_pjsip has always looked for A records and continues to do so.
The actual resolution process involves looking for these different records. To speed up call setup time these queries are performed in parallel.
On the first resolution iteration NAPTR, SRV, AAAA, and A records are queried depending on the available transports. If a transport is not available (such as TCP) then that query is not done. If IPv6 support is not available then that query is also not done. If NAPTR or SRV records are received then other records are discarded and a second iteration done. If no NAPTR or SRV records are available then the AAAA and A records are used as received.
Optional Second Iteration
On the second resolution iteration if NAPTR records were present in the first iteration then SRV records or AAAA and A records are queried depending on the NAPTR record itself. If only SRV records were present in the first iteration then AAAA and A records are queried next.
Optional Third Iteration
On the third resolution iteration if SRV records were present in the second iteration result then AAAA and A records are queried.
When a SIP request is sent that has no already resolved information the above iterative resolution process is done to ultimately get IP addresses and ports. This target information is placed on the SIP request (up to a fixed size) and the request is sent. If the request is not successfully sent (such as if it does not receive a response) then the next target is attempted until they are all exhausted. For environments where the DNS process results in multiple targets this allows failover to occur.
Resolution respects the SRV record priority and weighting allowing load balancing to be done. Given two SRV records where both are the same priority and the first has a weight of 1 and the second has a weight of 2 the second record will receive roughly 2/3 of the traffic. When populating the target information this is taken into account.
How do I use it?
You need to ensure you have a recent version of PJSIP and then you really just have to continue using PJSIP as you have done before. If you specify a SIP URI with a hostname then NAPTR and SRV lookups will be done automatically. If you want to restrict what transport is used you can specify it using “;transport=” such as “;transport=tcp” in the URI otherwise it will be based on what is available on the system. The rest (including failover) just happens!