Skip to content

Commit

Permalink
Support multiple address resolution in DNS requests
Browse files Browse the repository at this point in the history
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 010a343 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.
  • Loading branch information
sarchar committed Jun 1, 2021
1 parent 635d0c9 commit dd8fa11
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 26 deletions.
97 changes: 79 additions & 18 deletions core/io/ip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@ VARIANT_ENUM_CAST(IP::ResolverStatus);
struct _IP_ResolverPrivate {
struct QueueItem {
SafeNumeric<IP::ResolverStatus> status;
IPAddress response;

List<IPAddress> response;

String hostname;
IP::Type type;

void clear() {
status.set(IP::RESOLVER_STATUS_NONE);
response = IPAddress();
response.clear();
type = IP::TYPE_NONE;
hostname = "";
};
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -101,7 +99,7 @@ struct _IP_ResolverPrivate {
}
}

HashMap<String, IPAddress> cache;
HashMap<String, List<IPAddress>> cache;

static String get_cache_key(String p_hostname, IP::Type p_type) {
return itos(p_type) + p_hostname;
Expand All @@ -111,15 +109,43 @@ struct _IP_ResolverPrivate {
IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
MutexLock lock(resolver->mutex);

List<IPAddress> 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<IPAddress> 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) {
Expand All @@ -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<IPAddress>();
resolver->queue[id].status.set(IP::RESOLVER_STATUS_WAITING);
if (resolver->thread.is_started()) {
resolver->sem.post();
Expand Down Expand Up @@ -175,7 +201,40 @@ IPAddress IP::get_resolve_item_address(ResolverID p_id) const {
return IPAddress();
}

return resolver->queue[p_id].response;
List<IPAddress> 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<IPAddress> 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) {
Expand Down Expand Up @@ -245,9 +304,11 @@ void IP::get_local_addresses(List<IPAddress> *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);
Expand Down
6 changes: 5 additions & 1 deletion core/io/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class IP : public Object {
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;

Expand All @@ -84,11 +83,16 @@ class IP : public Object {
};

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<IPAddress> *r_addresses) const;

virtual void _resolve_hostname(List<IPAddress> &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<String, Interface_Info> *r_interfaces) const = 0;
void erase_resolve_item(ResolverID p_id);

Expand Down
20 changes: 20 additions & 0 deletions doc/classes/IP.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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]).
</description>
</method>
<method name="get_resolve_item_addresses" qualifiers="const">
<return type="Array">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns resolved addresses, or an empty array if an error happened or resolution didn't happen yet (see [method get_resolve_item_status]).
</description>
</method>
<method name="get_resolve_item_status" qualifiers="const">
<return type="int" enum="IP.ResolverStatus">
</return>
Expand All @@ -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].
</description>
</method>
<method name="resolve_hostname_addresses">
<return type="Array">
</return>
<argument index="0" name="host" type="String">
</argument>
<argument index="1" name="ip_type" type="int" enum="IP.Type" default="3">
</argument>
<description>
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].
</description>
</method>
<method name="resolve_hostname_queue_item">
<return type="int">
</return>
Expand Down
22 changes: 16 additions & 6 deletions drivers/unix/ip_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const {
struct addrinfo hints;
struct addrinfo *result = nullptr;

Expand All @@ -108,22 +108,32 @@ 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) {
ERR_PRINT("Invalid response from getaddrinfo");
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)
Expand Down
2 changes: 1 addition & 1 deletion drivers/unix/ip_unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<IPAddress> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override;

static IP *_create_unix();

Expand Down

0 comments on commit dd8fa11

Please sign in to comment.