DNS Lua Service

Introduction

The DnsLuaService is a service for initiating Lua scripts running within the LogicApp.

The DnsLuaService receives messages from one or more instances of the DnsServerApp which is configured to receive DNS (typically ENUM) Requests from an external client.

The DnsLuaService communicates with the DnsServerApp using the DNS-S-… messages.

Configuring DnsLuaService

The DnsLuaService is configured within a LogicApp.

    <?xml version="1.0" encoding="utf-8"?>
    <n2svcd>
      ...
      <applications>
        ...
        <application name="Logic" module="LogicApp">
          <include>
            <lib>../apps/logic/lib</lib>
          </include>
          <parameters>
            ...
          </parameters>
          <config>
            <services>
              <service module="DnsServerApp::DnsLuaService" libs="../apps/dns_s/lib" script_dir="/var/lib/n2svcd/logic/dns"/>
            </services>
            <agents>
              ...
            </agents>
          </config>
        </application>
        ...
      </application>
      ...
    </n2svcd>

In addition to the Common LogicApp Service Configuration, note the following specific attribute notes, and service-specific attributes.

Under normal installation, the following service attributes apply:

Parameter Name Type XML Type Description
module String Attribute [Required] The module name containing the Lua Service code: DnsServerApp::DnsLuaService
libs String Attribute Location of the module for DnsLuaService.
(Default: ../apps/dns_s/lib)
script_dir String Attribute [Required] The directory containing scripts used by this service.

Script Selection (DNS Request)

Script selection is not configured. The script key to be executed is determined by the numeric opcode value of the inbound DNS request represented in the DNS-S-REQUEST message.

Specifically:

E.g. standard query requests with opcode = 0 will be satisfied by a file named query.lua or query.lc in the script_dir directory configured for the service.

Refer to the LogicApp configuration for more information on directories, library paths, and script caching parameters.

Script Global Variables

Scripts run with this service have access to the Common LUA Service Global Variables.

There are no service-specific global variables.

Script Entry Parameters (DNS Request)

The Lua script must be a Lua chunk such as the following example for an E.164 lookup which always returns the indicated regex for the SIP+E2U service:

local n2svcd = require "n2.n2svcd"

local dns = ...

n2svcd.debug ("Echo Supplied DNS Inputs:")
n2svcd.debug_var (dns)

return { 
    reply_code = 0, 
    answers = {
        { class = 1, type = 35, name = dns.queries[1].name, service = 'SIP+E2U', regex = '!^.*$!sip:16133957218@example.com!' }
    }
}

The chunk will be executed with a single dns entry parameter which is an object with the following attributes:

Attribute Type Description
.remote_ip String The remote dot-notation IP address from which the DNS request was sent.
.remote_port Integer The remote UDP or TCP port from which the DNS request was sent.
.transaction_id 0 - 65535 The two-byte transaction ID as an integer value.
.flags Integer An integer representation of the top-level DNS message two-byte flags field.
.reply_code Integer An integer representation of the 4-bit reply code value from the flags field.
This should always be zero for inbound requests.
.opcode Integer An integer representation of the 4-bit opcode value from the flags field.
.recursion_desired 0 / 1 An integer representation of the 1-bit RD value from the flags field.
.checking_disabled 0 / 1 An integer representation of the 1-bit CD value from the flags field.
.num_questions Integer The number of entries contained in the questions section of the DNS request.
For a standard query with opcode = 0, at least one question should be present.
.num_answers Integer The number of entries contained in the answers section of the DNS request.
For an inverse query with opcode = 1, at least one answer should be present.
.num_authorities Integer The number of entries contained in the authorities section of the DNS request.
This should be zero in any normal scenario for both standard query or inverse query.
.num_additional Integer The number of entries contained in the additional section of the DNS request.
This should be zero in any normal scenario for both standard query or inverse query.
.queries Array of Object The list of Query records parsed from the DNS Request (see below for object structure).
.authorities Array of Object The list of Authority response records parsed from the DNS Request (see below for object structure).
.additional Array of Object The list of Additional response records parsed from the DNS Request (see below for object structure).
.answers Array of Object The list of Answer response records parsed from the DNS Request (see below for object structure).

The queries Object in the dns entry parameter has the following structure.

Field Type Description
.name String [Required] The name of the record as a dot-notation string.
For E.164 NAPTR records use the .e164_digits helper method to convert this to a simple digit string.
.type Integer [Required] The integer value of the Type, e.g. 35 indicating NAPTR.
.class Integer [Required] The integer value of the Class, e.g. 1 indicating IN.

