Commit 3b43c9b7b3 for bind

commit 3b43c9b7b340af57522a84e0bf7578dbdb93ee19
Author: Aydın Mercan <aydin@isc.org>
Date:   Thu Apr 30 13:05:42 2026 +0300

    expand isc_parse_* to 64-bit integers and add generic version

    The isc_parse_* family of functions are also now expanded to 64-bit
    integers but they still use the `stroull`/`strtoll` internally with
    better checking and constraints.

    Furthermore, there is now a C11 generic version of the parseint
    functions.

diff --git a/lib/isc/include/isc/parseint.h b/lib/isc/include/isc/parseint.h
index 9565d5f32a..8ed441f313 100644
--- a/lib/isc/include/isc/parseint.h
+++ b/lib/isc/include/isc/parseint.h
@@ -13,7 +13,7 @@

 #pragma once

-#include <inttypes.h>
+#include <stdint.h>

 #include <isc/types.h>

@@ -25,21 +25,27 @@
  ***	Functions
  ***/

-isc_result_t
-isc_parse_uint32(uint32_t *uip, const char *string, int base);
+#define isc_parse_unsigned_number(uip, string, base) \
+	_Generic((uip),                              \
+		uint64_t *: isc_parse_uint64,        \
+		uint32_t *: isc_parse_uint32,        \
+		uint16_t *: isc_parse_uint16,        \
+		uint8_t *: isc_parse_uint8)((uip), (string), (base))

-isc_result_t
-isc_parse_uint16(uint16_t *uip, const char *string, int base);
-
-isc_result_t
-isc_parse_uint8(uint8_t *uip, const char *string, int base);
-/*%<
+#define isc_parse_signed_number(ip, string, base) \
+	_Generic((ip),                            \
+		int64_t *: isc_parse_int64,       \
+		int32_t *: isc_parse_int32,       \
+		int16_t *: isc_parse_int16,       \
+		int8_t *: isc_parse_int8)((ip), (string), (base))
+/**
+ *
  * Parse the null-terminated string 'string' containing a base 'base'
  * integer, storing the result in '*uip'.
- * The base is interpreted
- * as in strtoul().  Unlike strtoul(), leading whitespace, minus or
- * plus signs are not accepted, and all errors (including overflow)
- * are reported uniformly through the return value.
+ *
+ * The base is interpreted as in strtoul().
+ * Unlike strtoul(), leading whitespace is not accepted and all errors
+ * (including overflow) are reported uniformly through the return value.
  *
  * Requires:
  *\li	'string' points to a null-terminated string
@@ -50,3 +56,27 @@ isc_parse_uint8(uint8_t *uip, const char *string, int base);
  *\li	#ISC_R_BADNUMBER   The string is not numeric (in the given base)
  *\li	#ISC_R_RANGE	  The number is not representable as the requested type.
  */
+
+isc_result_t
+isc_parse_uint64(uint64_t *uip, const char *string, int base);
+
+isc_result_t
+isc_parse_uint32(uint32_t *uip, const char *string, int base);
+
+isc_result_t
+isc_parse_uint16(uint16_t *uip, const char *string, int base);
+
+isc_result_t
+isc_parse_uint8(uint8_t *uip, const char *string, int base);
+
+isc_result_t
+isc_parse_int64(int64_t *ip, const char *string, int base);
+
+isc_result_t
+isc_parse_int32(int32_t *ip, const char *string, int base);
+
+isc_result_t
+isc_parse_int16(int16_t *ip, const char *string, int base);
+
+isc_result_t
+isc_parse_int8(int8_t *ip, const char *string, int base);
diff --git a/lib/isc/parseint.c b/lib/isc/parseint.c
index d9887aef1d..1239862208 100644
--- a/lib/isc/parseint.c
+++ b/lib/isc/parseint.c
@@ -19,41 +19,64 @@
 #include <limits.h>
 #include <stdlib.h>

+#include <isc/overflow.h>
 #include <isc/parseint.h>
 #include <isc/result.h>
+#include <isc/util.h>

 isc_result_t
