From d72eb6bd9072f4bd71ee3e1dab6e6078b36237f2 Mon Sep 17 00:00:00 2001
From: Daniel Salzman <daniel.salzman@nic.cz>
Date: Fri, 5 Sep 2014 16:33:14 +0200
Subject: [PATCH] dname: add output buffer parameters to knot_dname_to_str

---
 src/common/log.c               |  2 +-
 src/knot/ctl/remote.c          |  2 +-
 src/knot/dnssec/nsec3-chain.c  |  4 +--
 src/knot/nameserver/chaos.c    |  2 +-
 src/knot/updates/ddns.c        | 12 +++----
 src/knot/zone/semantic-check.c |  2 +-
 src/knot/zone/zonefile.c       |  4 +--
 src/libknot/dname.c            | 65 ++++++++++++++++++++++------------
 src/libknot/dname.h            | 24 ++++++++-----
 src/libknot/rrset-dump.c       | 25 ++++++++-----
 src/libknot/rrtype/tsig.c      |  4 +--
 src/libknot/tsig-op.c          |  2 +-
 src/utils/common/exec.c        |  6 ++--
 tests/ztree.c                  |  4 +--
 14 files changed, 97 insertions(+), 61 deletions(-)

diff --git a/src/common/log.c b/src/common/log.c
index a5c8b1c85..7eb73ccff 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -403,7 +403,7 @@ int log_msg_zone(int priority, const knot_dname_t *zone, const char *fmt, ...)
 {
 	va_list args;
 	va_start(args, fmt);
-	char *zone_str = knot_dname_to_str(zone);
+	char *zone_str = knot_dname_to_str_alloc(zone);
 	int result = log_msg_text(priority,
 				  zone_str ? zone_str : LOG_NULL_ZONE_STRING,
 				  fmt, args);
diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c
index fda72b0e3..ea54d5476 100644
--- a/src/knot/ctl/remote.c
+++ b/src/knot/ctl/remote.c
@@ -561,7 +561,7 @@ static void log_command(const char *cmd, const remote_cmdargs_t* args)
 		uint16_t rr_count = rr->rrs.rr_count;
 		for (uint16_t j = 0; j < rr_count; j++) {
 			const knot_dname_t *dn = knot_ns_name(&rr->rrs, j);
-			char *name = knot_dname_to_str(dn);
+			char *name = knot_dname_to_str_alloc(dn);
 
 			int ret = snprintf(params, rest, " %s", name);
 			free(name);
diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c
index c486a1508..685ee2998 100644
--- a/src/knot/dnssec/nsec3-chain.c
+++ b/src/knot/dnssec/nsec3-chain.c
@@ -287,7 +287,7 @@ static zone_node_t *create_nsec3_node(knot_dname_t *owner,
 	if (!new_node) {
 		return NULL;
 	}
-	
+
 	node_set_parent(new_node, apex_node);
 
 	knot_rrset_t nsec3_rrset;
@@ -385,7 +385,7 @@ static int connect_nsec3_nodes(zone_node_t *a, zone_node_t *b,
 	assert(raw_length == knot_nsec3_hash_length(algorithm));
 
 	knot_dname_to_lower(b->owner);
-	uint8_t *b32_hash = (uint8_t *)knot_dname_to_str(b->owner);
+	uint8_t *b32_hash = (uint8_t *)knot_dname_to_str_alloc(b->owner);
 	size_t b32_length = knot_nsec3_hash_b32_length(algorithm);
 	if (!b32_hash) {
 		return KNOT_ENOMEM;
diff --git a/src/knot/nameserver/chaos.c b/src/knot/nameserver/chaos.c
index 418221363..086ca8e7a 100644
--- a/src/knot/nameserver/chaos.c
+++ b/src/knot/nameserver/chaos.c
@@ -26,7 +26,7 @@
  */
 static const char *get_txt_response_string(const knot_dname_t *qname)
 {
-	char *qname_str = knot_dname_to_str(qname);
+	char *qname_str = knot_dname_to_str_alloc(qname);
 	const char *response = NULL;
 
 	/* id.server and hostname.bind should have similar meaning */
diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c
index 9a4158ade..494d00bbf 100644
--- a/src/knot/updates/ddns.c
+++ b/src/knot/updates/ddns.c
@@ -436,7 +436,7 @@ static bool singleton_replaced(changeset_t *changeset,
 	if (!should_replace(rr)) {
 		return false;
 	}
-	
+
 	zone_node_t *n = zone_contents_find_node_for_rr(changeset->add, rr);
 	if (n == NULL) {
 		return false;
@@ -446,12 +446,12 @@ static bool singleton_replaced(changeset_t *changeset,
 	if (rrs == NULL) {
 		return false;
 	}
-	
+
 	// Replace singleton RR.
 	knot_rdataset_clear(rrs, NULL);
 	node_remove_rdataset(n, rr->type);
 	node_add_rrset(n, rr, NULL);
-	
+
 	return true;
 }
 
@@ -544,7 +544,7 @@ static int process_add_nsec3param(const zone_node_t *node,
 {
 	if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) {
 		// Ignore non-apex additions
-		char *owner = knot_dname_to_str(rr->owner);
+		char *owner = knot_dname_to_str_alloc(rr->owner);
 		log_warning("DDNS, refusing to add NSEC3PARAM to non-apex "
 		            "node '%s'", owner);
 		free(owner);
@@ -555,7 +555,7 @@ static int process_add_nsec3param(const zone_node_t *node,
 		return add_rr_to_chgset(rr, changeset, NULL);
 	}
 
-	char *owner = knot_dname_to_str(rr->owner);
+	char *owner = knot_dname_to_str_alloc(rr->owner);
 	log_warning("DDNS, refusing to add second NSEC3PARAM to node '%s'", owner);
 	free(owner);
 
@@ -918,7 +918,7 @@ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query,
 		}
 		return KNOT_EINVAL;
 	}
-	
+
 	changeset_t *changeset = update->change;
 
 	if (changeset->soa_from == NULL) {
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
index be0821ae1..cc06004a5 100644
--- a/src/knot/zone/semantic-check.c
+++ b/src/knot/zone/semantic-check.c
@@ -181,7 +181,7 @@ static void log_error_from_node(err_handler_t *handler,
 
 	handler->error_count++;
 
-	char *name = knot_dname_to_str(node->owner);
+	char *name = knot_dname_to_str_alloc(node->owner);
 	const char *errmsg = error_messages[-error];
 
 	log_zone_warning(zone_name, "semantic check, node '%s' (%s%s%s)",
diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c
index 70ee8e988..9c26863ad 100644
--- a/src/knot/zone/zonefile.c
+++ b/src/knot/zone/zonefile.c
@@ -63,7 +63,7 @@ static bool handle_err(zcreator_t *zc, const zone_node_t *node,
                        const knot_rrset_t *rr, int ret, bool master)
 {
 	const knot_dname_t *zname = zc->z->apex->owner;
-	char *rrname = rr ? knot_dname_to_str(rr->owner) : NULL;
+	char *rrname = rr ? knot_dname_to_str_alloc(rr->owner) : NULL;
 	if (ret == KNOT_EOUTOFZONE) {
 		WARNING(zname, "ignoring out-of-zone data, owner '%s'",
 		        rrname ? rrname : "unknown");
@@ -164,7 +164,7 @@ static void scanner_process(zs_scanner_t *scanner)
 	knot_rrset_init(&rr, owner, scanner->r_type, scanner->r_class);
 	int ret = add_rdata_to_rr(&rr, scanner);
 	if (ret != KNOT_EOK) {
-		char *rr_name = knot_dname_to_str(rr.owner);
+		char *rr_name = knot_dname_to_str_alloc(rr.owner);
 		const knot_dname_t *zname = zc->z->apex->owner;
 		ERROR(zname, "failed to add RDATA, file '%s', line %"PRIu64", owner '%s'",
 		      scanner->file.name, scanner->line_counter, rr_name);
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
index e5ad1b849..2df6c5ce3 100644
--- a/src/libknot/dname.c
+++ b/src/libknot/dname.c
@@ -211,17 +211,24 @@ int knot_dname_unpack(uint8_t* dst, const knot_dname_t *src,
 
 /*----------------------------------------------------------------------------*/
 
-char *knot_dname_to_str(const knot_dname_t *name)
+char *knot_dname_to_str(char *dst, const knot_dname_t *name, size_t maxlen)
 {
-	if (name == NULL)
+	if (name == NULL) {
 		return NULL;
+	}
 
-	/*! \todo Supply packet. */
-	/*! \todo Write to static buffer? */
-	// Allocate space for dname string + 1 char termination.
 	int dname_size = knot_dname_size(name);
-	size_t alloc_size = dname_size + 1;
-	char *res = malloc(alloc_size);
+	if (dname_size <= 0) {
+		return NULL;
+	}
+
+	// Check the size for len(dname) + 1 char termination.
+	size_t alloc_size = (dst == NULL) ? dname_size + 1 : maxlen;
+	if (alloc_size < dname_size + 1) {
+		return NULL;
+	}
+
+	char *res = (dst == NULL) ? malloc(alloc_size) : dst;
 	if (res == NULL) {
 		return NULL;
 	}
@@ -253,33 +260,47 @@ char *knot_dname_to_str(const knot_dname_t *name)
 			 * encoded in \ddd notation.
 			 */
 
-			// Increase output size for \x format.
-			alloc_size += 1;
-			char *extended = realloc(res, alloc_size);
-			if (extended == NULL) {
-				free(res);
-				return NULL;
+			if (dst != NULL) {
+				if (maxlen <= str_len + 2) {
+					return NULL;
+				}
+			} else {
+				// Extend output buffer for \x format.
+				alloc_size += 1;
+				char *extended = realloc(res, alloc_size);
+				if (extended == NULL) {
+					free(res);
+					return NULL;
+				}
+				res = extended;
 			}
-			res = extended;
 
 			// Write encoded character.
 			res[str_len++] = '\\';
 			res[str_len++] = c;
 		} else {
-			// Increase output size for \DDD format.
-			alloc_size += 3;
-			char *extended = realloc(res, alloc_size);
-			if (extended == NULL) {
-				free(res);
-				return NULL;
+			if (dst != NULL) {
+				if (maxlen <= str_len + 4) {
+					return NULL;
+				}
+			} else {
+				// Extend output buffer for \DDD format.
+				alloc_size += 3;
+				char *extended = realloc(res, alloc_size);
+				if (extended == NULL) {
+					free(res);
+					return NULL;
+				}
+				res = extended;
 			}
-			res = extended;
 
 			// Write encoded character.
 			int ret = snprintf(res + str_len, alloc_size - str_len,
 			                   "\\%03u", c);
 			if (ret <= 0 || ret >= alloc_size - str_len) {
-				free(res);
+				if (dst == NULL) {
+					free(res);
+				}
 				return NULL;
 			}
 
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
index 58bfc174e..bafe66847 100644
--- a/src/libknot/dname.h
+++ b/src/libknot/dname.h
@@ -118,18 +118,26 @@ int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src,
                       size_t maxlen, const uint8_t *pkt);
 
 /*!
- * \brief Converts the given domain name to string representation.
+ * \brief Converts the given domain name to its string representation.
  *
- * \note Allocates new memory, remember to free it.
+ * \note Output buffer is allocated automatically if dst is NULL.
  *
- * \todo The function doesn't process escaped characters like \DDD or \X.
+ * \param dst    Output buffer.
+ * \param name   Domain name to be converted.
+ * \param maxlen Output buffer length.
  *
- * \param name Domain name to be converted.
- *
- * \return 0-terminated string representing the given domain name in
- *         presentation format.
+ * \return 0-terminated string if successful, NULL if error.
+ */
+char *knot_dname_to_str(char *dst, const knot_dname_t *name, size_t maxlen);
+
+/*!
+ * \brief This function is a shortcut for \ref knot_dname_to_str with
+ *        no output buffer parameters.
  */
-char *knot_dname_to_str(const knot_dname_t *name);
+static inline char *knot_dname_to_str_alloc(const knot_dname_t *name)
+{
+	return knot_dname_to_str(NULL, name, 0);
+}
 
 /*!
  * \brief Creates a dname structure from domain name given in presentation
diff --git a/src/libknot/rrset-dump.c b/src/libknot/rrset-dump.c
index 75f3590e2..647ea6b95 100644
--- a/src/libknot/rrset-dump.c
+++ b/src/libknot/rrset-dump.c
@@ -804,16 +804,23 @@ static void wire_dname_to_str(rrset_dump_params_t *p)
 	}
 
 	// Write dname string.
-	char *dname_str = knot_dname_to_str(p->in);
-	if (p->style->ascii_to_idn != NULL) {
+	if (p->style->ascii_to_idn == NULL) {
+		char *dname_str = knot_dname_to_str(p->out, p->in, p->out_max);
+		if (dname_str == NULL) {
+			return;
+		}
+		out_len = strlen(dname_str);
+	} else {
+		char *dname_str = knot_dname_to_str_alloc(p->in);
 		p->style->ascii_to_idn(&dname_str);
+
+		int ret = snprintf(p->out, p->out_max, "%s", dname_str);
+		free(dname_str);
+		if (ret < 0 || (size_t)ret >= p->out_max) {
+			return;
+		}
+		out_len = ret;
 	}
-	int ret = snprintf(p->out, p->out_max, "%s", dname_str);
-	free(dname_str);
-	if (ret < 0 || (size_t)ret >= p->out_max) {
-		return;
-	}
-	out_len = ret;
 
 	// Fill in output.
 	p->in += in_len;
@@ -1831,7 +1838,7 @@ int knot_rrset_txt_dump_header(const knot_rrset_t      *rrset,
 	int    ret;
 
 	// Dump rrset owner.
-	char *name = knot_dname_to_str(rrset->owner);
+	char *name = knot_dname_to_str_alloc(rrset->owner);
 	if (style->ascii_to_idn != NULL) {
 		style->ascii_to_idn(&name);
 	}
diff --git a/src/libknot/rrtype/tsig.c b/src/libknot/rrtype/tsig.c
index 814883d48..7722d3a1d 100644
--- a/src/libknot/rrtype/tsig.c
+++ b/src/libknot/rrtype/tsig.c
@@ -241,7 +241,7 @@ knot_tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig)
 	}
 
 	/* Convert alg name to string. */
-	char *name = knot_dname_to_str(alg_name);
+	char *name = knot_dname_to_str_alloc(alg_name);
 	if (!name) {
 		dbg_tsig("TSIG: rdata: cannot convert alg name.\n");
 		return KNOT_TSIG_ALG_NULL;
@@ -336,7 +336,7 @@ int tsig_alg_from_name(const knot_dname_t *alg_name)
 		return 0;
 	}
 
-	char *name = knot_dname_to_str(alg_name);
+	char *name = knot_dname_to_str_alloc(alg_name);
 	if (!name) {
 		return 0;
 	}
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
index 43c6b6c00..2d91e2db7 100644
--- a/src/libknot/tsig-op.c
+++ b/src/libknot/tsig-op.c
@@ -69,7 +69,7 @@ static int knot_tsig_check_key(const knot_rrset_t *tsig_rr,
 		return KNOT_EMALF;
 	}
 
-	char *name = knot_dname_to_str(tsig_name);
+	char *name = knot_dname_to_str_alloc(tsig_name);
 	if (!name) {
 		return KNOT_EMALF;
 	}
diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c
index b4ff5fa5f..d516518af 100644
--- a/src/utils/common/exec.c
+++ b/src/utils/common/exec.c
@@ -376,7 +376,7 @@ static void print_section_host(const knot_rrset_t *rrsets,
 		char                type[32] = "NULL";
 		char                *owner;
 
-		owner = knot_dname_to_str(rrset->owner);
+		owner = knot_dname_to_str_alloc(rrset->owner);
 		if (style->style.ascii_to_idn != NULL) {
 			style->style.ascii_to_idn(&owner);
 		}
@@ -432,7 +432,7 @@ static void print_error_host(const uint8_t    code,
 
 	knot_lookup_table_t *rcode;
 
-	owner = knot_dname_to_str(knot_pkt_qname(packet));
+	owner = knot_dname_to_str_alloc(knot_pkt_qname(packet));
 	if (style->style.ascii_to_idn != NULL) {
 		style->style.ascii_to_idn(&owner);
 	}
@@ -486,7 +486,7 @@ void print_header_xfr(const knot_pkt_t *packet, const style_t  *style)
 	}
 
 	if (style->show_header) {
-		char *owner = knot_dname_to_str(knot_pkt_qname(packet));
+		char *owner = knot_dname_to_str_alloc(knot_pkt_qname(packet));
 		if (style->style.ascii_to_idn != NULL) {
 			style->style.ascii_to_idn(&owner);
 		}
diff --git a/tests/ztree.c b/tests/ztree.c
index 4d24d9cd5..09523665e 100644
--- a/tests/ztree.c
+++ b/tests/ztree.c
@@ -55,8 +55,8 @@ static int ztree_iter_data(zone_node_t **node, void *data)
 	int result = KNOT_EOK;
 	if (owner != ORDER[*i]) {
 		result = KNOT_ERROR;
-		char *exp_s = knot_dname_to_str(ORDER[*i]);
-		char *owner_s = knot_dname_to_str(owner);
+		char *exp_s = knot_dname_to_str_alloc(ORDER[*i]);
+		char *owner_s = knot_dname_to_str_alloc(owner);
 		diag("ztree: at index: %u expected '%s' got '%s'\n", *i, exp_s, owner_s);
 		free(exp_s);
 		free(owner_s);
-- 
GitLab