diff --git a/core/io/ip.cpp b/core/io/ip.cpp index ff1d3064b3f..da1b0cc182d 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -41,13 +41,13 @@ struct _IP_ResolverPrivate { struct QueueItem { volatile IP::ResolverStatus status; - IP_Address response; + List response; String hostname; IP::Type type; void clear() { status = IP::RESOLVER_STATUS_NONE; - response = IP_Address(); + response.clear(); type = IP::TYPE_NONE; hostname = ""; }; @@ -81,12 +81,8 @@ struct _IP_ResolverPrivate { if (queue[i].status != IP::RESOLVER_STATUS_WAITING) continue; - queue[i].response = IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type); - - if (!queue[i].response.is_valid()) - queue[i].status = IP::RESOLVER_STATUS_ERROR; - else - queue[i].status = IP::RESOLVER_STATUS_DONE; + IP::get_singleton()->_resolve_hostname(queue[i].response, queue[i].hostname, queue[i].type); + queue[i].status = queue[i].response.empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE; } } @@ -104,7 +100,7 @@ struct _IP_ResolverPrivate { } } - HashMap cache; + HashMap > cache; static String get_cache_key(String p_hostname, IP::Type p_type) { return itos(p_type) + p_hostname; @@ -115,17 +111,44 @@ IP_Address IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { resolver->mutex->lock(); + List res; + String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); if (resolver->cache.has(key)) { - IP_Address res = resolver->cache[key]; - resolver->mutex->unlock(); - return res; + res = resolver->cache[key]; + } else { + _resolve_hostname(res, p_hostname, p_type); + resolver->cache[key] = res; + } + resolver->mutex->unlock(); + + for (int i = 0; i < res.size(); ++i) { + if (res[i].is_valid()) { + return res[i]; + } + } + return IP_Address(); +} + +Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) { + + resolver->mutex->lock(); + + String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); + if (!resolver->cache.has(key)) { + _resolve_hostname(resolver->cache[key], p_hostname, p_type); } - IP_Address res = _resolve_hostname(p_hostname, p_type); - resolver->cache[key] = res; + List res = resolver->cache[key]; resolver->mutex->unlock(); - return res; + + Array result; + for (int i = 0; i < res.size(); ++i) { + if (res[i].is_valid()) { + result.push_back(String(res[i])); + } + } + return result; } IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Type p_type) { @@ -147,7 +170,7 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ resolver->queue[id].response = resolver->cache[key]; resolver->queue[id].status = IP::RESOLVER_STATUS_DONE; } else { - resolver->queue[id].response = IP_Address(); + resolver->queue[id].response = List(); resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING; if (resolver->thread) resolver->sem->post(); @@ -187,10 +210,41 @@ IP_Address IP::get_resolve_item_address(ResolverID p_id) const { return IP_Address(); } - IP_Address res = resolver->queue[p_id].response; + List res = resolver->queue[p_id].response; resolver->mutex->unlock(); - return res; + + for (int i = 0; i < res.size(); ++i) { + if (res[i].is_valid()) { + return res[i]; + } + } + return IP_Address(); +} + +Array IP::get_resolve_item_addresses(ResolverID p_id) const { + + ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, Array()); + + resolver->mutex->lock(); + + if (resolver->queue[p_id].status != IP::RESOLVER_STATUS_DONE) { + ERR_PRINTS("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet."); + resolver->mutex->unlock(); + return Array(); + } + + List res = resolver->queue[p_id].response; + + resolver->mutex->unlock(); + + Array result; + for (int i = 0; i < res.size(); ++i) { + if (res[i].is_valid()) { + result.push_back(String(res[i])); + } + } + return result; } void IP::erase_resolve_item(ResolverID p_id) { @@ -235,9 +289,11 @@ Array IP::_get_local_addresses() const { void IP::_bind_methods() { ObjectTypeDB::bind_method(_MD("resolve_hostname", "host", "ip_type"), &IP::resolve_hostname, DEFVAL(IP::TYPE_ANY)); + ObjectTypeDB::bind_method(_MD("resolve_hostname_addresses", "host", "ip_type"), &IP::resolve_hostname_addresses); ObjectTypeDB::bind_method(_MD("resolve_hostname_queue_item", "host", "ip_type"), &IP::resolve_hostname_queue_item, DEFVAL(IP::TYPE_ANY)); ObjectTypeDB::bind_method(_MD("get_resolve_item_status", "id"), &IP::get_resolve_item_status); ObjectTypeDB::bind_method(_MD("get_resolve_item_address", "id"), &IP::get_resolve_item_address); + ObjectTypeDB::bind_method(_MD("get_resolve_item_addresses", "id"), &IP::get_resolve_item_addresses); ObjectTypeDB::bind_method(_MD("erase_resolve_item", "id"), &IP::erase_resolve_item); ObjectTypeDB::bind_method(_MD("get_local_addresses"), &IP::_get_local_addresses); ObjectTypeDB::bind_method(_MD("clear_cache"), &IP::clear_cache, DEFVAL("")); diff --git a/core/io/ip.h b/core/io/ip.h index 9b839ce2b32..c803ae33a84 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -70,17 +70,19 @@ protected: static IP *singleton; static void _bind_methods(); - virtual IP_Address _resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY) = 0; Array _get_local_addresses() const; static IP *(*_create)(); public: IP_Address resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY); + Array resolve_hostname_addresses(const String &p_hostname, Type p_type = TYPE_ANY); // async resolver hostname ResolverID resolve_hostname_queue_item(const String &p_hostname, Type p_type = TYPE_ANY); ResolverStatus get_resolve_item_status(ResolverID p_id) const; IP_Address get_resolve_item_address(ResolverID p_id) const; + Array get_resolve_item_addresses(ResolverID p_id) const; + virtual void _resolve_hostname(List &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const = 0; virtual void get_local_addresses(List *r_addresses) const = 0; void erase_resolve_item(ResolverID p_id); diff --git a/doc/base/classes.xml b/doc/base/classes.xml index efd7247a161..7ab76faaaac 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -15782,6 +15782,15 @@ Return a resolved item address, or an empty string if an error happened or resolution didn't happen yet (see [method get_resolve_item_status]). + + + + + + + Return resolved addresses, or an empty array if an error happened or resolution didn't happen yet (see [method get_resolve_item_status]). + + @@ -15802,6 +15811,17 @@ Resolve a given hostname, blocking. Resolved hostname is returned as an IPv4 or IPv6 depending on "ip_type". + + + + + + + + + Resolve a given hostname, blocking. Addresses are returned as an Array of IPv4 or IPv6 depending on "ip_type". + + diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index 25108e53468..1ea41429e94 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -85,7 +85,7 @@ static IP_Address _sockaddr2ip(struct sockaddr *p_addr) { return ip; }; -IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { +void IP_Unix::_resolve_hostname(List &r_addresses, const String &p_hostname, Type p_type) const { struct addrinfo hints; struct addrinfo *result; @@ -105,19 +105,28 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { int s = getaddrinfo(p_hostname.utf8().get_data(), NULL, &hints, &result); if (s != 0) { ERR_PRINT("getaddrinfo failed!"); - return IP_Address(); + return; }; if (result == NULL || result->ai_addr == NULL) { ERR_PRINT("Invalid response from getaddrinfo"); - return IP_Address(); + return; }; - IP_Address ip = _sockaddr2ip(result->ai_addr); + struct addrinfo *next = result; + + do { + if (next->ai_addr == NULL) { + next = next->ai_next; + continue; + } + IP_Address ip = _sockaddr2ip(next->ai_addr); + if (!r_addresses.find(ip)) + r_addresses.push_back(ip); + next = next->ai_next; + } while (next); freeaddrinfo(result); - - return ip; } #if defined(WINDOWS_ENABLED) diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index 9ad4626ff20..364120638b3 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -37,11 +37,10 @@ class IP_Unix : public IP { OBJ_TYPE(IP_Unix, IP); - virtual IP_Address _resolve_hostname(const String &p_hostname, IP::Type p_type); - static IP *_create_unix(); public: + virtual void _resolve_hostname(List &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const; virtual void get_local_addresses(List *r_addresses) const; static void make_default();