diff --git a/src/core/ipv6/ip6_addr.c b/src/core/ipv6/ip6_addr.c index 6e0ac86b0..0ca18499e 100644 --- a/src/core/ipv6/ip6_addr.c +++ b/src/core/ipv6/ip6_addr.c @@ -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)) /** @@ -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)) { @@ -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; @@ -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; @@ -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; diff --git a/test/unit/ip6/test_ip6.c b/test/unit/ip6/test_ip6.c index a030ee5a0..ccafca960 100644 --- a/test/unit/ip6/test_ip6.c +++ b/test/unit/ip6/test_ip6.c @@ -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 */ @@ -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); @@ -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