Skip to content

Fast and intuitive FFI API

While Defender provides a lot of new features, there are still cases where it makes sense to write Lua code to tailor DNSdist to specific needs. The Lua FFI provided by DNSdist is designed to offer the maximum possible performance, and therefore looks a lot like the C++ API that it is a thin wrapper over. This comes with very rough edges, and can be challenging to use. Defender wraps the FFI API to provide the ability to extend DNSdist without having the feeling to write C code.

Helpers

ComboAddress API

The ComboAddress API mimicks the ComboAddress class available in the regular, non-FFI interface.

newCAWithHints(version, address, port) -> FFIComboAddressWrapper

This method returns a FFIComboAddressWrapper object from a string.

Parameters:

  • version - integer: The IP version of the address, 4 for IPv4 and 6 for IPv6
  • address - string: The address, for example 192.0.2.1
  • port - integer: The port

FFIComboAddressWrapper class

The FFIComboAddressWrapper object mimicks the ComboAddress object available in the regular, non-FFI interface, and supports the following methods:

FFIComboAddressWrapper:getPort() -> integer

Returns the port as an integer.

FFIComboAddressWrapper:isIPv4() -> boolean

Returns true if the address is an IPv4 one, false otherwise.

FFIComboAddressWrapper:isIPv6() -> boolean

Returns true if the address is an IPv6 one, false otherwise.

FFIComboAddressWrapper:toString() -> string

Returns the address as a string, without the port.

FFIComboAddressWrapper:toStringWithPort() -> string

Returns the address as a string, including the port.

DNSName API

The DNSName API mimicks the DNSName class available in the regular, non-FFI interface.

newDNSName(raw) -> FFIDNSNameWrapper

This method returns a FFIDNSNameWrapper object from a Lua string containing a DNS name in wire format:

local dnsdistFFI = require 'dnsdist/ffi'
local name = dnsdistFFI.newDNSName('\007dnsdist\003org\000')
print(name:toString())

FFIDNSNameWrapper class

The FFIDNSNameWrapper object mimicks a DNSName object available in the regular, non-FFI interface, and supports the following methods:

FFIDNSNameWrapper:toString() -> string

Returns the DNS name in human-readable form as a string, including the trailing dot.

FFIDNSNameWrapper:wirelength() -> integer

Returns the length in bytes of the DNSName as it would be on the wire.

DNSQuestion API

The DNSQuestion API wraps the dnsdist_ffi_dnsquestion_t object to provide a compatility layer with the DNSQuestion object from the regular, non-FFI interface, making it easier to write code using the FFI interface and to reuse existing code.

DNSQuestion

newDNSQuestion(dnsdist_ffi_dnsquestion_t) -> FFIDNSQuestionWrapper

This method returns a FFIDNSNameWrapper object from a Lua string containing a DNS name in wire format:

local dnsdistFFI = require 'dnsdist/ffi'
local dq = dnsdistFFI.newDNSQuestion(dqffi)
print(dq.remoteaddr:toStringWithPort())

FFIDNSQuestionWrapper class

The FFIDNSQuestionWrapper object provides a compatility layer with the DNSQuestion object from the regular, non-FFI interface. It provides the following methods and fields:

FFIDNSQuestionWrapperFunctions:getProtocol() -> string

Return the protocol this query was received over, in human-readable format ("Do53 UDP", "Do53 TCP", "DNSCrypt UDP", "DNSCrypt TCP", "DNS over TLS", "DNS over HTTPS").

FFIDNSQuestionWrapperFunctions.len

The size, in bytes, of the received query.

FFIDNSQuestionWrapperFunctions.localaddr

The FFIComboAddressWrapper of the local bind this question was received on.

FFIDNSQuestionWrapperFunctions.opcode

The opcode, as an integer, of the received query.

FFIDNSQuestionWrapperFunctions.qclass

The requested class, as an integer, of the received query.

FFIDNSQuestionWrapperFunctions.qname

The requested name, as a FFIDNSNameWrapper, of the received query.

FFIDNSQuestionWrapperFunctions.qtype

The requested type, as an integer, of the received query.

FFIDNSQuestionWrapperFunctions.rcode

