]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add isc_dnsbuffer_t implementation
authorArtem Boldariev <artem@boldariev.com>
Tue, 7 Jun 2022 10:14:29 +0000 (13:14 +0300)
committerArtem Boldariev <artem@boldariev.com>
Tue, 20 Dec 2022 19:24:44 +0000 (21:24 +0200)
This commit adds "isc_dnsbuffer_t" object implementation, a thin
wrapper on top of "isc_buffer_t" which has the following
characteristics:

* provides interface specifically atuned for handling/generating DNS
messages, especially in the format used for DNS messages over TCP;
* avoids allocating dynamic memory when handling small DNS messages,
while transparently switching to using dynamic memory when handling
larger messages. This approach significantly reduces pressure on the
memory allocator, as most of the DNS messages are small.

.gitlab-ci.yml
lib/isc/Makefile.am
lib/isc/include/isc/dnsbuffer.h [new file with mode: 0644]

index 1a1ddc9b67aef3f7a4086b427d0a77ce53d8473d..6e14d6d634721b5ab54b722e53037e1a178f9549 100644 (file)
@@ -1521,6 +1521,7 @@ gcov:
     - for SRC in dns isc; do for DST in dns isc ns; do cp -f "lib/${SRC}/include/${SRC}"/*.h "lib/${DST}/"; done; done
     - find bin lib -maxdepth 1 -mindepth 1 -type d -exec cp -f lib/isc/include/isc/buffer.h "{}" \;
     - cp -f lib/isc/include/isc/buffer.h lib/isc/netmgr/buffer.h
+    - cp -f lib/isc/include/isc/dnsbuffer.h lib/isc/netmgr/dnsbuffer.h
     # Help gcovr find dlz_dbi.c file
     - for DST in ldap mysql mysqldyn sqlite3 wildcard; do cp contrib/dlz/modules/common/dlz_dbi.c "contrib/dlz/modules/${DST}"; done
     # Generate XML file in the Cobertura XML format suitable for use by GitLab
index e3ead9b465f5d9aae15208acb15c322147f45680..3d55874dabfc52c852139cd9cee7b8d93efab08e 100644 (file)
@@ -25,6 +25,7 @@ libisc_la_HEADERS =                   \
        include/isc/crc64.h             \
        include/isc/deprecated.h        \
        include/isc/dir.h               \
+       include/isc/dnsbuffer.h         \
        include/isc/endian.h            \
        include/isc/entropy.h           \
        include/isc/errno.h             \
diff --git a/lib/isc/include/isc/dnsbuffer.h b/lib/isc/include/isc/dnsbuffer.h
new file mode 100644 (file)
index 0000000..c79c7ad
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/region.h>
+#include <isc/util.h>
+
+#define ISC_DNSBUFFER_STATIC_BUFFER_SIZE         (512)
+#define ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE (ISC_BUFFER_INCR * 2)
+
+typedef struct isc_dnsbuffer {
+       isc_buffer_t *current; /* pointer to the currently used buffer */
+       isc_buffer_t  stbuf;   /* static memory buffer */
+       uint8_t buf[ISC_DNSBUFFER_STATIC_BUFFER_SIZE]; /* storage for the static
+                                                         buffer */
+       isc_buffer_t *dynbuf; /* resizeable dynamic memory buffer */
+       isc_mem_t    *mctx;
+} isc_dnsbuffer_t;
+/*
+ * The 'isc_dnsbuffer_t' object implementation is a thin wrapper on
+ * top of 'isc_buffer_t' which has the following characteristics:
+ *
+ * - provides interface specifically atuned for handling/generating
+ *   DNS messages, especially in the format used for DNS messages over
+ *   TCP;
+ *
+ * - avoids allocating dynamic memory when handling small DNS
+ *   messages, while transparently switching to using dynamic memory
+ *   when handling larger messages. This approach significantly
+ *   reduces pressure on the memory allocator, as most of the DNS
+ *   messages are small.
+ */
+
+static inline void
+isc_dnsbuffer_init(isc_dnsbuffer_t *restrict dnsbuf, isc_mem_t *memctx);
+/*!<
+ *  \brief Initialise the 'isc_dnsbuffer_t' object, keep a reference to
+ * 'memctx' inside the object.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL;
+ *\li  'memctx' is not NULL.
+ */
+
+static inline void
+isc_dnsbuffer_uninit(isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Un-initialise the 'isc_dnsbuffer_t' object, de-allocate any
+ *dynamically allocated memory, detach from an internal memory context
+ * reference.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline isc_dnsbuffer_t *
+isc_dnsbuffer_new(isc_mem_t *memctx);
+/*!<
+ * \brief Allocate and initialise a new 'isc_dnsbuffer_t' object, keep a
+ * reference to 'memctx' inside the object.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL;
+ *\li  'memctx' is not NULL.
+ */
+
+static inline void
+isc_dnsbuffer_free(isc_dnsbuffer_t **restrict pdnsbuf);
+/*!<
+ * \brief Un-initialise and de-allocate the given 'isc_dnsbuffer_t' object.
+ *
+ * Requires:
+ *\li  'pdnsbuf' is not NULL;
+ *\li  'pdnsbuf' does not point to NULL.
+ */
+
+static inline void
+isc_dnsbuffer_clear(isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Clear the given 'isc_dnsbuffer_t' object (make it empty).
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline unsigned int
+isc_dnsbuffer_length(const isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Return the total length of the internal memory buffer of
+ * the given 'isc_dnsbuffer_t' object.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline unsigned int
+isc_dnsbuffer_usedlength(const isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Return the total number of used bytes from the internal
+ * memory buffer of the given 'isc_dnsbuffer_t' object.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline unsigned int
+isc_dnsbuffer_remaininglength(const isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Return the total number of remaining (unprocessed data)
+ * bytes from the internal memory buffer of the given
+ * 'isc_dnsbuffer_t' object.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline void
+isc_dnsbuffer_remainingregion(const isc_dnsbuffer_t *restrict dnsbuf,
+                             isc_region_t *region);
+/*!<
+ * \brief Make the given 'isc_region_t' object reference remaining
+ * (unprocessed) data from the internal memory buffer of the given
+ * 'isc_dnsbuffer_t' object.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline void
+isc_dnsbuffer_compact(const isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Compact the internal used memory region of the internal
+ * memory buffer of the given 'isc_dnsbuffer_t' object so that it
+ * occurs at the start of the memory buffer. Then used region is
+ * shrunk by the size of the processed (consumed) region, and the
+ * consumed region is then made empty. This way the previously
+ * processed (consumed) amount of memory can be used again without
+ * resizing/reallocating the buffer.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline bool
+isc_dnsbuffer_trycompact(const isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Compact the internal used memory region of the internal
+ * memory buffer of the given 'isc_dnsbuffer_t' object in the case
+ * when the processed (consumed) region is larger or equal in size to
+ * the unprocessed one.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline void
+isc_dnsbuffer_consume(isc_dnsbuffer_t *restrict dnsbuf, const unsigned int n);
+/*!<
+ * \brief Consume the given number of bytes from the beginning of
+ * the unprocessed data region of the given 'isc_dnsbuffer_t' object.
+ * The call moves the 'current' unprocessed data region pointer by the
+ * given number of bytes.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline void
+isc_dnsbuffer_putmem(isc_dnsbuffer_t *restrict dnsbuf, void *buf,
+                    const unsigned int buf_size);
+/*!<
+ * \brief Copy 'buf_size' bytes from the location pointed to by
+ * 'buf' pointer to the end of the unprocessed data region of the
+ * given 'isc_dnsbuffer_t' object. Resize/reallocate the internal
+ * memory buffer if it is too small.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL;
+ *\li  'buf' is not NULL;
+ *\li  'buf_size' is greater than '0'.
+ */
+
+static inline uint8_t *
+isc_dnsbuffer_current(const isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Return the pointer to the beginning of unprocessed data
+ * region of the given 'isc_dnsbuffer_t' object ("current pointer").
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline uint16_t
+isc_dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Lookup an unsigned short (16-bit) value in
+ * big-endian/network byte order at the beginning of unprocessed data
+ * region of the given 'isc_dnsbuffer_t' object.
+ *
+ * If there is not enough data available in the region, '0' will be
+ * returned.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline uint16_t
+isc_dnsbuffer_consume_uint16be(isc_dnsbuffer_t *restrict dnsbuf);
+/*!<
+ * \brief Read an unsigned short (16-bit) value in
+ * big-endian/network byte order at the beginning of unprocessed data
+ * region of the given 'isc_dnsbuffer_t' object.
+ *
+ * If there is not enough data available in the region, '0' will be
+ * returned.
+ *
+ * In the case, when the data has been read successfully, the start of
+ * the unprocessed data region is advanced by two bytes.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline void
+isc_dnsbuffer_putmem_uint16be(isc_dnsbuffer_t *restrict dnsbuf,
+                             const uint16_t v);
+/*!<
+ * \brief Append a given unsigned short (16-bit) value 'v' converted
+ * into big-endian/network byte order at the end of unprocessed data
+ * region of the given 'isc_dnsbuffer_t' object. Resize/reallocate the
+ * internal memory buffer if it is too small to hold the appended data.
+ *
+ * Requires:
+ *\li  'dnsbuf' is not NULL.
+ */
+
+static inline void
+isc_dnsbuffer_init(isc_dnsbuffer_t *restrict dnsbuf, isc_mem_t *memctx) {
+       REQUIRE(dnsbuf != NULL);
+       REQUIRE(memctx != NULL);
+       *dnsbuf = (isc_dnsbuffer_t){ .current = &dnsbuf->stbuf };
+       isc_buffer_init(&dnsbuf->stbuf, &dnsbuf->buf[0], sizeof(dnsbuf->buf));
+       isc_mem_attach(memctx, &dnsbuf->mctx);
+}
+
+static inline void
+isc_dnsbuffer_uninit(isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       isc_buffer_clear(&dnsbuf->stbuf);
+       if (dnsbuf->dynbuf != NULL) {
+               isc_buffer_free(&dnsbuf->dynbuf);
+       }
+
+       if (dnsbuf->mctx != NULL) {
+               isc_mem_detach(&dnsbuf->mctx);
+       }
+}
+
+static inline isc_dnsbuffer_t *
+isc_dnsbuffer_new(isc_mem_t *memctx) {
+       isc_dnsbuffer_t *newbuf;
+
+       REQUIRE(memctx != NULL);
+
+       newbuf = isc_mem_get(memctx, sizeof(*newbuf));
+       isc_dnsbuffer_init(newbuf, memctx);
+
+       return (newbuf);
+}
+
+static inline void
+isc_dnsbuffer_free(isc_dnsbuffer_t **restrict pdnsbuf) {
+       isc_dnsbuffer_t *restrict buf = NULL;
+       isc_mem_t *memctx = NULL;
+       REQUIRE(pdnsbuf != NULL && *pdnsbuf != NULL);
+
+       buf = *pdnsbuf;
+
+       isc_mem_attach(buf->mctx, &memctx);
+       isc_dnsbuffer_uninit(buf);
+       isc_mem_putanddetach(&memctx, buf, sizeof(*buf));
+
+       *pdnsbuf = NULL;
+}
+
+static inline void
+isc_dnsbuffer_clear(isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       isc_buffer_clear(dnsbuf->current);
+}
+
+static inline unsigned int
+isc_dnsbuffer_length(const isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       return (isc_buffer_length(dnsbuf->current));
+}
+
+static inline unsigned int
+isc_dnsbuffer_usedlength(const isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       return (isc_buffer_usedlength(dnsbuf->current));
+}
+
+static inline unsigned int
+isc_dnsbuffer_remaininglength(const isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       return (isc_buffer_remaininglength(dnsbuf->current));
+}
+
+static inline void
+isc_dnsbuffer_remainingregion(const isc_dnsbuffer_t *restrict dnsbuf,
+                             isc_region_t *region) {
+       REQUIRE(dnsbuf != NULL);
+       REQUIRE(region != NULL);
+       isc_buffer_remainingregion(dnsbuf->current, region);
+}
+
+static inline void
+isc_dnsbuffer_compact(const isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       isc_buffer_compact(dnsbuf->current);
+}
+
+static inline bool
+isc_dnsbuffer_trycompact(const isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       if (isc_buffer_consumedlength(dnsbuf->current) >=
+           isc_dnsbuffer_remaininglength(dnsbuf))
+       {
+               isc_dnsbuffer_compact(dnsbuf);
+               return (true);
+       }
+
+       return (false);
+}
+
+static inline void
+isc_dnsbuffer_consume(isc_dnsbuffer_t *restrict dnsbuf, const unsigned int n) {
+       REQUIRE(dnsbuf != NULL);
+       isc_buffer_forward(dnsbuf->current, n);
+}
+
+static inline void
+isc_dnsbuffer_putmem(isc_dnsbuffer_t *restrict dnsbuf, void *buf,
+                    const unsigned int buf_size) {
+       REQUIRE(dnsbuf != NULL);
+       REQUIRE(buf != NULL);
+       REQUIRE(buf_size > 0);
+       if (!(dnsbuf->current == &dnsbuf->stbuf &&
+             isc_buffer_availablelength(dnsbuf->current) >= buf_size) &&
+           dnsbuf->dynbuf == NULL)
+       {
+               isc_region_t remaining = { 0 };
+               unsigned int total_size = 0;
+
+               isc_buffer_remainingregion(&dnsbuf->stbuf, &remaining);
+               total_size = remaining.length + buf_size;
+               if (total_size < ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE) {
+                       total_size = ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE;
+               }
+               isc_buffer_allocate(dnsbuf->mctx, &dnsbuf->dynbuf, total_size);
+               isc_buffer_setautorealloc(dnsbuf->dynbuf, true);
+               if (remaining.length > 0) {
+                       isc_buffer_putmem(dnsbuf->dynbuf, remaining.base,
+                                         remaining.length);
+               }
+
+               dnsbuf->current = dnsbuf->dynbuf;
+       }
+
+       isc_buffer_putmem(dnsbuf->current, buf, buf_size);
+}
+
+static inline uint8_t *
+isc_dnsbuffer_current(const isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       return (isc_buffer_current(dnsbuf->current));
+}
+
+static inline uint16_t
+isc__dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf) {
+       uint16_t v;
+       uint8_t *p = (uint8_t *)isc_dnsbuffer_current(dnsbuf);
+
+       v = p[0] << 8;
+       v |= p[1] & 0xFF;
+
+       return (v);
+}
+
+static inline uint16_t
+isc_dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf) {
+       REQUIRE(dnsbuf != NULL);
+       if (isc_dnsbuffer_remaininglength(dnsbuf) < sizeof(uint16_t)) {
+               return (0);
+       }
+
+       return (isc__dnsbuffer_peek_uint16be(dnsbuf));
+}
+
+static inline uint16_t
+isc_dnsbuffer_consume_uint16be(isc_dnsbuffer_t *restrict dnsbuf) {
+       uint16_t v;
+
+       REQUIRE(dnsbuf != NULL);
+
+       if (isc_dnsbuffer_remaininglength(dnsbuf) < sizeof(uint16_t)) {
+               return (0);
+       }
+
+       v = isc__dnsbuffer_peek_uint16be(dnsbuf);
+
+       isc_dnsbuffer_consume(dnsbuf, sizeof(uint16_t));
+
+       return (v);
+}
+
+static inline void
+isc_dnsbuffer_putmem_uint16be(isc_dnsbuffer_t *restrict dnsbuf,
+                             const uint16_t v) {
+       uint8_t b[2] = { 0 };
+
+       REQUIRE(dnsbuf != NULL);
+
+       b[0] = v >> 8;
+       b[1] = v & 0xFF;
+
+       isc_dnsbuffer_putmem(dnsbuf, b, sizeof(b));
+}