]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
libknot/rrset-dump/bugfix: ERANGE instead of enlarging output buffer if overflow...
authorLibor Peltan <libor.peltan@nic.cz>
Mon, 27 Oct 2025 16:17:24 +0000 (17:17 +0100)
committerDaniel Salzman <daniel.salzman@nic.cz>
Fri, 31 Oct 2025 13:07:32 +0000 (14:07 +0100)
18 files changed:
Knot.files
src/contrib/base32hex.c
src/contrib/base32hex.h
src/contrib/base64.c
src/contrib/base64.h
src/contrib/base64url.c
src/contrib/base64url.h
src/libknot/descriptor.c
src/libknot/descriptor.h
src/libknot/errcode.h
src/libknot/error.c
tests/.gitignore
tests/Makefile.am
tests/contrib/test_base32hex.c
tests/contrib/test_base64.c
tests/contrib/test_base64url.c
tests/libknot/test_descriptor.c
tests/libknot/test_rrset-dump.c [new file with mode: 0644]

index e2065759cdb2b0e1d0dc0ec6649de5366570c8c6..437b96ed2307a22ad9dce6ab913ab5b97fca6c02 100644 (file)
@@ -743,6 +743,7 @@ tests/libknot/test_pkt.c
 tests/libknot/test_probe.c
 tests/libknot/test_rdata.c
 tests/libknot/test_rdataset.c
+tests/libknot/test_rrset-dump.c
 tests/libknot/test_rrset-wire.c
 tests/libknot/test_rrset.c
 tests/libknot/test_tsig.c