The rcode, as an integer, of the received query.

FFIDNSQuestionWrapperFunctions.remoteaddr

The FFIComboAddressWrapper of the remote client.

FFIDNSQuestionWrapperFunctions.size

The total size, in bytes, of the buffer holding the query payload.

FFIDNSQuestionWrapperFunctions.tcp

Whether the query was received over UDP (false) or TCP (true). See FFIDNSQuestionWrapperFunctions:getProtocol() for the exact protocol.

StatNode API

The StatNode API is useful when extending the existing capabilities of the pseudo random subdomain detection and mitigation features by passinng a custom Lua function to DNSdist's DynBlockRulesGroup:setSuffixMatchRuleFFI(). The custom Lua function is then invoked for every node of the DNS tree built by the algorithm described in Pseudo Random Subdomain attack protection, getting a dnsdist_ffi_stat_node object containing information about the node and the traffic it received during the last time window.

FFIStatNodeWrapper

newStatNode(statNodeFFI) -> FFIStatNodeWrapper

This method returns a FFIStatNodeWrapper object from a dnsdist_ffi_stat_node one: :::

local dnsdistFFI = require 'dnsdist/ffi'
local node = dnsdistFFI.newStatNode(statNodeFFI)
if node:getChildrenCount() < minChildren then
  return false
end
local qps = node:getChildrenQueriesCount() / window
  if qps < minQPS then
    return false
  end
end

FFIStatNodeWrapper class

The FFIStatNodeWrapper object wraps a dnsdist_ffi_stat_node FFI object and supports the following methods:

FFIStatNodeWrapper:getBytes() -> integer

Returns the amount of bytes for all responses received by this node.

FFIStatNodeWrapper:getChildrenBytes() -> integer

Returns the amount of bytes for all responses received by the children of this node.

FFIStatNodeWrapper:getChildrenCount() -> integer (#FFIStatNodeWrapper.getChildrenCount)

Returns the number of children of this node.

FFIStatNodeWrapper:getChildrenHitsCount() -> integer

Returns the amount of cache hits for the children of this node.

FFIStatNodeWrapper:getChildrenQueriesCount() -> integer

Returns the number of queries the children of this node have received.

FFIStatNodeWrapper:getChildrenNOERRORCount() -> integer

Returns the number of NoError responses the children of this node have received.

FFIStatNodeWrapper:getChildrenNXDOMAINCount() -> integer

Returns the number of NXDOMAIN responses the children of this node have received.

FFIStatNodeWrapper:getChildrenSERVFAILCount() -> integer

Returns the number of SERVFAIL responses the children of this node have received.

FFIStatNodeWrapper:getFullName() -> integer

Returns a Lua string containing the DNS name of the current node.

FFIStatNodeWrapper:getHitsCount() -> integer

Returns the amount of cache hits for this node.

FFIStatNodeWrapper:getDropsCount() -> integer

Returns the number of queries for this node that did not get an answer from the backend in time.

FFIStatNodeWrapper:getLabelsCount() -> integer

Returns the number of labels in the DNS name of the current node.

FFIStatNodeWrapper:getNOERRORCount() -> integer

Returns the number of NoError responses this node has received.

FFIStatNodeWrapper:getNXDOMAINCount() -> integer

Returns the number of NXDOMAIN responses this node has received.

FFIStatNodeWrapper:getQueriesCount() -> integer

Returns the number of queries this node has received.

FFIStatNodeWrapper:getQueriesPerChildRatio() -> integer

Returns the average number of queries per child received by the children of this node.

FFIStatNodeWrapper:getSERVFAILCount() -> integer

Returns the number of SERVFAIL responses this node has received.

Metrics

The FFI interface provides a faster way to interact with metrics.

declareMetric(name, type, description[, prometheusName]) -> boolean

Identical to DNSdist's declareMetric.

incMetric(name[, value])

Increment the value of an existing metric by:

  • 1 if value is unset
  • value otherwise

decMetric(name)

Decrement the value of an existing metric by 1.

getMetric(name, isCounter) -> float

Return the value of an existing metric. isCounter should be set to true if the metric is a counter, and false if it is a gauge.

setMetric(name, value)

Set an existing metric to the specified value.