[libc-commits] [libc] c59178d - [libc] Fix inet_aton (#198791)
via libc-commits
libc-commits at lists.llvm.org
Mon May 25 04:10:07 PDT 2026
Author: Pavel Labath
Date: 2026-05-25T13:10:03+02:00
New Revision: c59178d6037f582c7e8fb0141c7a16cd8d5d8d5e
URL: https://github.com/llvm/llvm-project/commit/c59178d6037f582c7e8fb0141c7a16cd8d5d8d5e
DIFF: https://github.com/llvm/llvm-project/commit/c59178d6037f582c7e8fb0141c7a16cd8d5d8d5e.diff
LOG: [libc] Fix inet_aton (#198791)
The main (in terms of LOC) change is moving the implementation to an
internal function in order to avoid the inet_addr->inet_aton dependency.
I also fix a bug where we (mistakenly) accepted whitespace and signs
inside the address. I also match the glibc implementation in ignoring
the data after the first whitespace.
Added:
libc/src/__support/net/CMakeLists.txt
libc/src/__support/net/address.cpp
libc/src/__support/net/address.h
Modified:
libc/src/__support/CMakeLists.txt
libc/src/arpa/inet/CMakeLists.txt
libc/src/arpa/inet/inet_addr.cpp
libc/src/arpa/inet/inet_aton.cpp
libc/test/src/arpa/inet/inet_aton_test.cpp
Removed:
################################################################################
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index ada489046ef9e..1f77fc02380df 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -453,6 +453,7 @@ add_subdirectory(OSUtil)
add_subdirectory(StringUtil)
add_subdirectory(GPU)
add_subdirectory(RPC)
+add_subdirectory(net)
# Thread support is used by other "File". So, we add the "threads"
# before "File".
diff --git a/libc/src/__support/net/CMakeLists.txt b/libc/src/__support/net/CMakeLists.txt
new file mode 100644
index 0000000000000..84e9f1bebf230
--- /dev/null
+++ b/libc/src/__support/net/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_object_library(
+ address
+ HDRS
+ address.h
+ SRCS
+ address.cpp
+ DEPENDS
+ libc.hdr.types.in_addr_t
+ libc.src.__support.common
+ libc.src.__support.ctype_utils
+ libc.src.__support.CPP.optional
+ libc.src.__support.str_to_integer
+)
diff --git a/libc/src/__support/net/address.cpp b/libc/src/__support/net/address.cpp
new file mode 100644
index 0000000000000..1cb0924b07a99
--- /dev/null
+++ b/libc/src/__support/net/address.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements helper functions for parsing network addresses.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/net/address.h"
+#include "src/__support/common.h"
+#include "src/__support/ctype_utils.h"
+#include "src/__support/endian_internal.h"
+#include "src/__support/str_to_integer.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace net {
+
+cpp::optional<in_addr_t> inet_addr(const char *cp) {
+ constexpr int IPV4_MAX_DOT_NUM = 3;
+ in_addr_t parts[IPV4_MAX_DOT_NUM + 1] = {0};
+ int dot_num = 0;
+
+ for (; dot_num <= IPV4_MAX_DOT_NUM; ++dot_num) {
+ // strtointeger skips leading whitespace signs (1.+2.-3. 4), but we don't
+ // want that, so we explicitly check that the first character is a digit.
+ if (!internal::isdigit(*cp))
+ return cpp::nullopt;
+
+ auto result = internal::strtointeger<in_addr_t>(cp, 0);
+ parts[dot_num] = result;
+
+ if (result.has_error() || result.parsed_len == 0)
+ return cpp::nullopt;
+ cp += result.parsed_len;
+ if (*cp == '\0' || internal::isspace(*cp))
+ break;
+ if (*cp != '.')
+ return cpp::nullopt;
+ ++cp;
+ }
+
+ if (dot_num > IPV4_MAX_DOT_NUM)
+ return cpp::nullopt;
+
+ // converts the Internet host address cp from the IPv4 numbers-and-dots
+ // notation (a[.b[.c[.d]]]) into binary form (in network byte order)
+ in_addr_t result = 0;
+ for (int i = 0; i <= dot_num; ++i) {
+ in_addr_t max_part = i == dot_num ? (0xffffffffu >> (8 * dot_num)) : 0xffu;
+ if (parts[i] > max_part)
+ return cpp::nullopt;
+ int shift = i == dot_num ? 0 : 8 * (IPV4_MAX_DOT_NUM - i);
+ result |= parts[i] << shift;
+ }
+
+ return Endian::to_big_endian(result);
+}
+
+} // namespace net
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/net/address.h b/libc/src/__support/net/address.h
new file mode 100644
index 0000000000000..925b8d379f703
--- /dev/null
+++ b/libc/src/__support/net/address.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains helper functions for parsing and manipulating network
+/// addresses.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_NET_ADDRESS_H
+#define LLVM_LIBC_SRC___SUPPORT_NET_ADDRESS_H
+
+#include "hdr/types/in_addr_t.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace net {
+
+cpp::optional<in_addr_t> inet_addr(const char *cp);
+
+} // namespace net
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_NET_ADDRESS_H
diff --git a/libc/src/arpa/inet/CMakeLists.txt b/libc/src/arpa/inet/CMakeLists.txt
index c4019dd4bbd29..da041fc314b43 100644
--- a/libc/src/arpa/inet/CMakeLists.txt
+++ b/libc/src/arpa/inet/CMakeLists.txt
@@ -32,7 +32,8 @@ add_entrypoint_object(
libc.include.arpa_inet
libc.hdr.types.struct_in_addr
libc.src.__support.common
- libc.src.__support.str_to_integer
+ libc.src.__support.CPP.optional
+ libc.src.__support.net.address
)
add_entrypoint_object(
@@ -44,10 +45,10 @@ add_entrypoint_object(
DEPENDS
libc.include.arpa_inet
libc.hdr.netinet_in_macros
- libc.hdr.types.struct_in_addr
libc.hdr.types.in_addr_t
libc.src.__support.common
- libc.src.arpa.inet.inet_aton
+ libc.src.__support.CPP.optional
+ libc.src.__support.net.address
)
add_entrypoint_object(
diff --git a/libc/src/arpa/inet/inet_addr.cpp b/libc/src/arpa/inet/inet_addr.cpp
index 03757a1e8fb29..343ccf2057dc5 100644
--- a/libc/src/arpa/inet/inet_addr.cpp
+++ b/libc/src/arpa/inet/inet_addr.cpp
@@ -9,15 +9,17 @@
#include "src/arpa/inet/inet_addr.h"
#include "hdr/netinet_in_macros.h"
#include "hdr/types/in_addr_t.h"
-#include "hdr/types/struct_in_addr.h"
+#include "src/__support/CPP/optional.h"
#include "src/__support/common.h"
-#include "src/arpa/inet/inet_aton.h"
+#include "src/__support/net/address.h"
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(in_addr_t, inet_addr, (const char *cp)) {
- struct in_addr addr;
- return inet_aton(cp, &addr) ? addr.s_addr : INADDR_NONE;
+ cpp::optional<in_addr_t> addr = net::inet_addr(cp);
+ if (!addr.has_value())
+ return INADDR_NONE;
+ return addr.value();
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/arpa/inet/inet_aton.cpp b/libc/src/arpa/inet/inet_aton.cpp
index c7f002ec393aa..3dfe9ba994d0a 100644
--- a/libc/src/arpa/inet/inet_aton.cpp
+++ b/libc/src/arpa/inet/inet_aton.cpp
@@ -7,50 +7,18 @@
//===----------------------------------------------------------------------===//
#include "src/arpa/inet/inet_aton.h"
+#include "src/__support/CPP/optional.h"
#include "src/__support/common.h"
-#include "src/__support/endian_internal.h"
-#include "src/__support/str_to_integer.h"
+#include "src/__support/net/address.h"
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, inet_aton, (const char *cp, struct in_addr *inp)) {
- constexpr int IPV4_MAX_DOT_NUM = 3;
- unsigned long parts[IPV4_MAX_DOT_NUM + 1] = {0};
- int dot_num = 0;
-
- for (; dot_num <= IPV4_MAX_DOT_NUM; ++dot_num) {
- auto result = internal::strtointeger<unsigned long>(cp, 0);
- parts[dot_num] = result;
-
- if (result.has_error() || result.parsed_len == 0)
- return 0;
- char next_char = *(cp + result.parsed_len);
- if (next_char != '.' && next_char != '\0')
- return 0;
- else if (next_char == '\0')
- break;
- else
- cp += (result.parsed_len + 1);
- }
-
- if (dot_num > IPV4_MAX_DOT_NUM)
+ cpp::optional<in_addr_t> addr = net::inet_addr(cp);
+ if (!addr.has_value())
return 0;
-
- // converts the Internet host address cp from the IPv4 numbers-and-dots
- // notation (a[.b[.c[.d]]]) into binary form (in network byte order)
- unsigned long result = 0;
- for (int i = 0; i <= dot_num; ++i) {
- unsigned long max_part =
- i == dot_num ? (0xffffffffUL >> (8 * dot_num)) : 0xffUL;
- if (parts[i] > max_part)
- return 0;
- int shift = i == dot_num ? 0 : 8 * (IPV4_MAX_DOT_NUM - i);
- result |= parts[i] << shift;
- }
-
if (inp)
- inp->s_addr = Endian::to_big_endian(static_cast<uint32_t>(result));
-
+ inp->s_addr = addr.value();
return 1;
}
diff --git a/libc/test/src/arpa/inet/inet_aton_test.cpp b/libc/test/src/arpa/inet/inet_aton_test.cpp
index 7757ba7ad4d07..dd7ea26374584 100644
--- a/libc/test/src/arpa/inet/inet_aton_test.cpp
+++ b/libc/test/src/arpa/inet/inet_aton_test.cpp
@@ -53,12 +53,30 @@ TEST(LlvmLibcInetAton, ValidTest) {
a.s_addr = 0;
ASSERT_EQ(1, inet_aton("036", &a));
ASSERT_EQ(htonl(036U), a.s_addr);
+
+ // Trailing space.
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("1 ", &a));
+ ASSERT_EQ(htonl(1), a.s_addr);
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("1.2 ", &a));
+ ASSERT_EQ(htonl(0x01000002), a.s_addr);
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("1.2.3 ", &a));
+ ASSERT_EQ(htonl(0x01020003), a.s_addr);
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("1.2.3.4 ", &a));
+ ASSERT_EQ(htonl(0x01020304), a.s_addr);
+
+ // We can check for validity with a nullptr argument.
+ ASSERT_EQ(1, inet_aton("127.1.2.4", nullptr));
}
TEST(LlvmLibcInetAton, InvalidTest) {
ASSERT_EQ(0, inet_aton("", nullptr)); // Empty.
ASSERT_EQ(0, inet_aton("x", nullptr)); // Leading junk.
ASSERT_EQ(0, inet_aton("127.0.0.1x", nullptr)); // Trailing junk.
+ ASSERT_EQ(0, inet_aton("127.0.1x", nullptr)); // Trailing junk.
ASSERT_EQ(0, inet_aton("09.0.0.1", nullptr)); // Invalid octal.
ASSERT_EQ(0, inet_aton("0xg.0.0.1", nullptr)); // Invalid hex.
ASSERT_EQ(0, inet_aton("1.2.3.4.5", nullptr)); // Too many dots.
@@ -87,6 +105,13 @@ TEST(LlvmLibcInetAton, InvalidTest) {
// Out of range octal.
ASSERT_EQ(0, inet_aton("0400.0.0.1", nullptr));
+
+ // Signs and whitespace.
+ ASSERT_EQ(0, inet_aton(" 127.0.0.1", nullptr));
+ ASSERT_EQ(0, inet_aton("+127.0.0.1", nullptr));
+ ASSERT_EQ(0, inet_aton("-127.0.0.1", nullptr));
+ ASSERT_EQ(0, inet_aton("127. 0.0.1", nullptr));
+ ASSERT_EQ(0, inet_aton("127.+0.0.1", nullptr));
}
} // namespace LIBC_NAMESPACE_DECL
More information about the libc-commits
mailing list