mirror of
https://github.com/godotengine/godot.git
synced 2026-02-07 11:21:49 +00:00
network: Normalize IP parsing, fix IPv6, tests
This commit is contained in:
@@ -44,7 +44,7 @@ IPAddress::operator String() const {
|
||||
}
|
||||
|
||||
if (is_ipv4()) {
|
||||
// IPv4 address mapped to IPv6
|
||||
// IPv4 address mapped to IPv6.
|
||||
return itos(field8[12]) + "." + itos(field8[13]) + "." + itos(field8[14]) + "." + itos(field8[15]);
|
||||
}
|
||||
String ret;
|
||||
@@ -59,103 +59,134 @@ IPAddress::operator String() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) {
|
||||
uint16_t ret = 0;
|
||||
for (int i = p_start; i < p_start + 4; i++) {
|
||||
if (i >= p_string.length()) {
|
||||
break;
|
||||
}
|
||||
bool IPAddress::_parse_ipv6(const String &p_string, IPAddress &r_ip) {
|
||||
int len = p_string.length();
|
||||
const char32_t *buf = p_string.ptr();
|
||||
|
||||
int n = 0;
|
||||
char32_t c = p_string[i];
|
||||
if (is_digit(c)) {
|
||||
n = c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
n = 10 + (c - 'a');
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
n = 10 + (c - 'A');
|
||||
} else if (c == ':') {
|
||||
break;
|
||||
} else {
|
||||
ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + ".");
|
||||
int cur = 0;
|
||||
int shift = -1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
for (int j = i; j < len; j++) {
|
||||
char32_t c = buf[j];
|
||||
if (c == ':') {
|
||||
if (j + 1 == len) {
|
||||
return false; // Can't end with a column (unless part of shortening).
|
||||
}
|
||||
if (buf[j + 1] == ':') {
|
||||
if (shift > -1) {
|
||||
return false; // Only one shortening allowed.
|
||||
} else if (j == 0) {
|
||||
shift = cur;
|
||||
} else {
|
||||
shift = cur + 1;
|
||||
}
|
||||
j++;
|
||||
} else if (i == j) {
|
||||
return false; // Stray column.
|
||||
}
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
if (j - i > 3) {
|
||||
return false;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
r_ip.field16[cur] = r_ip.field16[cur] << 4;
|
||||
r_ip.field16[cur] |= c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
r_ip.field16[cur] = r_ip.field16[cur] << 4;
|
||||
r_ip.field16[cur] |= 10 + (c - 'a');
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
r_ip.field16[cur] = r_ip.field16[cur] << 4;
|
||||
r_ip.field16[cur] |= 10 + (c - 'A');
|
||||
} else if (c == '.') {
|
||||
// IPv4 mapped IPv6 (e.g. "::FFFF:127.0.0.1").
|
||||
if (cur < 1 || r_ip.field16[cur - 1] != 0xFFFF) {
|
||||
return false; // IPv6 part must end with FFFF.
|
||||
}
|
||||
if (shift < 0 && cur != 6) {
|
||||
return false; // Needs 5 zeros, and FFFF "0:0:0:0:0:FFFF:127.0.0.1".
|
||||
}
|
||||
// Only empty bytes allowed before FFFF.
|
||||
r_ip.field16[cur] = 0;
|
||||
r_ip.field16[cur - 1] = 0;
|
||||
while (cur > 0) {
|
||||
cur--;
|
||||
if (r_ip.field16[cur] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
r_ip.field16[5] = 0xFFFF;
|
||||
return _parse_ipv4(p_string, i, &r_ip.field8[12]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (j + 1 == len) {
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
r_ip.field16[cur] = BSWAP16(r_ip.field16[cur]);
|
||||
cur += 1;
|
||||
if (cur > 8 || (cur == 8 && i + 1 != len)) {
|
||||
return false;
|
||||
}
|
||||
ret = ret << 4;
|
||||
ret += n;
|
||||
}
|
||||
|
||||
p_dst[0] = ret >> 8;
|
||||
p_dst[1] = ret & 0xff;
|
||||
if (shift < 0) {
|
||||
return cur == 8; // Should have parsed 8 16-bits ints.
|
||||
} else if (shift > 7) {
|
||||
return false; // Can't shorten more than this.
|
||||
} else if (shift == cur) {
|
||||
return true; // Nothing to do, end is assumed zeroized.
|
||||
}
|
||||
// Shift bytes.
|
||||
int pad = 8 - cur;
|
||||
int blank_end = shift + pad;
|
||||
for (int i = 7; i > shift; i--) {
|
||||
if (i < blank_end) {
|
||||
r_ip.field16[i] = 0;
|
||||
} else {
|
||||
r_ip.field16[i] = r_ip.field16[i - pad];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IPAddress::_parse_ipv6(const String &p_string) {
|
||||
static const int parts_total = 8;
|
||||
int parts[parts_total] = { 0 };
|
||||
int parts_count = 0;
|
||||
bool part_found = false;
|
||||
bool part_skip = false;
|
||||
bool part_ipv4 = false;
|
||||
int parts_idx = 0;
|
||||
bool IPAddress::_parse_ipv4(const String &p_string, int p_start, uint8_t *r_dest) {
|
||||
int len = p_string.length();
|
||||
const char32_t *buf = p_string.ptr();
|
||||
|
||||
for (int i = 0; i < p_string.length(); i++) {
|
||||
char32_t c = p_string[i];
|
||||
if (c == ':') {
|
||||
if (i == 0) {
|
||||
continue; // next must be a ":"
|
||||
int cur = 0;
|
||||
uint16_t next = 0;
|
||||
bool parsed = false;
|
||||
for (int i = p_start; i < len; i++) {
|
||||
char32_t c = buf[i];
|
||||
if (c == '.') {
|
||||
if (!parsed) {
|
||||
return false;
|
||||
}
|
||||
if (!part_found) {
|
||||
part_skip = true;
|
||||
parts[parts_idx++] = -1;
|
||||
parsed = false;
|
||||
r_dest[cur] = next;
|
||||
next = 0;
|
||||
cur++;
|
||||
if (cur > 3) {
|
||||
return false;
|
||||
}
|
||||
part_found = false;
|
||||
} else if (c == '.') {
|
||||
part_ipv4 = true;
|
||||
|
||||
} else if (is_hex_digit(c)) {
|
||||
if (!part_found) {
|
||||
parts[parts_idx++] = i;
|
||||
part_found = true;
|
||||
++parts_count;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
parsed = true;
|
||||
next *= 10;
|
||||
next += c - '0';
|
||||
if (next > 255) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + ".");
|
||||
return false; // Invalid char.
|
||||
}
|
||||
}
|
||||
|
||||
int parts_extra = 0;
|
||||
if (part_skip) {
|
||||
parts_extra = parts_total - parts_count;
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
for (int i = 0; i < parts_idx; i++) {
|
||||
if (parts[i] == -1) {
|
||||
for (int j = 0; j < parts_extra; j++) {
|
||||
field16[idx++] = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (part_ipv4 && i == parts_idx - 1) {
|
||||
_parse_ipv4(p_string, parts[i], (uint8_t *)&field16[idx]); // should be the last one
|
||||
} else {
|
||||
_parse_hex(p_string, parts[i], (uint8_t *)&(field16[idx++]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IPAddress::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret) {
|
||||
String ip;
|
||||
if (p_start != 0) {
|
||||
ip = p_string.substr(p_start);
|
||||
} else {
|
||||
ip = p_string;
|
||||
}
|
||||
|
||||
int slices = ip.get_slice_count(".");
|
||||
ERR_FAIL_COND_MSG(slices != 4, "Invalid IP address string: " + ip + ".");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
p_ret[i] = ip.get_slicec('.', i).to_int();
|
||||
if (!parsed) {
|
||||
return false;
|
||||
}
|
||||
r_dest[cur] = next;
|
||||
return parsed && cur == 3;
|
||||
}
|
||||
|
||||
void IPAddress::clear() {
|
||||
@@ -192,23 +223,33 @@ void IPAddress::set_ipv6(const uint8_t *p_buf) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IPAddress::is_valid_ip_address(const String &p_string) {
|
||||
IPAddress addr;
|
||||
if (p_string.length() < IPV6_MAX_STRING_LENGTH && p_string.contains_char(':')) {
|
||||
return _parse_ipv6(p_string, addr);
|
||||
} else if (p_string.length() < IPV4_MAX_STRING_LENGTH) { // Try IPv4.
|
||||
return _parse_ipv4(p_string, 0, &addr.field8[12]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const String &p_string) {
|
||||
clear();
|
||||
|
||||
if (p_string == "*") {
|
||||
// Wildcard (not a valid IP)
|
||||
// Wildcard (not a valid IP).
|
||||
wildcard = true;
|
||||
|
||||
} else if (p_string.contains_char(':')) {
|
||||
// IPv6
|
||||
_parse_ipv6(p_string);
|
||||
valid = true;
|
||||
} else if (p_string.length() < IPV6_MAX_STRING_LENGTH && p_string.contains_char(':')) {
|
||||
// IPv6.
|
||||
valid = _parse_ipv6(p_string, *this);
|
||||
ERR_FAIL_COND_MSG(!valid, "Invalid IPv6 address: " + p_string);
|
||||
|
||||
} else if (p_string.get_slice_count(".") == 4) {
|
||||
// IPv4 (mapped to IPv6 internally)
|
||||
} else if (p_string.length() < IPV4_MAX_STRING_LENGTH) {
|
||||
// IPv4 (mapped to IPv6 internally).
|
||||
field16[5] = 0xffff;
|
||||
_parse_ipv4(p_string, 0, &field8[12]);
|
||||
valid = true;
|
||||
valid = _parse_ipv4(p_string, 0, &field8[12]);
|
||||
ERR_FAIL_COND_MSG(!valid, "Invalid IPv4 address: " + p_string);
|
||||
|
||||
} else {
|
||||
ERR_PRINT("Invalid IP address.");
|
||||
@@ -226,7 +267,7 @@ IPAddress::IPAddress(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, boo
|
||||
clear();
|
||||
valid = true;
|
||||
if (!is_v6) {
|
||||
// Mapped to IPv6
|
||||
// Mapped to IPv6.
|
||||
field16[5] = 0xffff;
|
||||
field8[12] = p_a;
|
||||
field8[13] = p_b;
|
||||
|
||||
@@ -40,12 +40,17 @@ private:
|
||||
uint32_t field32[4];
|
||||
};
|
||||
|
||||
enum {
|
||||
IPV4_MAX_STRING_LENGTH = 16,
|
||||
IPV6_MAX_STRING_LENGTH = 40,
|
||||
};
|
||||
|
||||
bool valid;
|
||||
bool wildcard;
|
||||
|
||||
protected:
|
||||
void _parse_ipv6(const String &p_string);
|
||||
void _parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret);
|
||||
static bool _parse_ipv6(const String &p_string, IPAddress &r_ip);
|
||||
static bool _parse_ipv4(const String &p_string, int p_start, uint8_t *r_dest);
|
||||
|
||||
public:
|
||||
//operator Variant() const;
|
||||
@@ -79,6 +84,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_valid_ip_address(const String &p_string);
|
||||
|
||||
void clear();
|
||||
bool is_wildcard() const { return wildcard; }
|
||||
bool is_valid() const { return valid; }
|
||||
|
||||
@@ -35,6 +35,7 @@ STATIC_ASSERT_INCOMPLETE_TYPE(class, Dictionary);
|
||||
STATIC_ASSERT_INCOMPLETE_TYPE(class, Object);
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/ip_address.h"
|
||||
#include "core/math/color.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/object/object.h"
|
||||
@@ -4940,43 +4941,7 @@ String String::validate_filename() const {
|
||||
}
|
||||
|
||||
bool String::is_valid_ip_address() const {
|
||||
if (find_char(':') >= 0) {
|
||||
Vector<String> ip = split(":");
|
||||
for (int i = 0; i < ip.size(); i++) {
|
||||
const String &n = ip[i];
|
||||
if (n.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
if (n.is_valid_hex_number(false)) {
|
||||
int64_t nint = n.hex_to_int();
|
||||
if (nint < 0 || nint > 0xffff) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!n.is_valid_ip_address()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
Vector<String> ip = split(".");
|
||||
if (ip.size() != 4) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < ip.size(); i++) {
|
||||
const String &n = ip[i];
|
||||
if (!n.is_valid_int()) {
|
||||
return false;
|
||||
}
|
||||
int val = n.to_int();
|
||||
if (val < 0 || val > 255) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return IPAddress::is_valid_ip_address(*this);
|
||||
}
|
||||
|
||||
bool String::is_resource_file() const {
|
||||
|
||||
283
tests/core/io/test_ip_address.h
Normal file
283
tests/core/io/test_ip_address.h
Normal file
@@ -0,0 +1,283 @@
|
||||
/**************************************************************************/
|
||||
/* test_ip_address.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/ip_address.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestIPAddress {
|
||||
|
||||
struct IPTester : public IPAddress {
|
||||
public:
|
||||
static bool _test_v4(const char *p_ip) {
|
||||
uint8_t ip[4];
|
||||
return _parse_ipv4(String::utf8(p_ip), 0, ip);
|
||||
}
|
||||
|
||||
static bool _test_v6(const char *p_ip) {
|
||||
IPAddress addr;
|
||||
return _parse_ipv6(String::utf8(p_ip), addr);
|
||||
}
|
||||
|
||||
static bool test_v4(const char *p_ip, bool p_valid) {
|
||||
bool is_6 = _test_v6(p_ip);
|
||||
bool is_4 = _test_v4(p_ip);
|
||||
bool is_valid = IPAddress::is_valid_ip_address(String::utf8(p_ip));
|
||||
IPAddress ip(p_ip);
|
||||
return is_4 == p_valid && is_6 == false && is_valid == p_valid && ip.is_valid() == p_valid && ip.is_wildcard() == false;
|
||||
}
|
||||
|
||||
static bool test_v6(const char *p_ip, bool p_valid) {
|
||||
bool is_6 = _test_v6(p_ip);
|
||||
bool is_4 = _test_v4(p_ip);
|
||||
bool is_valid = is_valid_ip_address(String::utf8(p_ip));
|
||||
IPAddress ip(p_ip);
|
||||
return is_4 == false && is_6 == p_valid && is_valid == p_valid && ip.is_valid() == p_valid && ip.is_wildcard() == false;
|
||||
}
|
||||
|
||||
static bool test_wildcard(const char *p_ip) {
|
||||
bool is_6 = _test_v6(p_ip);
|
||||
bool is_4 = _test_v4(p_ip);
|
||||
bool is_valid = is_valid_ip_address(String::utf8(p_ip));
|
||||
IPAddress ip(p_ip);
|
||||
return is_6 == false && is_4 == false && ip.is_valid() == false && is_valid == false && ip.is_wildcard() == true;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("[IPAddress] Wildcard and misc") {
|
||||
ERR_PRINT_OFF;
|
||||
|
||||
auto test_ip = [](const char *l_ip, bool l_valid) {
|
||||
return IPAddress(l_ip).is_wildcard() == false && l_valid == (IPTester::test_v4(l_ip, true) || IPTester::test_v6(l_ip, true));
|
||||
};
|
||||
|
||||
CHECK(IPTester::test_wildcard("*"));
|
||||
|
||||
CHECK(test_ip("", false));
|
||||
|
||||
CHECK(test_ip(" ", false));
|
||||
|
||||
CHECK(test_ip("::", true));
|
||||
CHECK(test_ip("0.0.0.0", true));
|
||||
|
||||
CHECK(test_ip("not an ip", false));
|
||||
CHECK(test_ip("surely.not:an:ip", false));
|
||||
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
TEST_CASE("[IPAddress] IPv4 is_valid") {
|
||||
ERR_PRINT_OFF;
|
||||
|
||||
auto test_ip = [](const char *l_ip, bool l_valid) {
|
||||
return IPTester::test_v4(l_ip, l_valid);
|
||||
};
|
||||
|
||||
// Valid IPs
|
||||
CHECK(test_ip("127.0.0.1", true));
|
||||
CHECK(test_ip("255.255.255.255", true));
|
||||
CHECK(test_ip("0.0.0.0", true));
|
||||
|
||||
// Invalid IPs
|
||||
CHECK(test_ip(" 127.0.0.1", false));
|
||||
CHECK(test_ip("127.0.0.1 ", false));
|
||||
CHECK(test_ip(" 127.0.0.1 ", false));
|
||||
CHECK(test_ip("127.0.0.-1", false));
|
||||
CHECK(test_ip("127.0.0.256", false));
|
||||
CHECK(test_ip("127.0.0.", false));
|
||||
CHECK(test_ip(".0.0.1", false));
|
||||
CHECK(test_ip("127.0.0.1.", false));
|
||||
CHECK(test_ip(".127.0.0.1", false));
|
||||
CHECK(test_ip("0.127.0.0.1", false));
|
||||
CHECK(test_ip("127.0.0.1.0", false));
|
||||
CHECK(test_ip(".....", false));
|
||||
CHECK(test_ip("....", false));
|
||||
CHECK(test_ip("...", false));
|
||||
CHECK(test_ip("..", false));
|
||||
CHECK(test_ip(".", false));
|
||||
CHECK(test_ip("", false));
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
TEST_CASE("[IPAddress] IPv4 Parsing") {
|
||||
auto test_ip = [](const char *l_ip, uint32_t l_test) {
|
||||
l_test = BSWAP32(l_test);
|
||||
return memcmp(IPAddress(l_ip).get_ipv4(), &l_test, 4) == 0 && IPTester::test_v4(l_ip, true);
|
||||
};
|
||||
CHECK(test_ip("127.0.0.1", 2130706433));
|
||||
CHECK(test_ip("255.0.0.0", 255 << 24));
|
||||
CHECK(test_ip("0.255.0.0", 255 << 16));
|
||||
CHECK(test_ip("0.0.255.0", 255 << 8));
|
||||
CHECK(test_ip("0.0.0.255", 255));
|
||||
CHECK(test_ip("127.0.0.0", 127 << 24));
|
||||
CHECK(test_ip("0.127.0.0", 127 << 16));
|
||||
CHECK(test_ip("0.0.127.0", 127 << 8));
|
||||
CHECK(test_ip("0.0.0.127", 127));
|
||||
CHECK(test_ip("1.0.0.0", 1 << 24));
|
||||
CHECK(test_ip("0.1.0.0", 1 << 16));
|
||||
CHECK(test_ip("0.0.1.0", 1 << 8));
|
||||
CHECK(test_ip("0.0.0.1", 1));
|
||||
}
|
||||
|
||||
TEST_CASE("[IPAddress] IPv6 is_valid") {
|
||||
ERR_PRINT_OFF;
|
||||
|
||||
auto test_ip = [](const char *l_ip, bool l_valid) {
|
||||
return IPTester::test_v6(l_ip, l_valid);
|
||||
};
|
||||
|
||||
// Valid IPs
|
||||
CHECK(test_ip("::", true));
|
||||
|
||||
CHECK(test_ip("::1", true));
|
||||
CHECK(test_ip("::1:1", true));
|
||||
CHECK(test_ip("::1:1:1", true));
|
||||
CHECK(test_ip("::1:1:1:1", true));
|
||||
CHECK(test_ip("::1:1:1:1:1", true));
|
||||
CHECK(test_ip("::1:1:1:1:1:1", true));
|
||||
CHECK(test_ip("::1:1:1:1:1:1:1", true));
|
||||
CHECK(test_ip("1:1:1:1:1:1:1:1", true));
|
||||
CHECK(test_ip("1:1:1:1:1:1:1::", true));
|
||||
CHECK(test_ip("1:1:1:1:1:1::", true));
|
||||
CHECK(test_ip("1:1:1:1:1::", true));
|
||||
CHECK(test_ip("1:1:1:1::", true));
|
||||
CHECK(test_ip("1:1:1::", true));
|
||||
CHECK(test_ip("1:1::", true));
|
||||
CHECK(test_ip("1::", true));
|
||||
|
||||
CHECK(test_ip("1::1:1:1:1:1:1", true));
|
||||
CHECK(test_ip("1:1::1:1:1:1:1", true));
|
||||
CHECK(test_ip("1:1:1::1:1:1:1", true));
|
||||
CHECK(test_ip("1:1:1:1::1:1:1", true));
|
||||
CHECK(test_ip("1:1:1:1:1::1:1", true));
|
||||
CHECK(test_ip("1:1:1:1:1:1::1", true));
|
||||
CHECK(test_ip("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true));
|
||||
CHECK(test_ip("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", true));
|
||||
CHECK(test_ip("::ffff", true));
|
||||
CHECK(test_ip("::FFFF", true));
|
||||
CHECK(test_ip("ffff::", true));
|
||||
CHECK(test_ip("FFFF::", true));
|
||||
|
||||
// IPv4-mapped address
|
||||
CHECK(test_ip("::ffff:127.0.0.1", true));
|
||||
CHECK(test_ip("::FFFF:127.0.0.1", true));
|
||||
CHECK(test_ip("0::ffff:127.0.0.1", true));
|
||||
CHECK(test_ip("0:0::ffff:127.0.0.1", true));
|
||||
CHECK(test_ip("0:0:0::ffff:127.0.0.1", true));
|
||||
CHECK(test_ip("0:0:0:0::ffff:127.0.0.1", true));
|
||||
CHECK(test_ip("0:0:0:0:0:ffff:127.0.0.1", true));
|
||||
|
||||
// Invalid IPs
|
||||
CHECK(test_ip(" ::", false));
|
||||
CHECK(test_ip(":: ", false));
|
||||
CHECK(test_ip("::-0", false));
|
||||
CHECK(test_ip("1:1:1:1:1:1:1:g", false));
|
||||
CHECK(test_ip(" 1:1:1:1:1:1:1:1", false));
|
||||
CHECK(test_ip("1:1:1:1:1:1:1:1 ", false));
|
||||
CHECK(test_ip(" 1:1:1:1:1:1:1:1 ", false));
|
||||
CHECK(test_ip("1:1:1:1:1:1:1:1:1", false));
|
||||
CHECK(test_ip("1:1:1:1:1:1:1:1::", false));
|
||||
CHECK(test_ip("::1:1:1:1:1:1:1:1", false));
|
||||
CHECK(test_ip("::1::1", false));
|
||||
CHECK(test_ip(":", false));
|
||||
CHECK(test_ip(":::", false));
|
||||
CHECK(test_ip("::::", false));
|
||||
CHECK(test_ip(":::::", false));
|
||||
CHECK(test_ip("::::::", false));
|
||||
CHECK(test_ip(":::::::", false));
|
||||
CHECK(test_ip("::::::::", false));
|
||||
|
||||
// IPv4-mapped address
|
||||
CHECK(test_ip("1::ffff:127.0.0.1", false));
|
||||
CHECK(test_ip("::1:ffff:127.0.0.1", false));
|
||||
CHECK(test_ip("::ffff:127.0.256.1", false));
|
||||
CHECK(test_ip("::ffff:127.0.0.256", false));
|
||||
CHECK(test_ip("::ffff:256.0.0.1", false));
|
||||
CHECK(test_ip("::ffff:127.0.0.1.1", false));
|
||||
CHECK(test_ip("::ffff:127.0.0.1.", false));
|
||||
CHECK(test_ip("::ffff:127.0.0.", false));
|
||||
CHECK(test_ip("::ffff:127.0.0", false));
|
||||
CHECK(test_ip("::ffff:127.0.", false));
|
||||
CHECK(test_ip("::ffff:127.0", false));
|
||||
CHECK(test_ip("::ffff:127.", false));
|
||||
CHECK(test_ip("::ffff:", false));
|
||||
|
||||
// This is a valid IPv6 address (non IPv4-mapped)
|
||||
CHECK(test_ip("::ffff:127", true));
|
||||
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
TEST_CASE("[IPAddress] IPv6 Parsing") {
|
||||
struct InitIP {
|
||||
uint16_t data[8];
|
||||
};
|
||||
auto test_ip = [](const char *l_ip, InitIP l_test) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
l_test.data[i] = BSWAP16(l_test.data[i]);
|
||||
}
|
||||
return memcmp(IPAddress(l_ip).get_ipv6(), l_test.data, 16) == 0 && IPTester::test_v6(l_ip, true);
|
||||
};
|
||||
CHECK(IPAddress("::").is_valid() == true);
|
||||
|
||||
CHECK(test_ip("::1", { 0, 0, 0, 0, 0, 0, 0, 1 }));
|
||||
CHECK(test_ip("::1:1", { 0, 0, 0, 0, 0, 0, 1, 1 }));
|
||||
CHECK(test_ip("::1:1:1", { 0, 0, 0, 0, 0, 1, 1, 1 }));
|
||||
CHECK(test_ip("::1:1:1:1", { 0, 0, 0, 0, 1, 1, 1, 1 }));
|
||||
CHECK(test_ip("::1:1:1:1:1", { 0, 0, 0, 1, 1, 1, 1, 1 }));
|
||||
CHECK(test_ip("::1:1:1:1:1:1", { 0, 0, 1, 1, 1, 1, 1, 1 }));
|
||||
CHECK(test_ip("::1:1:1:1:1:1:1", { 0, 1, 1, 1, 1, 1, 1, 1 }));
|
||||
CHECK(test_ip("1:1:1:1:1:1:1:1", { 1, 1, 1, 1, 1, 1, 1, 1 }));
|
||||
CHECK(test_ip("1:1:1:1:1:1:1::", { 1, 1, 1, 1, 1, 1, 1, 0 }));
|
||||
CHECK(test_ip("1:1:1:1:1:1::", { 1, 1, 1, 1, 1, 1, 0, 0 }));
|
||||
CHECK(test_ip("1:1:1:1:1::", { 1, 1, 1, 1, 1, 0, 0, 0 }));
|
||||
CHECK(test_ip("1:1:1:1::", { 1, 1, 1, 1, 0, 0, 0, 0 }));
|
||||
CHECK(test_ip("1:1:1::", { 1, 1, 1, 0, 0, 0, 0, 0 }));
|
||||
CHECK(test_ip("1:1::", { 1, 1, 0, 0, 0, 0, 0, 0 }));
|
||||
CHECK(test_ip("1::", { 1, 0, 0, 0, 0, 0, 0, 0 }));
|
||||
|
||||
CHECK(test_ip("ffff::", { 0xFFFF, 0, 0, 0, 0, 0, 0, 0 }));
|
||||
CHECK(test_ip("::ffff", { 0, 0, 0, 0, 0, 0, 0, 0xFFFF }));
|
||||
CHECK(test_ip("::fffe:0", { 0, 0, 0, 0, 0, 0, 0xFFFE, 0 }));
|
||||
CHECK(test_ip("0:fffe::", { 0, 0xFFFE, 0, 0, 0, 0, 0, 0 }));
|
||||
|
||||
// IPv4-mapped address
|
||||
CHECK(test_ip("::ffff:127.0.0.1", { 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1 }));
|
||||
CHECK(test_ip("::FFFF:127.0.0.1", { 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1 }));
|
||||
CHECK(test_ip("0::ffff:127.0.0.1", { 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1 }));
|
||||
CHECK(test_ip("0:0::ffff:127.0.0.1", { 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1 }));
|
||||
CHECK(test_ip("0:0:0::ffff:127.0.0.1", { 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1 }));
|
||||
CHECK(test_ip("0:0:0:0::ffff:127.0.0.1", { 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1 }));
|
||||
CHECK(test_ip("0:0:0:0:0:ffff:127.0.0.1", { 0, 0, 0, 0, 0, 0xFFFF, 0x7F00, 1 }));
|
||||
}
|
||||
|
||||
} // namespace TestIPAddress
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "tests/core/io/test_http_client.h"
|
||||
#include "tests/core/io/test_image.h"
|
||||
#include "tests/core/io/test_ip.h"
|
||||
#include "tests/core/io/test_ip_address.h"
|
||||
#include "tests/core/io/test_json.h"
|
||||
#include "tests/core/io/test_json_native.h"
|
||||
#include "tests/core/io/test_logger.h"
|
||||
|
||||
Reference in New Issue
Block a user