]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add documentation for suspending/resuming objects
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 10 Jan 2023 13:53:29 +0000 (14:53 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 13 Jan 2023 15:57:52 +0000 (16:57 +0100)
pdns/dnsdistdist/docs/advanced/asynchronous-processing.rst [new file with mode: 0644]
pdns/dnsdistdist/docs/advanced/index.rst
pdns/dnsdistdist/docs/imgs/AsyncQuery.png [new file with mode: 0644]
pdns/dnsdistdist/docs/reference/dnsparser.rst
pdns/dnsdistdist/docs/reference/dq.rst

diff --git a/pdns/dnsdistdist/docs/advanced/asynchronous-processing.rst b/pdns/dnsdistdist/docs/advanced/asynchronous-processing.rst
new file mode 100644 (file)
index 0000000..404e017
--- /dev/null
@@ -0,0 +1,52 @@
+Asynchronous processing
+=======================
+
+Since 1.8.0, dnsdist has the ability to process queries and responses in an asynchronous way, suspending them to continue processing other queries and responses, while we are waiting for an external event to occur.
+
+This is done by calling the :meth:`DNSQuestion:suspend` method on a query or a response to pause it, then later the :func:`getAsynchronousObject` to retrieve it before resuming via :meth:`AsynchronousObject:resume`.
+
+A timeout must be supplied when pausing a query or a response, to prevent paused objects from piling up, consuming memory. When the timeout expires, the suspended object is automatically retrieved and resumes its processing where it was left.
+
+.. figure:: ../imgs/AsyncQuery.png
+   :align: center
+   :alt: Asynchronous processing of queries and responses
+
+The following code shows a very simple example that forwards queries and responses to an external component over a unix network socket, and resumes them when it gets an answer from the external component.
+
+.. code-block:: lua
+
+    local asyncID = 0
+    local asyncResponderEndpoint = newNetworkEndpoint('/path/to/unix/network/socket/remote/endpoint')
+    local listener = newNetworkListener()
+    listener:addUnixListeningEndpoint('/path/to/unix/network/socket/local/endpoint', 0, gotAsyncResponse)
+    listener:start()
+
+    function gotAsyncResponse(endpointID, message, from)
+      local queryID = tonumber(message)
+      local asyncObject = getAsynchronousObject(asyncID, queryID)
+      local dq = asyncObject:getDQ()
+      dq:setTag(filteringTagName, filteringTagValue)
+      asyncObject:resume()
+    end
+
+    function passQueryToAsyncFilter(dq)
+      local timeout = 500 -- 500 ms
+      local buffer = dq:getContent()
+      local id = dq.dh:getID()
+      dq:suspend(asyncID, id, timeout)
+      asyncResponderEndpoint:send(buffer)
+      return DNSAction.Allow
+    end
+
+  function passResponseToAsyncFilter(dr)
+      local timeout = 500 -- 500 ms
+      local buffer = dr:getContent()
+      local id = dr.dh:getID()
+      dr:suspend(asyncID, id, timeout)
+      asyncResponderEndpoint:send(buffer)
+      return DNSResponseAction.Allow
+    end
+
+    addAction(AllRule(), LuaAction(passQueryToAsyncFilter))
+    addCacheHitResponseAction(AllRule(), LuaResponseAction(passResponseToAsyncFilter))
+    addResponseAction(AllRule(), LuaResponseAction(passResponseToAsyncFilter))
index a028a9292f73d20722c05a35da5d009815841462..e390db09eb7b3d5ff0591e857e94377bdc655c3b 100644 (file)
@@ -21,3 +21,4 @@ These chapters contain information on the advanced features of dnsdist
    ocsp-stapling
    tls-sessions-management
    internal-design
+   asynchronous-processing
diff --git a/pdns/dnsdistdist/docs/imgs/AsyncQuery.png b/pdns/dnsdistdist/docs/imgs/AsyncQuery.png
new file mode 100644 (file)
index 0000000..b9c5ab1
Binary files /dev/null and b/pdns/dnsdistdist/docs/imgs/AsyncQuery.png differ
index 1b07bf672d8cd7d6b99aceea13453ba023100af7..5661b7edc2f1ce7bfd5f20c47b0357ccbec502ba 100644 (file)
@@ -42,6 +42,11 @@ and then to create a :class:`DNSPacketOverlay` object:
 
   :param str packet: The DNS payload
 
+.. _DNSPacketOverlay:
+
+DNSPacketOverlay
+----------------
+
 .. class:: DNSPacketOverlay
 
   .. versionadded:: 1.8.0
index 8bd47cc03f2f7491e43193284a593a2f76992ca4..4f4afa4fe3b1e53d170d9e5f179b0297b7455322 100644 (file)
@@ -217,6 +217,14 @@ This state can be modified from the various hooks.
 
     :param string reason: An optional string describing the reason why this trap was sent
 
+  .. method:: DNSQuestion:setContent(data)
+
+    .. versionadded:: 1.8.0
+
+    Replace the DNS payload of the query with the supplied data, and adjusts the ID in the DNS header to the existing query.
+
+    :param string data: The raw DNS payload
+
   .. method:: DNSQuestion:setEDNSOption(code, data)
 
     .. versionadded:: 1.8.0
@@ -303,6 +311,18 @@ This state can be modified from the various hooks.
     :param string raw: The raw string to be spoofed, e.g. `"\\192\\000\\002\\001"`.
     :param table raws: The raw strings to be spoofed, e.g. `{ "\\192\\000\\002\\001", "\\192\\000\\002\\002" }`.
 
+  .. method:: DNSQuestion:suspend(asyncID, queryID, timeoutMS) -> bool
+
+    .. versionadded:: 1.8.0
+
+    Suspend the processing for the current query, making it asynchronous. The query is then placed into memory, in a map called the Asynchronous Holder, until it is either resumed or the supplied timeout kicks in. The object is stored under a key composed of the tuple (`asyncID`, `queryID`) which is needed to retrieve it later, which can be done via :func:`getAsynchronousObject`.
+    Note that the DNSQuestion object should NOT be accessed after successfully calling this method.
+    Returns true on success and false on failure, indicating that the query has not been suspended and the normal processing will continue.
+
+    :param int asyncID: A numeric identifier used to identify the suspended query for later retrieval
+    :param int queryID: A numeric identifier used to identify the suspended query for later retrieval. This ID does not have to match the query ID present in the initial DNS header. A given (asyncID, queryID) tuple should be unique at a given time.
+    :param int timeoutMS: The maximum duration this query will be kept in the asynchronous holder before being automatically resumed,  in milliseconds.
+
 .. _DNSResponse:
 
 DNSResponse object
@@ -371,6 +391,12 @@ DNSHeader (``dh``) object
 
     Get checking disabled flag.
 
+  .. method:: DNSHeader:getID() -> int
+
+    .. versionadded:: 1.8.0
+
+    Get the ID.
+
   .. method:: DNSHeader:getRA() -> bool
 
     Get recursion available flag.
@@ -438,3 +464,56 @@ EDNSOptionView object
   .. method:: EDNSOptionView:getValues()
 
     Return a table of NULL-safe strings values for this EDNS option.
+
+.. _AsynchronousObject:
+
+AsynchronousObject object
+=========================
+
+.. class:: AsynchronousObject
+
+  .. versionadded:: 1.8.0
+
+  This object holds a representation of a DNS query or response that has been suspended.
+
+  .. method:: AsynchronousObject:drop() -> bool
+
+    Drop that object immediately, without resuming it.
+    Returns true on success, false on failure.
+
+  .. method:: AsynchronousObject:getDQ() -> DNSQuestion
+
+    Return a DNSQuestion object for the suspended object.
+
+  .. method:: AsynchronousObject:getDR() -> DNSResponse
+
+    Return a DNSResponse object for the suspended object.
+
+  .. method:: AsynchronousObject:resume() -> bool
+
+    Resume the processing of the suspended object.
+    For a question, it means first checking whether it was turned into a response,
+    and sending the response out it it was. Otherwise do a cache-lookup, select a
+    backend and send the query to the backend.
+    For a response, it means inserting into the cache if needed and sending the
+    response to the backend.
+    Note that the AsynchronousObject object should NOT be accessed after successfully calling this method.
+    Returns true on success, false on failure.
+
+  .. method:: AsynchronousObject:setRCode(rcode, clearRecords) -> bool
+
+    Set the response code in the DNS header of the current object to the supplied value,
+    optionally removing all records from the existing payload, if any.
+    Returns true on success, false on failure.
+
+    :param int code: The response code to set
+    :param bool clearRecords: Whether to clear all records from the existing payload, if any
+
+.. function:: getAsynchronousObject(asyncID, queryID) -> AsynchronousObject
+
+  .. versionadded:: 1.8.0
+
+  Retrieves an asynchronous object stored into the Asynchronous holder.
+
+    :param int asyncID: A numeric identifier used to identify the query when it was suspended
+    :param int queryID: A numeric identifier used to identify the query when it was suspended