]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
dns_name_totext() can now resize dynamic buffers
authorEvan Hunt <each@isc.org>
Wed, 26 Nov 2025 03:48:02 +0000 (19:48 -0800)
committerEvan Hunt <each@isc.org>
Tue, 9 Dec 2025 20:26:30 +0000 (12:26 -0800)
When dns_name_totext() is called with a dynamically allocated
target buffer which is too small for the name, it will resize
the buffer instead of returning ISC_R_NOSPACE.

lib/dns/name.c

index c9b3709c1e6b0f6b17cccda713fa72568271e030..000a33977e8caadb55c0540760987c51ef2ef907 100644 (file)
@@ -974,15 +974,14 @@ dns_name_fromtext(dns_name_t *name, isc_buffer_t *source,
 isc_result_t
 dns_name_totext(const dns_name_t *name, unsigned int options,
                isc_buffer_t *target) {
-       unsigned char *ndata;
-       char *tdata;
-       unsigned int nlen, tlen;
-       unsigned char c;
-       unsigned int trem, count;
+       isc_result_t result;
+       unsigned char *ndata = NULL;
+       unsigned int nlen;
        unsigned int labels;
        bool saw_root = false;
        unsigned int oused;
        bool omit_final_dot = ((options & DNS_NAME_OMITFINALDOT) != 0);
+       bool first = true;
 
        /*
         * This function assumes the name is in proper uncompressed
@@ -996,30 +995,14 @@ dns_name_totext(const dns_name_t *name, unsigned int options,
        ndata = name->ndata;
        nlen = name->length;
        labels = dns_name_countlabels(name);
-       tdata = isc_buffer_used(target);
-       tlen = isc_buffer_availablelength(target);
-
-       trem = tlen;
 
        if (labels == 0 && nlen == 0) {
                /*
                 * Special handling for an empty name.
                 */
-               if (trem == 0) {
-                       return ISC_R_NOSPACE;
-               }
-
-               /*
-                * The names of these booleans are misleading in this case.
-                * This empty name is not necessarily from the root node of
-                * the DNS root zone, nor is a final dot going to be included.
-                * They need to be set this way, though, to keep the "@"
-                * from being trounced.
-                */
-               saw_root = true;
-               omit_final_dot = false;
-               *tdata++ = '@';
-               trem--;
+               omit_final_dot = true;
+               CHECK(isc_buffer_reserve(target, 1));
+               isc_buffer_putuint8(target, '@');
 
                /*
                 * Skip the while() loop.
@@ -1029,14 +1012,8 @@ dns_name_totext(const dns_name_t *name, unsigned int options,
                /*
                 * Special handling for the root label.
                 */
-               if (trem == 0) {
-                       return ISC_R_NOSPACE;
-               }
-
                saw_root = true;
                omit_final_dot = false;
-               *tdata++ = '.';
-               trem--;
 
                /*
                 * Skip the while() loop.
@@ -1044,17 +1021,27 @@ dns_name_totext(const dns_name_t *name, unsigned int options,
                nlen = 0;
        }
 
-       while (labels > 0 && nlen > 0 && trem > 0) {
+       while (labels > 0 && nlen > 0) {
+               unsigned int count = *ndata++;
                labels--;
-               count = *ndata++;
                nlen--;
                if (count == 0) {
                        saw_root = true;
                        break;
+               } else if (!first) {
+                       CHECK(isc_buffer_reserve(target, 1));
+                       isc_buffer_putuint8(target, '.');
                }
+               first = false;
+
                if (count <= DNS_NAME_LABELLEN) {
+                       unsigned char c;
+
                        INSIST(nlen >= count);
+
                        while (count > 0) {
+                               uint32_t value;
+
                                c = *ndata;
                                switch (c) {
                                /* Special modifiers in zone files. */
@@ -1071,36 +1058,33 @@ dns_name_totext(const dns_name_t *name, unsigned int options,
                                case 0x2E: /* '.' */
                                case 0x3B: /* ';' */
                                case 0x5C: /* '\\' */
-                                       if (trem < 2) {
-                                               return ISC_R_NOSPACE;
-                                       }
-                                       *tdata++ = '\\';
-                                       *tdata++ = c;
+                                       value = '\\' << 8 | c;
+                                       CHECK(isc_buffer_reserve(target, 2));
+                                       isc_buffer_putuint16(target, value);
                                        ndata++;
-                                       trem -= 2;
                                        nlen--;
                                        break;
                                no_escape:
                                default:
                                        if (c > 0x20 && c < 0x7f) {
-                                               if (trem == 0) {
-                                                       return ISC_R_NOSPACE;
-                                               }
-                                               *tdata++ = c;
+                                               CHECK(isc_buffer_reserve(target,
+                                                                        1));
+                                               isc_buffer_putuint8(target, c);
                                                ndata++;
-                                               trem--;
                                                nlen--;
                                        } else {
-                                               if (trem < 4) {
-                                                       return ISC_R_NOSPACE;
-                                               }
-                                               *tdata++ = 0x5c;
-                                               *tdata++ = 0x30 +
-                                                          ((c / 100) % 10);
-                                               *tdata++ = 0x30 +
-                                                          ((c / 10) % 10);
-                                               *tdata++ = 0x30 + (c % 10);
-                                               trem -= 4;
+                                               value = 0x5c << 24;
+                                               value |= (0x30 +
+                                                         ((c / 100) % 10))
+                                                        << 16;
+                                               value |=
+                                                       (0x30 + ((c / 10) % 10))
+                                                       << 8;
+                                               value |= 0x30 + (c % 10);
+                                               CHECK(isc_buffer_reserve(target,
+                                                                        4));
+                                               isc_buffer_putuint32(target,
+                                                                    value);
                                                ndata++;
                                                nlen--;
                                        }
@@ -1111,38 +1095,26 @@ dns_name_totext(const dns_name_t *name, unsigned int options,
                        FATAL_ERROR("Unexpected label type %02x", count);
                        UNREACHABLE();
                }
-
-               /*
-                * The following assumes names are absolute.  If not, we
-                * fix things up later.  Note that this means that in some
-                * cases one more byte of text buffer is required than is
-                * needed in the final output.
-                */
-               if (trem == 0) {
-                       return ISC_R_NOSPACE;
-               }
-               *tdata++ = '.';
-               trem--;
        }
 
-       if (nlen != 0 && trem == 0) {
-               return ISC_R_NOSPACE;
+       if (saw_root && !omit_final_dot) {
+               CHECK(isc_buffer_reserve(target, 1));
+               isc_buffer_putuint8(target, '.');
        }
 
-       if (!saw_root || omit_final_dot) {
-               trem++;
-               tdata--;
+       if (isc_buffer_availablelength(target) > 1) {
+               uint8_t *p = isc_buffer_used(target);
+               *p = 0;
        }
-       if (trem > 0) {
-               *tdata = 0;
-       }
-       isc_buffer_add(target, tlen - trem);
 
        if (totext_filter_proc != NULL) {
                return (totext_filter_proc)(target, oused);
        }
 
        return ISC_R_SUCCESS;
+
+cleanup:
+       return result;
 }
 
 isc_result_t