index 8a7d7dc43f086ee096e6fd5098bd8f0b191c817c..6affc9180a857eea04558ee1ddab69d7a3a2abea 100644 (file)
@@ -78,9 +78,12 @@ int32_t knot_base32hex_encode(const uint8_t  *in,
        if (in == NULL || out == NULL) {
                return KNOT_EINVAL;
        }
-       if (in_len > MAX_B32_BIN_DATA_LEN || out_len < ((in_len + 4) / 5) * 8) {
+       if (in_len > MAX_B32_BIN_DATA_LEN) {
                return KNOT_ERANGE;
        }
+       if (out_len < ((in_len + 4) / 5) * 8) {
+               return KNOT_ESPACE; // NOTE it is essential to return KNOT_ESPACE when output buffer overflows because this is directly called in rrset_dump and there is a realloc/retry mechanism in KNOT_ESPACE case.
+       }
 
        uint8_t         rest_len = in_len % 5;
        const uint8_t   *stop = in + in_len - rest_len;
@@ -191,9 +194,12 @@ int32_t knot_base32hex_decode(const uint8_t  *in,
        if (in == NULL || out == NULL) {
                return KNOT_EINVAL;
        }
-       if (in_len > INT32_MAX || out_len < ((in_len + 7) / 8) * 5) {
+       if (in_len > INT32_MAX) {
                return KNOT_ERANGE;
        }
+       if (out_len < ((in_len + 7) / 8) * 5) {
+               return KNOT_ESPACE;
+       }
        if ((in_len % 8) != 0) {
                return KNOT_BASE32HEX_ESIZE;
        }
index c5a81a58af2f5da75e16b5c50cb8dda23bce1412..5a907b9b2abaf544492f9a9d6e76f59be4fdabef 100644 (file)
@@ -26,7 +26,8 @@
  * \param out_len      Size of output buffer.
  *
  * \retval >=0         length of output string.
- * \retval KNOT_E*     if error.
+ * \retval KNOT_ESPACE if output buffer is not large enough.
+ * \return KNOT_E*     if other error.
  */
 int32_t knot_base32hex_encode(const uint8_t  *in,
                               const uint32_t in_len,
@@ -65,7 +66,8 @@ int32_t knot_base32hex_encode_alloc(const uint8_t  *in,
  * \param out_len      Size of output buffer.
  *
  * \retval >=0         length of output data.
- * \retval KNOT_E*     if error.
+ * \retval KNOT_ESPACE if output buffer is not large enough.
+ * \return KNOT_E*     if other error.
  */
 int32_t knot_base32hex_decode(const uint8_t  *in,
                               const uint32_t in_len,
index ed123fec7be8950a6f5eab840c71c62e7dfb6401..a878731878c703a04dfeb08881863f1d185c0c2b 100644 (file)
@@ -79,9 +79,12 @@ int32_t knot_base64_encode(const uint8_t  *in,
        if (in == NULL || out == NULL) {
                return KNOT_EINVAL;
        }
-       if (in_len > MAX_B64_BIN_DATA_LEN || out_len < ((in_len + 2) / 3) * 4) {
+       if (in_len > MAX_B64_BIN_DATA_LEN) {
                return KNOT_ERANGE;
        }
+       if (out_len < ((in_len + 2) / 3) * 4) {
+               return KNOT_ESPACE; // NOTE it is essential to return KNOT_ESPACE when output buffer overflows because this is directly called in rrset_dump and there is a realloc/retry mechanism in KNOT_ESPACE case.
+       }
 
        uint8_t         rest_len = in_len % 3;
        const uint8_t   *stop = in + in_len - rest_len;
@@ -158,9 +161,12 @@ int32_t knot_base64_decode(const uint8_t  *in,
        if (in == NULL || out == NULL) {
                return KNOT_EINVAL;
        }
-       if (in_len > INT32_MAX || out_len < ((in_len + 3) / 4) * 3) {
+       if (in_len > INT32_MAX) {
                return KNOT_ERANGE;
        }
+       if (out_len < ((in_len + 3) / 4) * 3) {
+               return KNOT_ESPACE;
+       }
        if ((in_len % 4) != 0) {
                return KNOT_BASE64_ESIZE;
        }
index f512dba075f7584ec98796f579ce61cc63e8585a..c6bc79796f64c2e5033f73d4830dc48f1f7b1ee5 100644 (file)
@@ -23,7 +23,8 @@
  * \param out_len      Size of output buffer.
  *
  * \retval >=0         length of output string.
- * \retval KNOT_E*     if error.
+ * \retval KNOT_ESPACE if output buffer is not large enough.
+ * \return KNOT_E*     if other error.
  */
 int32_t knot_base64_encode(const uint8_t  *in,
                            const uint32_t in_len,
@@ -62,7 +63,8 @@ int32_t knot_base64_encode_alloc(const uint8_t  *in,
  * \param out_len      Size of output buffer.
  *
  * \retval >=0         length of output data.
- * \retval KNOT_E*     if error.
+ * \retval KNOT_ESPACE if output buffer is not large enough.
+ * \return KNOT_E*     if other error.
  */
 int32_t knot_base64_decode(const uint8_t  *in,
                            const uint32_t in_len,
index 38c8fd9c040050a74c5a7f2c437f663f0a4f199b..64bf1bcb63db773d29c488727dcde19e4af7693b 100644 (file)
@@ -80,9 +80,12 @@ int32_t knot_base64url_encode(const uint8_t  *in,
        if (in == NULL || out == NULL) {
                return KNOT_EINVAL;
        }
-       if (in_len > MAX_BIN_DATA_LEN || out_len < ((in_len + 2) / 3) * 4) {
+       if (in_len > MAX_BIN_DATA_LEN) {
                return KNOT_ERANGE;
        }
+       if (out_len < ((in_len + 2) / 3) * 4) {
+               return KNOT_ESPACE;
+       }
 
        uint8_t         rest_len = in_len % 3;
        const uint8_t   *stop = in + in_len - rest_len;
@@ -170,9 +173,12 @@ int32_t knot_base64url_decode(const uint8_t  *in,
                }
        }
 
-       if (in_len > INT32_MAX || out_len < ((in_len + 3) / 4) * 3) {
+       if (in_len > INT32_MAX) {
                return KNOT_ERANGE;
        }
+       if (out_len < ((in_len + 3) / 4) * 3) {
+               return KNOT_ESPACE;
+       }
 
        const uint8_t   *stop = in + in_len;
        uint8_t         *bin = out;
index 43c27c355b85e0797d8a04c761bf0b573b46c413..07f8e617adbcc643e8fbca298f91d2facde821aa 100644 (file)
@@ -23,7 +23,8 @@
  * \param out_len      Size of output buffer.
  *
  * \retval >=0         length of output string.
- * \retval KNOT_E*     if error.
+ * \retval KNOT_ESPACE if output buffer is not large enough.
+ * \return KNOT_E*     if other error.
  */
 int32_t knot_base64url_encode(const uint8_t  *in,
                               const uint32_t in_len,
@@ -62,7 +63,8 @@ int32_t knot_base64url_encode_alloc(const uint8_t  *in,
  * \param out_len      Size of output buffer.
  *
  * \retval >=0         length of output data.
- * \retval KNOT_E*     if error.
+ * \retval KNOT_ESPACE if output buffer is not large enough.
+ * \return KNOT_E*     if other error.
  */
 int32_t knot_base64url_decode(const uint8_t  *in,
                               uint32_t       in_len,
index dcd0eda26411850fe747b7261f68990c4e1cdb28..441e45a95361d6f74300780ded3122b24f3dae18 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "libknot/attribute.h"
 #include "libknot/descriptor.h"
+#include "libknot/errcode.h"
 
 /*!
  * \brief Table with supported DNS classes.
@@ -197,7 +198,7 @@ int knot_rrtype_to_string(const uint16_t rrtype,
                           const size_t   out_len)
 {
        if (out == NULL) {
-               return -1;
+               return KNOT_EINVAL;
        }
 
        int ret;
@@ -211,7 +212,7 @@ int knot_rrtype_to_string(const uint16_t rrtype,
        }
 
        if (ret <= 0 || (size_t)ret >= out_len) {
-               return -1;
+               return KNOT_ESPACE; // NOTE it is essential to return KNOT_ESPACE when output buffer overflows because this is directly called in rrset_dump and there is a realloc/retry mechanism in KNOT_ESPACE case.
        } else {
                return ret;
        }
@@ -221,7 +222,7 @@ _public_
 int knot_rrtype_from_string(const char *name, uint16_t *num)
 {
        if (name == NULL || num == NULL) {
-               return -1;
+               return KNOT_EINVAL;
        }
 
        int i;
@@ -233,13 +234,13 @@ int knot_rrtype_from_string(const char *name, uint16_t *num)
                if (rdata_descriptors[i].type_name != NULL &&
                    strcasecmp(rdata_descriptors[i].type_name, name) == 0) {
                        *num = i;
-                       return 0;
+                       return KNOT_EOK;
                }
        }
 
        // Type name must begin with TYPE.
        if (strncasecmp(name, "TYPE", 4) != 0) {
-               return -1;
+               return KNOT_ENOTYPE;
        } else {
                name += 4;
        }
@@ -247,11 +248,11 @@ int knot_rrtype_from_string(const char *name, uint16_t *num)
        // The rest must be a number.
        n = strtoul(name, &end, 10);
        if (end == name || *end != '\0' || n > UINT16_MAX) {
-               return -1;
+               return KNOT_ENOTYPE;
        }
 
        *num = n;
-       return 0;
+       return KNOT_EOK;
 }
 
 _public_
@@ -260,7 +261,7 @@ int knot_rrclass_to_string(const uint16_t rrclass,
                            const size_t   out_len)
 {
        if (out == NULL) {
-               return -1;
+               return KNOT_EINVAL;
        }
 
        int ret;
@@ -272,7 +273,7 @@ int knot_rrclass_to_string(const uint16_t rrclass,
        }
 
        if (ret <= 0 || (size_t)ret >= out_len) {
-               return -1;
+               return KNOT_ESPACE;
        } else {
                return ret;
        }
@@ -282,7 +283,7 @@ _public_
 int knot_rrclass_from_string(const char *name, uint16_t *num)
 {
        if (name == NULL || num == NULL) {
-               return -1;
+               return KNOT_EINVAL;
        }
 
        int i;
@@ -295,13 +296,13 @@ int knot_rrclass_from_string(const char *name, uint16_t *num)
                if ((row[0] != NULL && strcasecmp(row[0], name) == 0) ||
                    (row[1] != NULL && strcasecmp(row[1], name) == 0)) {
                        *num = i;
-                       return 0;
+                       return KNOT_EOK;
                }
        }
 
        // Class name must begin with CLASS.
        if (strncasecmp(name, "CLASS", 5) != 0) {
-               return -1;
+               return KNOT_ENOCLASS;
        } else {
                name += 5;
        }
@@ -309,11 +310,11 @@ int knot_rrclass_from_string(const char *name, uint16_t *num)
        // The rest must be a number.
        n = strtoul(name, &end, 10);
        if (end == name || *end != '\0' || n > UINT16_MAX) {
-               return -1;
+               return KNOT_ENOCLASS;
        }
 
        *num = n;
-       return 0;
+       return KNOT_EOK;
 }
 
 _public_
@@ -382,7 +383,7 @@ _public_
 int knot_opt_code_to_string(const uint16_t code, char *out, const size_t out_len)
 {
        if (out == NULL) {
-               return -1;
+               return KNOT_EINVAL;
        }
 
        const char *name = NULL;
@@ -412,7 +413,7 @@ int knot_opt_code_to_string(const uint16_t code, char *out, const size_t out_len
        }
 
        if (ret <= 0 || (size_t)ret >= out_len) {
-               return -1;
+               return KNOT_ESPACE;
        } else {
                return ret;
        }
index d4644a04b29420a5cadf20ed8e8583af627f1c27..bd8a6955254fbfc9dca47b25f3a34b80de5e6f18 100644 (file)
@@ -196,7 +196,8 @@ const knot_rdata_descriptor_t *knot_get_obsolete_rdata_descriptor(const uint16_t
  * \param out_len Length of the output buffer.
  *
  * \retval Length of output string.
- * \retval -1 if error.
+ * \retval KNOT_ESPACE if output buffer not large enough.
+ * \return KNOT_E* if other error.
  */
 int knot_rrtype_to_string(const uint16_t rrtype,
                           char           *out,
@@ -208,8 +209,7 @@ int knot_rrtype_to_string(const uint16_t rrtype,
  * \param name Mnemonic string to be converted.
  * \param num  Output variable.
  *
- * \retval  0 if OK.
- * \retval -1 if error.
+ * \return KNOT_E*
  */
 int knot_rrtype_from_string(const char *name, uint16_t *num);
 
@@ -221,7 +221,8 @@ int knot_rrtype_from_string(const char *name, uint16_t *num);
  * \param out_len Length of the output buffer.
  *
  * \retval Length of output string.
- * \retval -1 if error.
+ * \retval KNOT_ESPACE if output buffer not large enough.
+ * \return KNOT_E* if other error.
  */
 int knot_rrclass_to_string(const uint16_t rrclass,
                            char           *out,
@@ -233,8 +234,7 @@ int knot_rrclass_to_string(const uint16_t rrclass,
  * \param name Mnemonic string to be converted.
  * \param num  Output variable.
  *
- * \retval  0 if OK.
- * \retval -1 if error.
+ * \return KNOT_E*
  */
 int knot_rrclass_from_string(const char *name, uint16_t *num);
 
@@ -292,7 +292,8 @@ bool knot_rrtype_should_be_lowercased(const uint16_t type);
  * \param out_len  The available size of the buffer.
  *
  * \retval Length of output string.
- * \retval -1 if error.
+ * \retval KNOT_ESPACE if output buffer not large enough.
+ * \return KNOT_E* if other error.
  */
 int knot_opt_code_to_string(const uint16_t code, char *out, const size_t out_len);
 
index afa69f0999bb5fa198208057cad24dbc9473757b..fe3a2dab880fe6ecf37a320dcdb44b8df6b33be0 100644 (file)
@@ -102,6 +102,8 @@ enum knot_error {
        KNOT_EMODINVAL,
        KNOT_EEXTERNAL,
        KNOT_ERDB,
+       KNOT_ENOTYPE,
+       KNOT_ENOCLASS,
 
        KNOT_GENERAL_ERROR = -900,
 
index a2b3e213f1b649f6b59d55e58f8ae5d969e3ae7a..c6e32b104cb1e78f64ba3faf4d1fcc4624482da1 100644 (file)
@@ -100,6 +100,8 @@ static const struct error errors[] = {
        { KNOT_EMODINVAL,    "invalid module" },
        { KNOT_EEXTERNAL,    "external validation failed" },
        { KNOT_ERDB,         "zone database error" },
+       { KNOT_ENOTYPE,      "no such RR type" },
+       { KNOT_ENOCLASS,     "no such CLASS" },
 
        { KNOT_GENERAL_ERROR, "unknown general error" },
 
index a553a648a468822b2a7fd0b38157ac42259323ef..123430fc01af906d5b5a9f7a29f6b42930b8d6db 100644 (file)
@@ -79,6 +79,7 @@
 /libknot/test_rdata
 /libknot/test_rdataset
 /libknot/test_rrset
+/libknot/test_rrset-dump
 /libknot/test_rrset-wire
 /libknot/test_tsig
 /libknot/test_xdp_tcp
index 6e662272d8f42c7bdba33130d16285cc1e1cf81e..e39d9eafdd299a424318c8039e640581bbf9c639 100644 (file)
@@ -147,6 +147,7 @@ check_PROGRAMS += \
        libknot/test_rdata                      \
        libknot/test_rdataset                   \
        libknot/test_rrset                      \
+       libknot/test_rrset-dump                 \
        libknot/test_rrset-wire                 \
        libknot/test_tsig                       \
        libknot/test_yparser                    \
index 6ad468826e6742c41eee287db0c284d3fd7a9fcc..492ff2902ad166a6eb3025777f7d8243659fc207 100644 (file)
@@ -31,7 +31,7 @@ int main(int argc, char *argv[])
        ret = knot_base32hex_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
        is_int(KNOT_ERANGE, ret, "knot_base32hex_encode: input buffer too large");
        ret = knot_base32hex_encode(in, BUF_LEN, out, BUF_LEN);
-       is_int(KNOT_ERANGE, ret, "knot_base32hex_encode: output buffer too small");
+       is_int(KNOT_ESPACE, ret, "knot_base32hex_encode: output buffer too small");
 
        ret = knot_base32hex_encode_alloc(NULL, 0, &out3);
        is_int(KNOT_EINVAL, ret, "knot_base32hex_encode_alloc: NULL input buffer");
@@ -47,7 +47,7 @@ int main(int argc, char *argv[])
        ret = knot_base32hex_decode(in, UINT32_MAX, out, BUF_LEN);
        is_int(KNOT_ERANGE, ret, "knot_base32hex_decode: input buffer too large");
        ret = knot_base32hex_decode(in, BUF_LEN, out, 0);
-       is_int(KNOT_ERANGE, ret, "knot_base32hex_decode: output buffer too small");
+       is_int(KNOT_ESPACE, ret, "knot_base32hex_decode: output buffer too small");
 
        ret = knot_base32hex_decode_alloc(NULL, 0, &out3);
        is_int(KNOT_EINVAL, ret, "knot_base32hex_decode_alloc: NULL input buffer");
index e6eb72499c8870032e6f12efbceececd4b82b4a3..83dc72a24813dd9800368482bb90aa10bce47d72 100644 (file)
@@ -31,7 +31,7 @@ int main(int argc, char *argv[])
        ret = knot_base64_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
        is_int(KNOT_ERANGE, ret, "knot_base64_encode: input buffer too large");
        ret = knot_base64_encode(in, BUF_LEN, out, BUF_LEN);
-       is_int(KNOT_ERANGE, ret, "knot_base64_encode: output buffer too small");
+       is_int(KNOT_ESPACE, ret, "knot_base64_encode: output buffer too small");
 
        ret = knot_base64_encode_alloc(NULL, 0, &out3);
        is_int(KNOT_EINVAL, ret, "knot_base64_encode_alloc: NULL input buffer");
@@ -47,7 +47,7 @@ int main(int argc, char *argv[])
        ret = knot_base64_decode(in, UINT32_MAX, out, BUF_LEN);
        is_int(KNOT_ERANGE, ret, "knot_base64_decode: input buffer too large");
        ret = knot_base64_decode(in, BUF_LEN, out, 0);
-       is_int(KNOT_ERANGE, ret, "knot_base64_decode: output buffer too small");
+       is_int(KNOT_ESPACE, ret, "knot_base64_decode: output buffer too small");
 
        ret = knot_base64_decode_alloc(NULL, 0, &out3);
        is_int(KNOT_EINVAL, ret, "knot_base64_decode_alloc: NULL input buffer");
index 458c4e7503593d57208c85b7b9b718359d44f597..4540ae568e8d3da78f44517c96b9a8b86b36044a 100644 (file)
@@ -31,7 +31,7 @@ int main(int argc, char *argv[])
        ret = knot_base64url_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
        is_int(KNOT_ERANGE, ret, "knot_base64ulr_encode: input buffer too large");
        ret = knot_base64url_encode(in, BUF_LEN, out, BUF_LEN);
-       is_int(KNOT_ERANGE, ret, "knot_base64ulr_encode: output buffer too small");
+       is_int(KNOT_ESPACE, ret, "knot_base64ulr_encode: output buffer too small");
 
        ret = knot_base64url_encode_alloc(NULL, 0, &out3);
        is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode_alloc: NULL input buffer");
@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
        ret = knot_base64url_decode(in, BUF_LEN, NULL, 0);
        is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode: NULL output buffer");
        ret = knot_base64url_decode(in, BUF_LEN, out, 0);
-       is_int(KNOT_ERANGE, ret, "knot_base64ulr_decode: output buffer too small");
+       is_int(KNOT_ESPACE, ret, "knot_base64ulr_decode: output buffer too small");
 
        ret = knot_base64url_decode_alloc(NULL, 0, &out3);
        is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode_alloc: NULL input buffer");
index 7c10fd63fa86df2aad70474fcec81b27565fcd89..d04bc4b8cee1dec277ea56a8d625b91ffe05b919 100644 (file)
@@ -9,6 +9,7 @@
 #include <tap/basic.h>
 
 #include "libknot/descriptor.h"
+#include "libknot/errcode.h"
 
 #define BUF_LEN 256
 
@@ -31,7 +32,7 @@ int main(int argc, char *argv[])
           "get TYPE0 descriptor 2. item type");
 
        ret = knot_rrtype_to_string(0, name, BUF_LEN);
-       ok(ret != -1, "get TYPE0 ret");
+       ok(ret > 0, "get TYPE0 ret");
        ok(strcmp(name, "TYPE0") == 0, "get TYPE0 name");
 
        // 2. A
@@ -43,7 +44,7 @@ int main(int argc, char *argv[])
           "get A descriptor 2. item type");
 
        ret = knot_rrtype_to_string(1, name, BUF_LEN);
-       ok(ret != -1, "get A ret");
+       ok(ret > 0, "get A ret");
        ok(strcmp(name, "A") == 0, "get A name");
 
        // 3. CNAME
@@ -55,7 +56,7 @@ int main(int argc, char *argv[])
           "get CNAME descriptor 2. item type");
 
        ret = knot_rrtype_to_string(5, name, BUF_LEN);
-       ok(ret != -1, "get CNAME ret");
+       ok(ret > 0, "get CNAME ret");
        ok(strcmp(name, "CNAME") == 0, "get CNAME name");
 
        // 4. TYPE38 (A6)
@@ -67,7 +68,7 @@ int main(int argc, char *argv[])
           "get TYPE38 descriptor 2. item type");
 
        ret = knot_rrtype_to_string(38, name, BUF_LEN);
-       ok(ret != -1, "get TYPE38 ret");
+       ok(ret > 0, "get TYPE38 ret");
        ok(strcmp(name, "TYPE38") == 0, "get TYPE38 name");
 
        // 5. ANY
@@ -79,7 +80,7 @@ int main(int argc, char *argv[])
           "get ANY descriptor 2. item type");
 
        ret = knot_rrtype_to_string(255, name, BUF_LEN);
-       ok(ret != -1, "get ANY ret");
+       ok(ret > 0, "get ANY ret");
        ok(strcmp(name, "ANY") == 0, "get ANY name");
 
        // 6. TYPE65535
@@ -91,121 +92,121 @@ int main(int argc, char *argv[])
           "get TYPE65535 descriptor 2. item type");
 
        ret = knot_rrtype_to_string(65535, name, BUF_LEN);
-       ok(ret != -1, "get TYPE65535 ret");
+       ok(ret > 0, "get TYPE65535 ret");
        ok(strcmp(name, "TYPE65535") == 0, "get TYPE65535 name");
 
        // Class num to string:
        // 7. CLASS0
        ret = knot_rrclass_to_string(0, name, BUF_LEN);
-       ok(ret != -1, "get CLASS0 ret");
+       ok(ret > 0, "get CLASS0 ret");
        ok(strcmp(name, "CLASS0") == 0, "get CLASS0 name");
 
        // 8. IN
        ret = knot_rrclass_to_string(1, name, BUF_LEN);
-       ok(ret != -1, "get IN ret");
+       ok(ret > 0, "get IN ret");
        ok(strcmp(name, "IN") == 0, "get IN name");
 
        // 9. ANY
        ret = knot_rrclass_to_string(255, name, BUF_LEN);
-       ok(ret != -1, "get ANY ret");
+       ok(ret > 0, "get ANY ret");
        ok(strcmp(name, "ANY") == 0, "get ANY name");
 
        // 10. CLASS65535
        ret = knot_rrclass_to_string(65535, name, BUF_LEN);
-       ok(ret != -1, "get CLASS65535 ret");
+       ok(ret > 0, "get CLASS65535 ret");
        ok(strcmp(name, "CLASS65535") == 0, "get CLASS65535 name");
 
        // String to type num:
        // 11. A
        ret = knot_rrtype_from_string("A", &num);
-       ok(ret != -1, "get A num ret");
+       ok(ret == KNOT_EOK, "get A num ret");
        ok(num == 1, "get A num");
 
        // 12. a
        ret = knot_rrtype_from_string("a", &num);
-       ok(ret != -1, "get a num ret");
+       ok(ret == KNOT_EOK, "get a num ret");
        ok(num == 1, "get a num");
 
        // 13. AaAa
        ret = knot_rrtype_from_string("AaAa", &num);
-       ok(ret != -1, "get AaAa num ret");
+       ok(ret == KNOT_EOK, "get AaAa num ret");
        ok(num == 28, "get AaAa num");
 
        // 14. ""
        ret = knot_rrtype_from_string("", &num);
-       ok(ret == -1, "get "" num ret");
+       ok(ret == KNOT_ENOTYPE, "get "" num ret");
 
        // 15. DUMMY
        ret = knot_rrtype_from_string("DUMMY", &num);
-       ok(ret == -1, "get DUMMY num ret");
+       ok(ret == KNOT_ENOTYPE, "get DUMMY num ret");
 
        // 16. TypE33
        ret = knot_rrtype_from_string("TypE33", &num);
-       ok(ret != -1, "get TypE33 num ret");
+       ok(ret == KNOT_EOK, "get TypE33 num ret");
        ok(num == 33, "get TypE33 num");
 
        // 17. TYPE
        ret = knot_rrtype_from_string("TYPE", &num);
-       ok(ret == -1, "get TYPE num ret");
+       ok(ret == KNOT_ENOTYPE, "get TYPE num ret");
 
        // 18. TYPE0
        ret = knot_rrtype_from_string("TYPE0", &num);
-       ok(ret != -1, "get TYPE0 num ret");
+       ok(ret == KNOT_EOK, "get TYPE0 num ret");
        ok(num == 0, "get TYPE0 num");
 
        // 19. TYPE65535
        ret = knot_rrtype_from_string("TYPE65535", &num);
-       ok(ret != -1, "get TYPE65535 num ret");
+       ok(ret == KNOT_EOK, "get TYPE65535 num ret");
        ok(num == 65535, "get TYPE65535 num");
 
        // 20. TYPE65536
        ret = knot_rrtype_from_string("TYPE65536", &num);
-       ok(ret == -1, "get TYPE65536 num ret");
+       ok(ret == KNOT_ENOTYPE, "get TYPE65536 num ret");
 
        // String to class num:
        // 21. In
        ret = knot_rrclass_from_string("In", &num);
-       ok(ret != -1, "get In num ret");
+       ok(ret == KNOT_EOK, "get In num ret");
        ok(num == 1, "get In num");
        ret = knot_rrclass_from_string("Internet", &num);
-       ok(ret != -1, "get In num ret");
+       ok(ret == KNOT_EOK, "get In num ret");
        ok(num == 1, "get In num");
 
        // 22. ANY
        ret = knot_rrclass_from_string("ANY", &num);
-       ok(ret != -1, "get ANY num ret");
+       ok(ret == KNOT_EOK, "get ANY num ret");
        ok(num == 255, "get ANY num");
 
        // 23. ""
        ret = knot_rrclass_from_string("", &num);
-       ok(ret == -1, "get "" num ret");
+       ok(ret == KNOT_ENOCLASS, "get "" num ret");
 
        // 24. DUMMY
        ret = knot_rrclass_from_string("DUMMY", &num);
-       ok(ret == -1, "get DUMMY num ret");
+       ok(ret == KNOT_ENOCLASS, "get DUMMY num ret");
 
        // 25. CLass33
        ret = knot_rrclass_from_string("CLass33", &num);
-       ok(ret != -1, "get CLass33 num ret");
+       ok(ret == KNOT_EOK, "get CLass33 num ret");
        ok(num == 33, "get CLass33 num");
 
        // 26. CLASS
        ret = knot_rrclass_from_string("CLASS", &num);
-       ok(ret == -1, "get CLASS num ret");
+       ok(ret == KNOT_ENOCLASS, "get CLASS num ret");
 
        // 27. CLASS0
        ret = knot_rrclass_from_string("CLASS0", &num);
-       ok(ret != -1, "get CLASS0 num ret");
+       ok(ret == KNOT_EOK, "get CLASS0 num ret");
        ok(num == 0, "get CLASS0 num");
 
        // 28. CLASS65535
        ret = knot_rrclass_from_string("CLASS65535", &num);
-       ok(ret != -1, "get CLASS65535 num ret");
+       ok(ret == KNOT_EOK, "get CLASS65535 num ret");
        ok(num == 65535, "get CLASS65535 num");
 
        // 29. CLASS65536
        ret = knot_rrclass_from_string("CLASS65536", &num);
-       ok(ret == -1, "get CLASS65536 num ret");
+       ok(ret == KNOT_ENOCLASS, "get CLASS65536 num ret");
 
        // Get obsolete descriptor:
        // 30. TYPE0
@@ -242,13 +243,22 @@ int main(int argc, char *argv[])
        ok(descr->block_types[1] == KNOT_RDATA_WF_END,
           "get TYPE38 descriptor 2. item type");
 
-       // knot_rrtype_to_string invalid output buffer size
+       // knot_rrtype_to_string NULL output buffer
        ret = knot_rrtype_to_string(1, NULL, 0);
-       ok(ret == -1, "knot_rrtype_to_string: invalid output buffer size");
+       ok(ret == KNOT_EINVAL, "knot_rrtype_to_string: NULL output buffer");
 
-       // knot_rrclass_to_string invalid output buffer size
+       // knot_rrclass_to_string NULL output buffer
        ret = knot_rrclass_to_string(1, NULL, 0);
-       ok(ret == -1, "knot_rrclass_to_string: invalid output buffer size");
+       ok(ret == KNOT_EINVAL, "knot_rrclass_to_string: NULL output buffer");
+
+       char dummy_buf[1] = { 0 };
+       // knot_rrtype_to_string invalid output buffer size
+       ret = knot_rrtype_to_string(1, dummy_buf, 1);
+       ok(ret == KNOT_ESPACE, "knot_rrtype_to_string: invalid output buffer size");
+
+       // knot_rrclass_to_string invalid output buffer size
+       ret = knot_rrclass_to_string(1, dummy_buf, 1);
+       ok(ret == KNOT_ESPACE, "knot_rrclass_to_string: invalid output buffer size");
 
        // knot_rrtype_is_metatype
        ok(knot_rrtype_is_metatype(0) == 0,
diff --git a/tests/libknot/test_rrset-dump.c b/tests/libknot/test_rrset-dump.c
new file mode 100644 (file)
index 0000000..216fae5
--- /dev/null
@@ -0,0 +1,80 @@
+/*  Copyright (C) CZ.NIC, z.s.p.o. and contributors
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ *  For more information, see <https://www.knot-dns.cz/>
+ */
+
+#include <tap/basic.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/rrset-dump.h"
+
+/* BACKGROUND:
+ *
+ * This unit test could be used for testing rrset-dump with newly implemented RR types.
+ * But so far, the functional test records/load is used for this purpose.
+ * This unit test ought to catch different quirks of knot_rrset_txt_dump,
+ * like re-allocating the given output buffer based on errcode (KNOT_ESPACE).
+ */
+
+const knot_dump_style_t *dump_style = &KNOT_DUMP_STYLE_DEFAULT;
+
+typedef struct {
+       const char *description;
+       knot_rrset_t rrset;
+       const char *expect_out;
+       int expect_ret;
+       size_t buf_sizes[8];
+} rrset_dump_test_case_t;
+
+const char * const rrsig_case_text = "test.               \t1234567890\tRRSIG\tDNSKEY 13 1 1234567890 20251015085855 20251015063355 33658 test. uj40mBZYSg21VqhF7AcU6CTp3dM2k8G/Br8ZP902OCrsDjRq3qPZySxYwmcnbNYeAdVyT1m2zLmKZbYa8cCqRA==\n";
+
+knot_rdata_t rrsig_case_rdata = {
+       88, { 0x00, 0x30,    0x0d,    0x01,    0x49, 0x96, 0x02, 0xd2,    0x68, 0xef, 0x62, 0x4f,   0x68, 0xef, 0x40, 0x53,    0x83, 0x7a,    0x04, 0x74, 0x65, 0x73, 0x74, 0x00,
+             0xba, 0x3e, 0x34, 0x98, 0x16, 0x58, 0x4a, 0x0d, 0xb5, 0x56, 0xa8, 0x45, 0xec, 0x07, 0x14, 0xe8, 0x24, 0xe9, 0xdd, 0xd3, 0x36, 0x93, 0xc1, 0xbf, 0x06, 0xbf, 0x19, 0x3f, 0xdd, 0x36, 0x38, 0x2a,
+             0xec, 0x0e, 0x34, 0x6a, 0xde, 0xa3, 0xd9, 0xc9, 0x2c, 0x58, 0xc2, 0x67, 0x27, 0x6c, 0xd6, 0x1e, 0x01, 0xd5, 0x72, 0x4f, 0x59, 0xb6, 0xcc, 0xb9, 0x8a, 0x65, 0xb6, 0x1a, 0xf1, 0xc0, 0xaa, 0x44
+           }
+};
+
+rrset_dump_test_case_t rrset_dump_test_cases[] = {
+        { "some RRSIG", { (knot_dname_t *)"\x04""test", 1234567890U, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, { 1, 90, &rrsig_case_rdata }, NULL }, rrsig_case_text, KNOT_EOK, { 1, 3, 5, 7, 9, 11 } },
+};
+
+void test_rrset_dump(const char *description, const knot_rrset_t *rrset, const char *expect_out, int expect_ret, size_t initial_buf)
+{
+        size_t bufsize = initial_buf;
+       char *buf = calloc(1, initial_buf);
+       assert(buf != NULL);
+
+       if (expect_ret == KNOT_EOK) {
+               expect_ret = strlen(expect_out);
+       }
+
+       int ret = knot_rrset_txt_dump(rrset, &buf, &bufsize, dump_style);
+       ok(ret == expect_ret, "%s (init buf %zu): return code %d found %d", description, initial_buf, expect_ret, ret);
+
+       if (expect_out != NULL) {
+               ok(strcmp(buf, expect_out) == 0, "%s (init buf %zu): output string '%s' found '%s'", description, initial_buf, expect_out, buf);
+       }
+
+       free(buf);
+}
+
+int main(int argc, char *argv[])
+{
+       plan_lazy();
+
+       for (size_t i = 0; i < sizeof(rrset_dump_test_cases) / sizeof(*rrset_dump_test_cases); i++) {
+               rrset_dump_test_case_t *c = &rrset_dump_test_cases[i];
+               for (size_t j = 0; j < sizeof(c->buf_sizes) / sizeof(*c->buf_sizes); j++) {
+                       if (c->buf_sizes[j] > 0) {
+                               test_rrset_dump(c->description, &c->rrset, c->expect_out, c->expect_ret, c->buf_sizes[j]);
+                       }
+               }
+       }
+
+       return 0;
+}