Skip to content

Commit 490d3fa

Browse files
c8efLukacma
authored andcommitted
[libc] implement inet_aton (llvm#162651)
This patch adds the implementation for `inet_aton` function. Since this function is not explicitly included in POSIX, I have marked it with `llvm_libc_ext`. It is widely available and commonly used, and can also be used to implement `inet_addr`, which is included in POSIX.
1 parent 4b0fa40 commit 490d3fa

File tree

10 files changed

+213
-1
lines changed

10 files changed

+213
-1
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,7 @@ if(LLVM_LIBC_FULL_BUILD)
945945
# arpa/inet.h entrypoints
946946
libc.src.arpa.inet.htonl
947947
libc.src.arpa.inet.htons
948+
libc.src.arpa.inet.inet_aton
948949
libc.src.arpa.inet.ntohl
949950
libc.src.arpa.inet.ntohs
950951

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,7 @@ if(LLVM_LIBC_FULL_BUILD)
10771077
# arpa/inet.h entrypoints
10781078
libc.src.arpa.inet.htonl
10791079
libc.src.arpa.inet.htons
1080+
libc.src.arpa.inet.inet_aton
10801081
libc.src.arpa.inet.ntohl
10811082
libc.src.arpa.inet.ntohs
10821083

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,7 @@ if(LLVM_LIBC_FULL_BUILD)
11131113
# arpa/inet.h entrypoints
11141114
libc.src.arpa.inet.htonl
11151115
libc.src.arpa.inet.htons
1116+
libc.src.arpa.inet.inet_aton
11161117
libc.src.arpa.inet.ntohl
11171118
libc.src.arpa.inet.ntohs
11181119

libc/docs/dev/undefined_behavior.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,10 @@ parsed as normal. For l64a it's unspecified what happens if the input value is
156156
negative. For LLVM-libc, all inputs to l64a are treated as unsigned 32 bit ints.
157157
Additionally, the return of l64a is in a thread-local buffer that's overwritten
158158
on each call.
159+
160+
`inet_aton` and Non-Standard Binary Integers
161+
--------------------------------------------
162+
The current implementation of the `inet_aton` function utilizes the same code
163+
as `strtol` to parse IPv4 numbers-and-dots notations. This approach may permit
164+
the use of binary integers (prefixed with 0b), which is not supported by the
165+
standard.

libc/include/arpa/inet.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
header: arpa/inet.h
22
header_template: inet.h.def
33
macros: []
4-
types: []
4+
types:
5+
- type_name: in_addr
56
enums: []
67
objects: []
78
functions:
@@ -17,6 +18,13 @@ functions:
1718
return_type: uint16_t
1819
arguments:
1920
- type: uint16_t
21+
- name: inet_aton
22+
standards:
23+
- llvm_libc_ext
24+
return_type: int
25+
arguments:
26+
- type: const char *
27+
- type: in_addr *
2028
- name: ntohl
2129
standards:
2230
- POSIX

libc/src/arpa/inet/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ add_entrypoint_object(
2222
libc.src.__support.common
2323
)
2424

25+
add_entrypoint_object(
26+
inet_aton
27+
SRCS
28+
inet_aton.cpp
29+
HDRS
30+
inet_aton.h
31+
DEPENDS
32+
libc.include.arpa_inet
33+
libc.include.llvm-libc-types.in_addr
34+
libc.src.__support.common
35+
libc.src.__support.str_to_integer
36+
)
37+
2538
add_entrypoint_object(
2639
ntohl
2740
SRCS

libc/src/arpa/inet/inet_aton.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===-- Implementation of inet_aton function ------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/arpa/inet/inet_aton.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/endian_internal.h"
12+
#include "src/__support/str_to_integer.h"
13+
14+
namespace LIBC_NAMESPACE_DECL {
15+
16+
LLVM_LIBC_FUNCTION(int, inet_aton, (const char *cp, in_addr *inp)) {
17+
constexpr int IPV4_MAX_DOT_NUM = 3;
18+
unsigned long parts[IPV4_MAX_DOT_NUM + 1] = {0};
19+
int dot_num = 0;
20+
21+
for (; dot_num <= IPV4_MAX_DOT_NUM; ++dot_num) {
22+
auto result = internal::strtointeger<unsigned long>(cp, 0);
23+
parts[dot_num] = result;
24+
25+
if (result.has_error() || result.parsed_len == 0)
26+
return 0;
27+
char next_char = *(cp + result.parsed_len);
28+
if (next_char != '.' && next_char != '\0')
29+
return 0;
30+
else if (next_char == '\0')
31+
break;
32+
else
33+
cp += (result.parsed_len + 1);
34+
}
35+
36+
if (dot_num > IPV4_MAX_DOT_NUM)
37+
return 0;
38+
39+
// converts the Internet host address cp from the IPv4 numbers-and-dots
40+
// notation (a[.b[.c[.d]]]) into binary form (in network byte order)
41+
unsigned long result = 0;
42+
for (int i = 0; i <= dot_num; ++i) {
43+
unsigned long max_part =
44+
i == dot_num ? (0xffffffffUL >> (8 * dot_num)) : 0xffUL;
45+
if (parts[i] > max_part)
46+
return 0;
47+
int shift = i == dot_num ? 0 : 8 * (IPV4_MAX_DOT_NUM - i);
48+
result |= parts[i] << shift;
49+
}
50+
51+
if (inp)
52+
inp->s_addr = Endian::to_big_endian(static_cast<uint32_t>(result));
53+
54+
return 1;
55+
}
56+
57+
} // namespace LIBC_NAMESPACE_DECL

libc/src/arpa/inet/inet_aton.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header of inet_aton ----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_ARPA_INET_INET_ATON_H
10+
#define LLVM_LIBC_SRC_ARPA_INET_INET_ATON_H
11+
12+
#include "include/llvm-libc-types/in_addr.h"
13+
#include "src/__support/macros/config.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
int inet_aton(const char *cp, in_addr *inp);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_ARPA_INET_INET_ATON_H

libc/test/src/arpa/inet/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ add_libc_unittest(
2222
libc.src.arpa.inet.ntohs
2323
)
2424

25+
add_libc_unittest(
26+
inet_aton
27+
SUITE
28+
libc_arpa_inet_unittests
29+
SRCS
30+
inet_aton_test.cpp
31+
DEPENDS
32+
libc.src.arpa.inet.htonl
33+
libc.src.arpa.inet.inet_aton
34+
)
35+
2536
add_libc_unittest(
2637
ntohl
2738
SUITE
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//===-- Unittests for inet_aton -------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/arpa/inet/htonl.h"
10+
#include "src/arpa/inet/inet_aton.h"
11+
#include "test/UnitTest/Test.h"
12+
13+
namespace LIBC_NAMESPACE_DECL {
14+
15+
TEST(LlvmLibcInetAton, ValidTest) {
16+
in_addr a;
17+
18+
// a.b.c.d
19+
a.s_addr = 0;
20+
ASSERT_EQ(1, inet_aton("127.1.2.4", &a));
21+
ASSERT_EQ(htonl(0x7f010204), a.s_addr);
22+
23+
// a.b.c
24+
a.s_addr = 0;
25+
ASSERT_EQ(1, inet_aton("127.1.4", &a));
26+
ASSERT_EQ(htonl(0x7f010004), a.s_addr);
27+
28+
// a.b
29+
a.s_addr = 0;
30+
ASSERT_EQ(1, inet_aton("127.1", &a));
31+
ASSERT_EQ(htonl(0x7f000001), a.s_addr);
32+
33+
// a
34+
a.s_addr = 0;
35+
ASSERT_EQ(1, inet_aton("0x7f000001", &a));
36+
ASSERT_EQ(htonl(0x7f000001), a.s_addr);
37+
38+
// Hex (0x) and mixed-case hex digits.
39+
a.s_addr = 0;
40+
ASSERT_EQ(1, inet_aton("0xFf.0.0.1", &a));
41+
ASSERT_EQ(htonl(0xff000001), a.s_addr);
42+
43+
// Hex (0X) and mixed-case hex digits.
44+
a.s_addr = 0;
45+
ASSERT_EQ(1, inet_aton("0XfF.0.0.1", &a));
46+
ASSERT_EQ(htonl(0xff000001), a.s_addr);
47+
48+
// Octal.
49+
a.s_addr = 0;
50+
ASSERT_EQ(1, inet_aton("0177.0.0.1", &a));
51+
ASSERT_EQ(htonl(0x7f000001), a.s_addr);
52+
53+
a.s_addr = 0;
54+
ASSERT_EQ(1, inet_aton("036", &a));
55+
ASSERT_EQ(htonl(036U), a.s_addr);
56+
}
57+
58+
TEST(LlvmLibcInetAton, InvalidTest) {
59+
ASSERT_EQ(0, inet_aton("", nullptr)); // Empty.
60+
ASSERT_EQ(0, inet_aton("x", nullptr)); // Leading junk.
61+
ASSERT_EQ(0, inet_aton("127.0.0.1x", nullptr)); // Trailing junk.
62+
ASSERT_EQ(0, inet_aton("09.0.0.1", nullptr)); // Invalid octal.
63+
ASSERT_EQ(0, inet_aton("0xg.0.0.1", nullptr)); // Invalid hex.
64+
ASSERT_EQ(0, inet_aton("1.2.3.4.5", nullptr)); // Too many dots.
65+
ASSERT_EQ(0, inet_aton("1.2.3.4.", nullptr)); // Trailing dot.
66+
67+
// Out of range a.b.c.d form.
68+
ASSERT_EQ(0, inet_aton("999.0.0.1", nullptr));
69+
ASSERT_EQ(0, inet_aton("0.999.0.1", nullptr));
70+
ASSERT_EQ(0, inet_aton("0.0.999.1", nullptr));
71+
ASSERT_EQ(0, inet_aton("0.0.0.999", nullptr));
72+
73+
// Out of range a.b.c form.
74+
ASSERT_EQ(0, inet_aton("256.0.0", nullptr));
75+
ASSERT_EQ(0, inet_aton("0.256.0", nullptr));
76+
ASSERT_EQ(0, inet_aton("0.0.0x10000", nullptr));
77+
78+
// Out of range a.b form.
79+
ASSERT_EQ(0, inet_aton("256.0", nullptr));
80+
ASSERT_EQ(0, inet_aton("0.0x1000000", nullptr));
81+
82+
// Out of range a form.
83+
ASSERT_EQ(0, inet_aton("0x100000000", nullptr));
84+
85+
// 64-bit overflow.
86+
ASSERT_EQ(0, inet_aton("0x10000000000000000", nullptr));
87+
88+
// Out of range octal.
89+
ASSERT_EQ(0, inet_aton("0400.0.0.1", nullptr));
90+
}
91+
92+
} // namespace LIBC_NAMESPACE_DECL

0 commit comments

Comments
 (0)