From dd8fa11ac182cc5b53746c9148067968d45c5588 Mon Sep 17 00:00:00 2001 From: Chuck Date: Mon, 24 May 2021 20:14:51 +0700 Subject: Support multiple address resolution in DNS requests Add two new functions to the IP class that returns all addresses/aliases associated with a given address. This is a cherry-pick merge from 010a3433df43a94fee95474360ffa6662c7441b9 which was merged in 2.1, and has been updated to build with the latest code. This merge adds two new methods IP.resolve_hostname_addresses and IP.get_resolve_item_addresses that returns a List of all addresses returned from the DNS request. --- core/io/ip.cpp | 97 +++++++++++++++++++++++++++++++++++++++--------- core/io/ip.h | 6 ++- doc/classes/IP.xml | 20 ++++++++++ drivers/unix/ip_unix.cpp | 22 ++++++++--- drivers/unix/ip_unix.h | 2 +- 5 files changed, 121 insertions(+), 26 deletions(-) diff --git a/core/io/ip.cpp b/core/io/ip.cpp index eb7814054b..de37ba87dd 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -41,13 +41,15 @@ VARIANT_ENUM_CAST(IP::ResolverStatus); struct _IP_ResolverPrivate { struct QueueItem { SafeNumeric status; - IPAddress response; + + List response; + String hostname; IP::Type type; void clear() { status.set(IP::RESOLVER_STATUS_NONE); - response = IPAddress(); + response.clear(); type = IP::TYPE_NONE; hostname = ""; }; @@ -80,13 +82,9 @@ struct _IP_ResolverPrivate { if (queue[i].status.get() != 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.set(IP::RESOLVER_STATUS_ERROR); - } else { - queue[i].status.set(IP::RESOLVER_STATUS_DONE); - } + IP::get_singleton()->_resolve_hostname(queue[i].response, queue[i].hostname, queue[i].type); + queue[i].status.set(queue[i].response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE); } } @@ -101,7 +99,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; @@ -111,15 +109,43 @@ struct _IP_ResolverPrivate { IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { MutexLock lock(resolver->mutex); + List res; + String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); - if (resolver->cache.has(key) && resolver->cache[key].is_valid()) { - IPAddress res = resolver->cache[key]; - return res; + if (resolver->cache.has(key)) { + res = resolver->cache[key]; + } else { + _resolve_hostname(res, p_hostname, p_type); + resolver->cache[key] = res; } + resolver->mutex.unlock(); - IPAddress res = _resolve_hostname(p_hostname, p_type); - resolver->cache[key] = res; - return res; + for (int i = 0; i < res.size(); ++i) { + if (res[i].is_valid()) { + return res[i]; + } + } + return IPAddress(); +} + +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); + } + + List res = resolver->cache[key]; + 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; } IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Type p_type) { @@ -135,11 +161,11 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); resolver->queue[id].hostname = p_hostname; resolver->queue[id].type = p_type; - if (resolver->cache.has(key) && resolver->cache[key].is_valid()) { + if (resolver->cache.has(key)) { resolver->queue[id].response = resolver->cache[key]; resolver->queue[id].status.set(IP::RESOLVER_STATUS_DONE); } else { - resolver->queue[id].response = IPAddress(); + resolver->queue[id].response = List(); resolver->queue[id].status.set(IP::RESOLVER_STATUS_WAITING); if (resolver->thread.is_started()) { resolver->sem.post(); @@ -175,7 +201,40 @@ IPAddress IP::get_resolve_item_address(ResolverID p_id) const { return IPAddress(); } - return resolver->queue[p_id].response; + List res = resolver->queue[p_id].response; + + resolver->mutex.unlock(); + + for (int i = 0; i < res.size(); ++i) { + if (res[i].is_valid()) { + return res[i]; + } + } + return IPAddress(); +} + +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.get() != IP::RESOLVER_STATUS_DONE) { + ERR_PRINT("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) { @@ -245,9 +304,11 @@ void IP::get_local_addresses(List *r_addresses) const { void IP::_bind_methods() { ClassDB::bind_method(D_METHOD("resolve_hostname", "host", "ip_type"), &IP::resolve_hostname, DEFVAL(IP::TYPE_ANY)); + ClassDB::bind_method(D_METHOD("resolve_hostname_addresses", "host", "ip_type"), &IP::resolve_hostname_addresses, DEFVAL(IP::TYPE_ANY)); ClassDB::bind_method(D_METHOD("resolve_hostname_queue_item", "host", "ip_type"), &IP::resolve_hostname_queue_item, DEFVAL(IP::TYPE_ANY)); ClassDB::bind_method(D_METHOD("get_resolve_item_status", "id"), &IP::get_resolve_item_status); ClassDB::bind_method(D_METHOD("get_resolve_item_address", "id"), &IP::get_resolve_item_address); + ClassDB::bind_method(D_METHOD("get_resolve_item_addresses", "id"), &IP::get_resolve_item_addresses); ClassDB::bind_method(D_METHOD("erase_resolve_item", "id"), &IP::erase_resolve_item); ClassDB::bind_method(D_METHOD("get_local_addresses"), &IP::_get_local_addresses); ClassDB::bind_method(D_METHOD("get_local_interfaces"), &IP::_get_local_interfaces); diff --git a/core/io/ip.h b/core/io/ip.h index 0c4a83257d..3c6040a1f0 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -69,7 +69,6 @@ protected: static IP *singleton; static void _bind_methods(); - virtual IPAddress _resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY) = 0; Array _get_local_addresses() const; Array _get_local_interfaces() const; @@ -84,11 +83,16 @@ public: }; IPAddress 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; IPAddress get_resolve_item_address(ResolverID p_id) const; virtual void get_local_addresses(List *r_addresses) const; + + virtual void _resolve_hostname(List &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const = 0; + Array get_resolve_item_addresses(ResolverID p_id) const; + virtual void get_local_interfaces(Map *r_interfaces) const = 0; void erase_resolve_item(ResolverID p_id); diff --git a/doc/classes/IP.xml b/doc/classes/IP.xml index 44da913492..b3ce1abaeb 100644 --- a/doc/classes/IP.xml +++ b/doc/classes/IP.xml @@ -59,6 +59,15 @@ Returns a queued hostname's IP address, given its queue [code]id[/code]. Returns an empty string on error or if resolution hasn't happened yet (see [method get_resolve_item_status]). + + + + + + + Returns resolved addresses, or an empty array if an error happened or resolution didn't happen yet (see [method get_resolve_item_status]). + + @@ -79,6 +88,17 @@ Returns a given hostname's IPv4 or IPv6 address when resolved (blocking-type method). The address type returned depends on the [enum Type] constant given as [code]ip_type[/code]. + + + + + + + + + Resolves a given hostname in a blocking way. Addresses are returned as an [Array] of IPv4 or IPv6 addresses depending on [code]ip_type[/code]. + + diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index 053e3fd9d6..e8f8ae4717 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -89,7 +89,7 @@ static IPAddress _sockaddr2ip(struct sockaddr *p_addr) { return ip; }; -IPAddress IPUnix::_resolve_hostname(const String &p_hostname, Type p_type) { +void IPUnix::_resolve_hostname(List &r_addresses, const String &p_hostname, Type p_type) const { struct addrinfo hints; struct addrinfo *result = nullptr; @@ -108,7 +108,7 @@ IPAddress IPUnix::_resolve_hostname(const String &p_hostname, Type p_type) { int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result); if (s != 0) { ERR_PRINT("getaddrinfo failed! Cannot resolve hostname."); - return IPAddress(); + return; }; if (result == nullptr || result->ai_addr == nullptr) { @@ -116,14 +116,24 @@ IPAddress IPUnix::_resolve_hostname(const String &p_hostname, Type p_type) { if (result) { freeaddrinfo(result); } - return IPAddress(); + return; }; - IPAddress ip = _sockaddr2ip(result->ai_addr); + struct addrinfo *next = result; - freeaddrinfo(result); + do { + if (next->ai_addr == NULL) { + next = next->ai_next; + continue; + } + IPAddress ip = _sockaddr2ip(next->ai_addr); + if (!r_addresses.find(ip)) { + r_addresses.push_back(ip); + } + next = next->ai_next; + } while (next); - return ip; + freeaddrinfo(result); } #if defined(WINDOWS_ENABLED) diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index e6479be6e5..0d64648b39 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -38,7 +38,7 @@ class IPUnix : public IP { GDCLASS(IPUnix, IP); - virtual IPAddress _resolve_hostname(const String &p_hostname, IP::Type p_type) override; + virtual void _resolve_hostname(List &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override; static IP *_create_unix(); -- cgit v1.2.3