Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ip6addr_aton allows invalid IPv6 address strings #33

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions src/core/ipv6/ip6_addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
/* used by IP6_ADDR_ANY(6) in ip6_addr.h */
const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);

#define SMALLEST_POSSIBLE_IPV6_STRLEN 2 /* "::" is the smallest possible ipv6 address */

#define lwip_xchar(i) ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10))

/**
Expand All @@ -71,18 +73,40 @@ const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
int
ip6addr_aton(const char *cp, ip6_addr_t *addr)
{
u32_t addr_index, zero_blocks, current_block_index, current_block_value;
u32_t addr_index, current_block_index, current_block_value, double_colon_found;
s32_t zero_blocks;
size_t len;
const char *s;
int block_length;
#if LWIP_IPV4
int check_ipv4_mapped = 0;
#endif /* LWIP_IPV4 */

if (!cp) {
return 0;
}

len = strlen(cp);

if (len < SMALLEST_POSSIBLE_IPV6_STRLEN) {
return 0;
}

/* if last character is a colon but not a double colon, invalid */
if ((cp[len-1] == ':') && (cp[len-2] != ':')) {
return 0;
}

/* Count the number of colons, to count the number of blocks in a "::" sequence
zero_blocks may be 1 even if there are no :: sequences */
zero_blocks = 8;
double_colon_found = 0;
for (s = cp; *s != 0; s++) {
if (*s == ':') {
zero_blocks--;
if (s[1] == ':') {
double_colon_found = 1;
}
#if LWIP_IPV4
} else if (*s == '.') {
if ((zero_blocks == 5) ||(zero_blocks == 2)) {
Expand All @@ -96,16 +120,25 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr)
break;
#endif /* LWIP_IPV4 */
} else if (!lwip_isxdigit(*s)) {
break;
return 0;
}
}

/* we found a double colon but it is impossible to populate it */
if (double_colon_found && zero_blocks <= 0) {
return 0;
}

/* parse each block */
addr_index = 0;
current_block_index = 0;
current_block_value = 0;
block_length = 0;
for (s = cp; *s != 0; s++) {
if (*s == ':') {
if (block_length > 4) {
return 0; /* invalid block length */
}
if (addr) {
if (current_block_index & 0x1) {
addr->addr[addr_index++] |= current_block_value;
Expand All @@ -132,6 +165,7 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr)
}
#endif /* LWIP_IPV4 */
current_block_value = 0;
block_length = 0;
if (current_block_index > 7) {
/* address too long! */
return 0;
Expand Down Expand Up @@ -160,16 +194,24 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr)
}
}
} else if (lwip_isxdigit(*s)) {
if (block_length == 4) {
return 0; /* invalid block length */
}
block_length++;
/* add current digit */
current_block_value = (current_block_value << 4) +
(lwip_isdigit(*s) ? (u32_t)(*s - '0') :
(u32_t)(10 + (lwip_islower(*s) ? *s - 'a' : *s - 'A')));
} else {
/* unexpected digit, space? CRLF? */
break;
return 0;
}
}

if (block_length > 4) {
return 0; /* invalid block length */
}

if (addr) {
if (current_block_index & 0x1) {
addr->addr[addr_index++] |= current_block_value;
Expand Down
43 changes: 43 additions & 0 deletions test/unit/ip6/test_ip6.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,17 @@ START_TEST(test_ip6_aton_ipv4mapped)
const ip_addr_t addr_expected = IPADDR6_INIT_HOST(0, 0, 0xFFFF, 0xD4CC65D2);
const char *full_ipv6_addr = "0:0:0:0:0:FFFF:D4CC:65D2";
const char *shortened_ipv6_addr = "::FFFF:D4CC:65D2";
const char *shortened_ipv6_addr_unexpected_char = "::FFFF:D4CC:65DZ";
const char *shortened_ipv6_addr_invalid = "::GGGGGGGG";
const char *full_ipv4_mapped_addr = "0:0:0:0:0:FFFF:212.204.101.210";
const char *shortened_ipv4_mapped_addr = "::FFFF:212.204.101.210";
const char *bogus_ipv4_mapped_addr = "::FFFF:212.204.101.2101";
const char *ipv6_block_too_long = "1234:5678:9aBc:acDef:1122:3344:5566:7788";
const char *ipv6_trailing_single_colon = "fE80::1:";
const char *ipv6_impossible_compression1 = "1234:5678:9aBc::cDef:1122:3344:5566:7788";
const char *ipv6_impossible_compression2 = "1234:5678:9aBc:cDef:1122:3344:5566:7788::";
const char *ipv6_valid_compression = "fE80::1:1";

LWIP_UNUSED_ARG(_i);

/* check IPv6 representation */
Expand All @@ -223,6 +231,16 @@ START_TEST(test_ip6_aton_ipv4mapped)
fail_unless(ret == 1);
fail_unless(memcmp(&addr, &addr_expected, 16) == 0);

/* check shortened IPv6 with unexpected char */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(shortened_ipv6_addr_unexpected_char, &addr6);
fail_unless(ret == 0);

/* check shortened IPv6 that is clearly invalid */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(shortened_ipv6_addr_invalid, &addr6);
fail_unless(ret == 0);

/* checked shortened mixed representation */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(shortened_ipv4_mapped_addr, &addr6);
Expand Down Expand Up @@ -250,6 +268,31 @@ START_TEST(test_ip6_aton_ipv4mapped)
memset(&addr, 0, sizeof(addr));
ret = ipaddr_aton(bogus_ipv4_mapped_addr, &addr);
fail_unless(ret == 0);

/* checking incorrect representation with a block containing 5 characters */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(ipv6_block_too_long, &addr6);
fail_unless(ret == 0);

/* trailing single colon, invalid */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(ipv6_trailing_single_colon, &addr6);
fail_unless(ret == 0);

/* impossible to support compression, already enough blocks, invalid */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(ipv6_impossible_compression1, &addr6);
fail_unless(ret == 0);

/* impossible to support compression at the end of the address, already enough blocks, invalid */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(ipv6_impossible_compression2, &addr6);
fail_unless(ret == 0);

/* valid ipv6 with compression */
memset(&addr6, 0, sizeof(addr6));
ret = ip6addr_aton(ipv6_valid_compression, &addr6);
fail_unless(ret == 1);
}
END_TEST

Expand Down