The answers, authorities, and additional Objects in the dns entry parameter have the following structure.

Note that in normal processing, only the answers object will be present, and that only when the top-level opcode = 1 which indicates an inverse query.

Note that the sub-fields name, type, class, ttl are common to all of these three record types (answers, authorities, and additional) regardless of type/class.

All other sub-fields are specific to individual record type values. At this time the only supported response record types for which extra data fields will be decoded are the following:

Field Type Description
.name String [Required] The name of the record as a dot-notation string.
.type Integer [Required] The integer value of the Type, e.g. 35 indicating NAPTR.
.class Integer [Required] The integer value of the Class, e.g. 1 indicating IN.
.ttl Integer [Required] The TTL (time-to-live) in seconds for this record.
.order Integer [Required for NAPTR (Type = 35)] The Order value (0-65535) supplied in the NAPTR response record.
.preference Integer [Required for NAPTR (Type = 35)] The Preference value (0-65535) supplied in the NAPTR response record.
.flags Integer [Required for NAPTR (Type = 35)] An integer representation of the flags supplied in the NAPTR response record.
.service String [Required for NAPTR (Type = 35)] The Service string supplied in the NAPTR response record.
.regex String [Required for NAPTR (Type = 35)] The Regex string (if any) supplied in the NAPTR response record.
.replacement String [Required for NAPTR (Type = 35)] The Replacement domain name (if any) supplied in the NAPTR response record, expressed as a dot-separated domain name.

Script Return Parameters (DNS Response)

The Lua script is responsible for determing the DNS Response which will be sent back in reply to the original DNS Request.

The simplest way to do this is by the return value given back to the service at the end of script execution.

When returning an DNS response with answers, the script return value must be a response object with the following attributes:

Field Type Description
response Object Container for the DNS response parameters we are to send.
.authoritative_answer 0 / 1 Should the response set the 1-bit AA value in the top-level flags.
(Default = 0)
.recursion_available 0 / 1 Should the response set the 1-bit RA value in the top-level flags.
(Default = 0)
.authenticated_data 0 / 1 Should the response set the 1-bit AD value in the top-level flags.
(Default = 0)
.reply_code 0 - 15 [Required] The top-level 4-bit Reply Code to set in the top-level flags.
A value of 0 indicates success and should always be used when returning any answer entries.
.queries Array of Object The list of Query records to return as DNS Response (see below for object structure).
The default behavior (when reply_code = 0 for success) is to copy the inbound DNS query records back into the response. If this is not desirable then you may explicitly specify an array of queries records in the response.
If you explicitly require no queries in the response then set the queries attribute to be the UNDEF constant. (Default: Copy the inbound DNS Request query records back out into the DNS Response)
.authorities Array of Object The list of Authority response records to return as DNS Response (see below for object structure).
.additional Array of Object The list of Additional response records to return as DNS Response (see below for object structure).
.answers Array of Object The list of Answer response records to return as DNS Response (see below for object structure).

Note that many of the return flags and values in the DNS Response are set automatically and cannot be controlled by the service logic.

Specifically:

The queries Object in the returned DNS response argument has the following structure.

Note that typically it is not required to set this value, instead the inbound request queries array will be copied back into the response. This copy-back is performed by the DnsServerApp when it constructs the on-the-wire response message, and is performed only when the reply code is 0 indicating success.

Field Type Description
.name String [Required] The name of the record as a dot-notation string.
.type Integer [Required] The integer value of the Type, e.g. 35 indicating NAPTR.
.class Integer [Required] The integer value of the Class, e.g. 1 indicating IN.

The answers, authorities, and additional Objects in the returned DNS response argument have the following structure.

Note that the sub-fields name, type, class, ttl are common to all of these three record types (answers, authorities, and additional) regardless of type/class.

All other sub-fields are specific to individual record type values. At this time the only supported response record types for which extra data fields will be encoded are the following:

Field Type Description
.name String [Required] The name of the record as a dot-notation string.
For an answers record this might be copied from the received queries list.
.type Integer [Required] The integer value of the Type, e.g. 35 indicating NAPTR.
.class Integer [Required] The integer value of the Class, e.g. 1 indicating IN.
.ttl Integer The TTL (time-to-live) in seconds for this record. You should generally set this value explicitly.
(Default = 300)
.order Integer [Applies for NAPTR (Type = 35)] The Order value (0-65535) supplied in the NAPTR response record.
(Default: The first record of each list will be given order 10, the next 20, etc.)
.preference Integer [Applies for NAPTR (Type = 35)] The Preference value (0-65535) supplied in the NAPTR response record.
(Default = 1)
.flags Integer [Applies for NAPTR (Type = 35)] An integer representation of the flags supplied in the NAPTR response record.
(Default = 0)
.service String [Required for NAPTR (Type = 35)] The Service string supplied in the NAPTR response record.
.regex String [Required for NAPTR (Type = 35)] The Regex string (if any) supplied in the NAPTR response record.
.replacement String [Applies for NAPTR (Type = 35)] The Replacement domain name (if any) supplied in the NAPTR response record, expressed as a dot-separated domain name.
(Default = '')

