From: Remi Gacogne Date: Tue, 10 Jan 2023 13:53:29 +0000 (+0100) Subject: dnsdist: Add documentation for suspending/resuming objects X-Git-Tag: dnsdist-1.8.0-rc1~86^2~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4b6fa7a772ad9b0a6375bced51d58841e76c385b;p=thirdparty%2Fpdns.git dnsdist: Add documentation for suspending/resuming objects --- diff --git a/pdns/dnsdistdist/docs/advanced/asynchronous-processing.rst b/pdns/dnsdistdist/docs/advanced/asynchronous-processing.rst new file mode 100644 index 0000000000..404e0170d1 --- /dev/null +++ b/pdns/dnsdistdist/docs/advanced/asynchronous-processing.rst @@ -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)) diff --git a/pdns/dnsdistdist/docs/advanced/index.rst b/pdns/dnsdistdist/docs/advanced/index.rst index a028a9292f..e390db09eb 100644 --- a/pdns/dnsdistdist/docs/advanced/index.rst +++ b/pdns/dnsdistdist/docs/advanced/index.rst @@ -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 index 0000000000..b9c5ab121c Binary files /dev/null and b/pdns/dnsdistdist/docs/imgs/AsyncQuery.png differ diff --git a/pdns/dnsdistdist/docs/reference/dnsparser.rst b/pdns/dnsdistdist/docs/reference/dnsparser.rst index 1b07bf672d..5661b7edc2 100644 --- a/pdns/dnsdistdist/docs/reference/dnsparser.rst +++ b/pdns/dnsdistdist/docs/reference/dnsparser.rst @@ -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 diff --git a/pdns/dnsdistdist/docs/reference/dq.rst b/pdns/dnsdistdist/docs/reference/dq.rst index 8bd47cc03f..4f4afa4fe3 100644 --- a/pdns/dnsdistdist/docs/reference/dq.rst +++ b/pdns/dnsdistdist/docs/reference/dq.rst @@ -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