Skip to content

Commit 70b8fcd

Browse files
authored
Move is ipv4/ipv6 functions from Aws-c-sdkutils to Aws-c-common (#1105)
1 parent 86e9e27 commit 70b8fcd

File tree

4 files changed

+231
-0
lines changed

4 files changed

+231
-0
lines changed

include/aws/common/host_utils.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef AWS_COMMON_HOST_UTILS_H
2+
#define AWS_COMMON_HOST_UTILS_H
3+
/**
4+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
* SPDX-License-Identifier: Apache-2.0.
6+
*/
7+
#include <aws/common/common.h>
8+
9+
struct aws_byte_cursor;
10+
11+
AWS_PUSH_SANE_WARNING_LEVEL
12+
AWS_EXTERN_C_BEGIN
13+
14+
/*
15+
* Determine whether host cursor is IPv4 string.
16+
*/
17+
AWS_COMMON_API bool aws_host_utils_is_ipv4(struct aws_byte_cursor host);
18+
19+
/*
20+
* Determine whether host cursor is IPv6 string.
21+
* Supports checking for uri encoded strings and scoped literals.
22+
*/
23+
AWS_COMMON_API bool aws_host_utils_is_ipv6(struct aws_byte_cursor host, bool is_uri_encoded);
24+
25+
AWS_EXTERN_C_END
26+
AWS_POP_SANE_WARNING_LEVEL
27+
28+
#endif /* AWS_COMMON_HOST_UTILS_H */

source/host_utils.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
#include <aws/common/host_utils.h>
6+
#include <aws/common/string.h>
7+
#include <inttypes.h>
8+
9+
#ifdef _MSC_VER /* Disable sscanf warnings on windows. */
10+
# pragma warning(disable : 4204)
11+
# pragma warning(disable : 4706)
12+
# pragma warning(disable : 4996)
13+
#endif
14+
15+
/* 4 octets of 3 chars max + 3 separators + null terminator */
16+
#define AWS_IPV4_STR_LEN 16
17+
#define IP_CHAR_FMT "%03" SCNu16
18+
19+
static bool s_is_ipv6_char(uint8_t value) {
20+
return aws_isxdigit(value) || value == ':';
21+
}
22+
23+
static bool s_starts_with(struct aws_byte_cursor cur, uint8_t ch) {
24+
return cur.len > 0 && cur.ptr[0] == ch;
25+
}
26+
27+
static bool s_ends_with(struct aws_byte_cursor cur, uint8_t ch) {
28+
return cur.len > 0 && cur.ptr[cur.len - 1] == ch;
29+
}
30+
31+
bool aws_host_utils_is_ipv4(struct aws_byte_cursor host) {
32+
if (host.len > AWS_IPV4_STR_LEN - 1) {
33+
return false;
34+
}
35+
36+
char copy[AWS_IPV4_STR_LEN] = {0};
37+
memcpy(copy, host.ptr, host.len);
38+
39+
uint16_t octet[4] = {0};
40+
char remainder[2] = {0};
41+
if (4 != sscanf(
42+
copy,
43+
IP_CHAR_FMT "." IP_CHAR_FMT "." IP_CHAR_FMT "." IP_CHAR_FMT "%1s",
44+
&octet[0],
45+
&octet[1],
46+
&octet[2],
47+
&octet[3],
48+
remainder)) {
49+
return false;
50+
}
51+
52+
for (size_t i = 0; i < 4; ++i) {
53+
if (octet[i] > 255) {
54+
return false;
55+
}
56+
}
57+
58+
return true;
59+
}
60+
61+
/* actual encoding is %25, but % is omitted for simplicity, since split removes it */
62+
static struct aws_byte_cursor s_percent_uri_enc = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("25");
63+
/*
64+
* IPv6 format:
65+
* 8 groups of 4 hex chars separated by colons (:)
66+
* leading 0s in each group can be skipped
67+
* 2 or more consecutive zero groups can be replaced by double colon (::),
68+
* but only once.
69+
* ipv6 literal can be scoped by to zone by appending % followed by zone name
70+
* ( does not look like there is length reqs on zone name length. this
71+
* implementation enforces that its > 1 )
72+
* ipv6 can be embedded in url, in which case it must be wrapped inside []
73+
* and % be uri encoded as %25.
74+
* Implementation is fairly trivial and just iterates through the string
75+
* keeping track of the spec above.
76+
*/
77+
bool aws_host_utils_is_ipv6(struct aws_byte_cursor host, bool is_uri_encoded) {
78+
if (host.len == 0) {
79+
return false;
80+
}
81+
82+
if (is_uri_encoded) {
83+
if (!s_starts_with(host, '[') || !s_ends_with(host, ']')) {
84+
return false;
85+
}
86+
aws_byte_cursor_advance(&host, 1);
87+
--host.len;
88+
}
89+
90+
struct aws_byte_cursor substr = {0};
91+
/* first split is required ipv6 part */
92+
bool is_split = aws_byte_cursor_next_split(&host, '%', &substr);
93+
AWS_ASSERT(is_split); /* function is guaranteed to return at least one split */
94+
95+
if (!is_split || substr.len == 0 || s_ends_with(substr, ':') ||
96+
!aws_byte_cursor_satisfies_pred(&substr, s_is_ipv6_char)) {
97+
return false;
98+
}
99+
100+
uint8_t group_count = 0;
101+
bool has_double_colon = false;
102+
struct aws_byte_cursor group = {0};
103+
while (aws_byte_cursor_next_split(&substr, ':', &group)) {
104+
++group_count;
105+
106+
if (group_count > 8 || /* too many groups */
107+
group.len > 4 || /* too many chars in group */
108+
(has_double_colon && group.len == 0 && group_count > 2)) { /* only one double colon allowed */
109+
return false;
110+
}
111+
112+
has_double_colon = has_double_colon || group.len == 0;
113+
}
114+
115+
/* second split is optional zone part */
116+
if (aws_byte_cursor_next_split(&host, '%', &substr)) {
117+
if ((is_uri_encoded &&
118+
(substr.len < 3 ||
119+
!aws_byte_cursor_starts_with(&substr, &s_percent_uri_enc))) || /* encoding for % + 1 extra char */
120+
(!is_uri_encoded && substr.len == 0) || /* at least 1 char */
121+
!aws_byte_cursor_satisfies_pred(&substr, aws_isalnum)) {
122+
return false;
123+
}
124+
}
125+
126+
return has_double_colon ? group_count < 7 : group_count == 8;
127+
}

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,8 @@ add_test_case(test_cross_process_lock_works_cross_proc)
528528
add_test_case(cross_process_lock_mp_test_runner)
529529
add_test_case(test_cross_process_lock_invalid_nonce_fails)
530530

531+
add_test_case(host_util_is_ipv4)
532+
add_test_case(host_util_is_ipv6)
531533

532534
generate_test_driver(${PROJECT_NAME}-tests)
533535

tests/host_util_test.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#include <aws/common/byte_buf.h>
7+
#include <aws/common/host_utils.h>
8+
#include <aws/testing/aws_test_harness.h>
9+
10+
AWS_TEST_CASE(host_util_is_ipv4, s_test_is_ipv4)
11+
static int s_test_is_ipv4(struct aws_allocator *allocator, void *ctx) {
12+
(void)allocator;
13+
(void)ctx;
14+
15+
ASSERT_TRUE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("0.0.0.0")));
16+
ASSERT_TRUE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("127.0.0.1")));
17+
ASSERT_TRUE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("255.255.255.255")));
18+
ASSERT_TRUE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("192.168.1.1")));
19+
20+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("256.0.0.1")));
21+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("127.0.0")));
22+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("127.0")));
23+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("127")));
24+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("")));
25+
26+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("foo.com")));
27+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("a.b.c.d")));
28+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("a127.0.0.1")));
29+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("127.0.0.1a")));
30+
ASSERT_FALSE(aws_host_utils_is_ipv4(aws_byte_cursor_from_c_str("127.0.0.1011")));
31+
32+
return AWS_OP_SUCCESS;
33+
}
34+
35+
AWS_TEST_CASE(host_util_is_ipv6, s_test_is_ipv6)
36+
static int s_test_is_ipv6(struct aws_allocator *allocator, void *ctx) {
37+
(void)allocator;
38+
(void)ctx;
39+
40+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("0:0:0000:0000:0000:0:0:0"), false));
41+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("2001:0db8:0000:0000:0000:8a2e:0370:7334"), false));
42+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("2001:0DB8:0000:0000:0000:8a2e:0370:7334"), false));
43+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("fe80::1"), false));
44+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("fe80::1%en0"), false));
45+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("::1"), false));
46+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("0:0:0:0:0:0:0:1"), false));
47+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("fd00:ec2::23"), false));
48+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("fd00:ec2:0:0:0:0:0:23"), false));
49+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("[2001:0db8:0000:0000:0000:8a2e:0370:7334]"), true));
50+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("[fe80::1]"), true));
51+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("[fe80::1%25en0]"), true));
52+
ASSERT_TRUE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("[2001:db8:85a3:8d3:1319:8a2e:370:7348]"), true));
53+
54+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("2001:0db8:0000:0000:0000:8a2e:0370"), false));
55+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("2001:0db8:0000:0000:0000:8a2e:0370:"), false));
56+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("2001::"), false));
57+
ASSERT_FALSE(
58+
aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("2001:0db8:0000:0000:0000:8a2e:0370:7334:8745"), false));
59+
ASSERT_FALSE(
60+
aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str(":2001:0db8:0000:0000:0000:8a2e:0370:7334:8745"), false));
61+
ASSERT_FALSE(
62+
aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("z001:0db8:0000:0000:0000:8a2e:0370:7334:8745"), false));
63+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("z001::8a2e::8745"), false));
64+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("::2001:0db8:0000:0000:8a2e:0370:7334"), false));
65+
66+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("fe80::1%25en0"), true));
67+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("[fe80::1%en0]"), true));
68+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("[fe80::1%24en0]"), true));
69+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("[fe80::1%25en0"), true));
70+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("fe80::1%25en0]"), true));
71+
ASSERT_FALSE(aws_host_utils_is_ipv6(aws_byte_cursor_from_c_str("[fe80::1%25]"), true));
72+
73+
return AWS_OP_SUCCESS;
74+
}

0 commit comments

Comments
 (0)