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