Skip to content
Merged
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
229 changes: 135 additions & 94 deletions core/io/ip_address.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

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 + ".");
}
ret = ret << 4;
ret += n;
}

p_dst[0] = ret >> 8;
p_dst[1] = ret & 0xff;
}
bool IPAddress::_parse_ipv6(const String &p_string, IPAddress &r_ip) {
int len = p_string.length();
const char32_t *buf = p_string.ptr();

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;

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;
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 (!part_found) {
part_skip = true;
parts[parts_idx++] = -1;
if (j - i > 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;
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;
}
} else {
ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + ".");
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;
}
}

int parts_extra = 0;
if (part_skip) {
parts_extra = parts_total - parts_count;
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.
}

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
// 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 {
_parse_hex(p_string, parts[i], (uint8_t *)&(field16[idx++]));
r_ip.field16[i] = r_ip.field16[i - pad];
}
}
return true;
}

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;
}
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();

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();
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;
}
parsed = false;
r_dest[cur] = next;
next = 0;
cur++;
if (cur > 3) {
return false;
}
} else if (c >= '0' && c <= '9') {
parsed = true;
next *= 10;
next += c - '0';
if (next > 255) {
return false;
}
} else {
return false; // Invalid char.
}
}
if (!parsed) {
return false;
}
r_dest[cur] = next;
return parsed && cur == 3;
}

void IPAddress::clear() {
Expand Down Expand Up @@ -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.");
Expand All @@ -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;
Expand Down
11 changes: 9 additions & 2 deletions core/io/ip_address.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,17 @@ struct [[nodiscard]] IPAddress {
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;
Expand Down Expand Up @@ -79,6 +84,8 @@ struct [[nodiscard]] IPAddress {
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; }
Expand Down
39 changes: 2 additions & 37 deletions core/string/ustring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down
Loading