Skip to content

Commit

Permalink
Merge pull request #49026 from sarchar/multiple-dns-resolves
Browse files Browse the repository at this point in the history
  • Loading branch information
akien-mga authored Jun 1, 2021
2 parents c5f237e + dd8fa11 commit 9990f28
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 9990f28

Please sign in to comment.