Example (returning a Table DNS Response):

local n2svcd = require "n2.n2svcd"

local dns = ...

return ({ 
  reply_code = 0, 
  answers = {
    { class = 1, type = 35, name = dns.queries[1].name, service = 'SIP+E2U', regex = '!^.*$!sip:16133957218@example.com!' }
  }
})

Alternatively, a script may return a simple Lua number value as the result, which indicates an reply_code with no additional returned records.

Typically the reply code should be non-zero in this case.

Example (returning explicit error):

local n2svcd = require "n2.n2svcd"

local dns = ...

-- Reply Code 4 = NOTIMP Function not implemented
return 4

i.e. return 4 is shorthand for return { reply_code = 4 }.

Alternatively, a script may return nil.

This will cause the return of an DNS success response with reply code = 0 but with an empty answers section.

Example (success returning no answers):

local n2svcd = require "n2.n2svcd"

local dns = ...

return nil

i.e. return nil is shorthand for return { reply_code = 0 }.

The DnsLuaService API

The DNS Service API can be loaded as follows.

local dns_service = require "n2.n2svcd.dns_service"

It is not necessary to load the DNS Service API if you are only using the simple response mechanism described above. It is only required if you wish to use any of the extended features described below.

.response

When a Lua Script needs to perform extended processing, it may wish to send an early response before the script completes. This can be done with the response method on the DNS API.

The response method takes a single response parameter. The structure of this response parameter must be a table, with structure as per the response return value described above in Script Return Parameters (DNS Response)

The shorthand return values for nil and for Lua number values are not available when using this method.

The response method returns true.

[Fragment] Example (Full table early response):

    ...
    dns_service.response ({ 
        reply_code = 0,
        answers = {
          { class = 1, type = 35, name = dns.queries[1].name, service = 'SIP+E2U', regex = '!^.*$!sip:16133957218@example.com!' }
        }
    })
    ...
    [post-processing after DNS transaction is concluded]
    ...

.e164_digits [Pure Lua]

The e164_digits method is a helper method to facilitate the processing of received ENUM NAPTR lookup requests.

This method accepts the following parameters:

Field Type Description
dns String [Required] The received DNS request object as passed in the first parameter to the service script.
suffix Table An optional override for the E.164 suffix string which must be matched before the name is considered an E.164 digit string.
(Default = 'e164.org').

This method returns a string which is the E.164 digits (without leading +) in the case where the first query record is an NAPTR record which contains a name field with the expected E.164 suffix. In all other cases it will return nil.

[Fragment] Example:

local n2svcd = require "n2.n2svcd"

local dns = ...

local e164_digits = dns_agent.e164_digits (dns)

if (e164_digits ~= nil) then
    if (e164_digits == '642711209400') then

    ...

Constants

The following DNS and DNS constants are defined on the returned dns_service object.

See:

-- DNS Type Constants.
dns_service.TYPE_NS = 2
dns_service.TYPE_NAPTR = 35

-- DNS Class Constants.
dns_service.CLASS_IN = 1

-- DNS OPCODE Constants.
dns_service.OPCODE_QUERY = 0
dns_service.OPCODE_IQUERY = 1
dns_service.OPCODE_STATUS = 2

-- DNS REPLY CODE Constants.
dns_service.RCODE_NOERROR = 0     -- DNS Query completed successfully
dns_service.RCODE_FORMERR = 1     -- DNS Query Format Error
dns_service.RCODE_SERVFAIL = 2    -- Server failed to complete the DNS request
dns_service.RCODE_NXDOMAIN = 3    -- Domain name does not exist
dns_service.RCODE_NOTIMP = 4      -- Function not implemented
dns_service.RCODE_REFUSED = 5     -- The server refused to answer for the query
dns_service.RCODE_YXDOMAIN = 6    -- Name that should not exist, does exist
dns_service.RCODE_XRRSET = 7      -- RRset that should not exist, does exist
dns_service.RCODE_NOTAUTH = 8     -- Server not authoritative for the zone
dns_service.RCODE_NOTZONE = 9     -- Name not in zone