-isc_parse_uint32(uint32_t *uip, const char *string, int base) {
-	unsigned long n;
-	uint32_t r;
-	char *e;
-	if (!isalnum((unsigned char)(string[0]))) {
+isc_parse_uint64(uint64_t *uip, const char *string, int base) {
+	unsigned long long ui;
+	char *endp = NULL;
+
+	REQUIRE(uip != NULL && string != NULL);
+
+	if (isalnum((unsigned char)string[0]) == 0 && string[0] != '+') {
 		return ISC_R_BADNUMBER;
 	}
+
+	if (strtoll(string, NULL, base) < 0) {
+		return ISC_R_BADNUMBER;
+	}
+
 	errno = 0;
-	n = strtoul(string, &e, base);
-	if (*e != '\0') {
+	ui = strtoull(string, &endp, base);
+
+	if (*endp != '\0') {
 		return ISC_R_BADNUMBER;
 	}
-	/*
-	 * Where long is 64 bits we need to convert to 32 bits then test for
-	 * equality.  This is a no-op on 32 bit machines and a good compiler
-	 * will optimise it away.
-	 */
-	r = (uint32_t)n;
-	if ((n == ULONG_MAX && errno == ERANGE) || (n != (unsigned long)r)) {
+
+	switch (errno) {
+	case 0:
+		*uip = ui;
+		return ISC_R_SUCCESS;
+	case EINVAL:
+		return ISC_R_BADNUMBER;
+	case ERANGE:
 		return ISC_R_RANGE;
+	default:
+		UNREACHABLE();
 	}
-	*uip = r;
+}
+
+isc_result_t
+isc_parse_uint32(uint32_t *uip, const char *string, int base) {
+	uint64_t val;
+
+	RETERR(isc_parse_uint64(&val, string, base));
+	if (val > UINT32_MAX) {
+		return ISC_R_RANGE;
+	}
+	*uip = (uint32_t)val;
 	return ISC_R_SUCCESS;
 }

 isc_result_t
 isc_parse_uint16(uint16_t *uip, const char *string, int base) {
-	uint32_t val;
+	uint64_t val;

-	RETERR(isc_parse_uint32(&val, string, base));
-	if (val > 0xFFFF) {
+	RETERR(isc_parse_uint64(&val, string, base));
+	if (val > UINT16_MAX) {
 		return ISC_R_RANGE;
 	}
 	*uip = (uint16_t)val;
@@ -62,12 +85,81 @@ isc_parse_uint16(uint16_t *uip, const char *string, int base) {

 isc_result_t
 isc_parse_uint8(uint8_t *uip, const char *string, int base) {
-	uint32_t val;
+	uint64_t val;

-	RETERR(isc_parse_uint32(&val, string, base));
-	if (val > 0xFF) {
+	RETERR(isc_parse_uint64(&val, string, base));
+	if (val > UINT8_MAX) {
 		return ISC_R_RANGE;
 	}
 	*uip = (uint8_t)val;
 	return ISC_R_SUCCESS;
 }
+
+isc_result_t
+isc_parse_int64(int64_t *ip, const char *string, int base) {
+	long long i;
+	char *endp;
+
+	REQUIRE(ip != NULL && string != NULL);
+
+	if (!isalnum((unsigned char)string[0]) && string[0] != '+' &&
+	    string[0] != '-')
+	{
+		return ISC_R_BADNUMBER;
+	}
+
+	errno = 0;
+	i = strtoll(string, &endp, base);
+
+	if (*endp != '\0') {
+		return ISC_R_BADNUMBER;
+	}
+
+	switch (errno) {
+	case 0:
+		*ip = i;
+		return ISC_R_SUCCESS;
+	case EINVAL:
+		return ISC_R_BADNUMBER;
+	case ERANGE:
+		return ISC_R_RANGE;
+	default:
+		UNREACHABLE();
+	}
+}
+
+isc_result_t
+isc_parse_int32(int32_t *ip, const char *string, int base) {
+	int64_t val;
+
+	RETERR(isc_parse_int64(&val, string, base));
+	if (val > INT32_MAX || val < INT32_MIN) {
+		return ISC_R_RANGE;
+	}
+	*ip = (int32_t)val;
+	return ISC_R_SUCCESS;
+}
+
+isc_result_t
+isc_parse_int16(int16_t *ip, const char *string, int base) {
+	int64_t val;
+
+	RETERR(isc_parse_int64(&val, string, base));
+	if (val > INT16_MAX || val < INT16_MIN) {
+		return ISC_R_RANGE;
+	}
+	*ip = (int16_t)val;
+	return ISC_R_SUCCESS;
+}
+
+isc_result_t
+isc_parse_int8(int8_t *ip, const char *string, int base) {
+	int64_t val;
+
+	RETERR(isc_parse_int64(&val, string, base));
+	if (val > INT8_MAX || val < INT8_MIN) {
+		return ISC_R_RANGE;
+	}
+	*ip = (int8_t)val;
+	return ISC_R_SUCCESS;
+}
diff --git a/tests/isc/parse_test.c b/tests/isc/parse_test.c
index 19e453b76f..4bcc752015 100644
--- a/tests/isc/parse_test.c
+++ b/tests/isc/parse_test.c
@@ -14,13 +14,13 @@
 /*! \file */

 #include <inttypes.h>
-#include <sched.h> /* IWYU pragma: keep */
-#include <setjmp.h>
+#include <sched.h>  /* IWYU pragma: keep */
+#include <setjmp.h> /* IWYU pragma: keep */
 #include <stdarg.h>
 #include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
+#include <stdlib.h> /* IWYU pragma: keep */
+#include <string.h> /* IWYU pragma: keep */
+#include <time.h>   /* IWYU pragma: keep */
 #include <unistd.h>

 #define UNIT_TESTING
@@ -32,25 +32,110 @@

 #include <tests/isc.h>

-/* Test for 32 bit overflow on 64 bit machines in isc_parse_uint32 */
 ISC_RUN_TEST_IMPL(parse_overflow) {
 	isc_result_t result;
-	uint32_t output;
+	uint32_t u32;
+	int32_t i32;

-	result = isc_parse_uint32(&output, "1234567890", 10);
-	assert_int_equal(ISC_R_SUCCESS, result);
-	assert_int_equal(1234567890, output);
+	result = isc_parse_uint32(&u32, "123456789012345", 10);
+	assert_int_equal(result, ISC_R_RANGE);

-	result = isc_parse_uint32(&output, "123456789012345", 10);
-	assert_int_equal(ISC_R_RANGE, result);
+	result = isc_parse_uint32(&u32, "12345678901234567890", 10);
+	assert_int_equal(result, ISC_R_RANGE);

-	result = isc_parse_uint32(&output, "12345678901234567890", 10);
-	assert_int_equal(ISC_R_RANGE, result);
+	result = isc_parse_unsigned_number(&u32, "4294967295", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(u32, UINT32_MAX);
+
+	result = isc_parse_unsigned_number(&u32, "4294967296", 10);
+	assert_int_equal(result, ISC_R_RANGE);
+
+	result = isc_parse_signed_number(&i32, "-2147483648", 0);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i32, INT32_MIN);
+
+	result = isc_parse_signed_number(&i32, "-2147483649", 0);
+	assert_int_equal(result, ISC_R_RANGE);
+}
+
+ISC_RUN_TEST_IMPL(parse_positive) {
+	isc_result_t result;
+	uint32_t u32;
+	uint64_t u64;
+
+	result = isc_parse_uint32(&u32, "1234567890", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(u32, 1234567890);
+
+	result = isc_parse_uint32(&u32, "+1234567890", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(u32, 1234567890);
+
+	result = isc_parse_uint64(&u64, "1234567890", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(u64, 1234567890);
+
+	result = isc_parse_uint64(&u64, "+1234567890", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(u64, 1234567890);
+
+	result = isc_parse_uint64(&u64, "-1234567890", 10);
+	assert_int_equal(result, ISC_R_BADNUMBER);
+
+	result = isc_parse_unsigned_number(&u64, "4294967296", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(u64, 4294967296);
+}
+
+ISC_RUN_TEST_IMPL(parse_negative) {
+	isc_result_t result;
+	int64_t i64;
+
+	result = isc_parse_int64(&i64, "1234567890", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, 1234567890);
+
+	result = isc_parse_int64(&i64, "+1234567890", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, 1234567890);
+
+	result = isc_parse_int64(&i64, "-010", 0);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, -8);
+
+	result = isc_parse_int64(&i64, "-8", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, -8);
+
+	result = isc_parse_int64(&i64, "9223372036854775807", 0);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, INT64_MAX);
+
+	result = isc_parse_int64(&i64, "-0", 5);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, 0);
+
+	result = isc_parse_int64(&i64, "-000000", 17);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, 0);
+
+	result = isc_parse_int64(&i64, "-9223372036854775807", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, INT64_MIN + 1);
+
+	result = isc_parse_int64(&i64, "-9223372036854775808", 10);
+	assert_int_equal(result, ISC_R_SUCCESS);
+	assert_int_equal(i64, INT64_MIN);
+
+	result = isc_parse_int64(&i64, "-9223372036854775809", 10);
+	assert_int_equal(result, ISC_R_RANGE);
 }

 ISC_TEST_LIST_START

 ISC_TEST_ENTRY(parse_overflow)
+ISC_TEST_ENTRY(parse_positive)
+ISC_TEST_ENTRY(parse_negative)

 ISC_TEST_LIST_END