resolve_hostname_addresses: retrieve every addresses associated with a hostname

This commit is contained in:
James 2017-10-18 21:47:50 +02:00 committed by Rémi Verschelde
parent 2712014744
commit 010a3433df
5 changed files with 113 additions and 27 deletions

View file

@ -41,13 +41,13 @@ struct _IP_ResolverPrivate {
struct QueueItem { struct QueueItem {
volatile IP::ResolverStatus status; volatile IP::ResolverStatus status;
IP_Address response; List<IP_Address> response;
String hostname; String hostname;
IP::Type type; IP::Type type;
void clear() { void clear() {
status = IP::RESOLVER_STATUS_NONE; status = IP::RESOLVER_STATUS_NONE;
response = IP_Address(); response.clear();
type = IP::TYPE_NONE; type = IP::TYPE_NONE;
hostname = ""; hostname = "";
}; };
@ -81,12 +81,8 @@ struct _IP_ResolverPrivate {
if (queue[i].status != IP::RESOLVER_STATUS_WAITING) if (queue[i].status != IP::RESOLVER_STATUS_WAITING)
continue; continue;
queue[i].response = IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type); 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;
if (!queue[i].response.is_valid())
queue[i].status = IP::RESOLVER_STATUS_ERROR;
else
queue[i].status = IP::RESOLVER_STATUS_DONE;
} }
} }
@ -104,7 +100,7 @@ struct _IP_ResolverPrivate {
} }
} }
HashMap<String, IP_Address> cache; HashMap<String, List<IP_Address> > cache;
static String get_cache_key(String p_hostname, IP::Type p_type) { static String get_cache_key(String p_hostname, IP::Type p_type) {
return itos(p_type) + p_hostname; 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(); resolver->mutex->lock();
List<IP_Address> res;
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
if (resolver->cache.has(key)) { if (resolver->cache.has(key)) {
IP_Address res = resolver->cache[key]; res = resolver->cache[key];
resolver->mutex->unlock(); } else {
return res; _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); List<IP_Address> res = resolver->cache[key];
resolver->cache[key] = res;
resolver->mutex->unlock(); 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) { 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].response = resolver->cache[key];
resolver->queue[id].status = IP::RESOLVER_STATUS_DONE; resolver->queue[id].status = IP::RESOLVER_STATUS_DONE;
} else { } else {
resolver->queue[id].response = IP_Address(); resolver->queue[id].response = List<IP_Address>();
resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING; resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING;
if (resolver->thread) if (resolver->thread)
resolver->sem->post(); resolver->sem->post();
@ -187,10 +210,41 @@ IP_Address IP::get_resolve_item_address(ResolverID p_id) const {
return IP_Address(); return IP_Address();
} }
IP_Address res = resolver->queue[p_id].response; List<IP_Address> res = resolver->queue[p_id].response;
resolver->mutex->unlock(); 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<IP_Address> 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) { void IP::erase_resolve_item(ResolverID p_id) {
@ -235,9 +289,11 @@ Array IP::_get_local_addresses() const {
void IP::_bind_methods() { 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", "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("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_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_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("erase_resolve_item", "id"), &IP::erase_resolve_item);
ObjectTypeDB::bind_method(_MD("get_local_addresses"), &IP::_get_local_addresses); ObjectTypeDB::bind_method(_MD("get_local_addresses"), &IP::_get_local_addresses);
ObjectTypeDB::bind_method(_MD("clear_cache"), &IP::clear_cache, DEFVAL("")); ObjectTypeDB::bind_method(_MD("clear_cache"), &IP::clear_cache, DEFVAL(""));

View file

@ -70,17 +70,19 @@ protected:
static IP *singleton; static IP *singleton;
static void _bind_methods(); static void _bind_methods();
virtual IP_Address _resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY) = 0;
Array _get_local_addresses() const; Array _get_local_addresses() const;
static IP *(*_create)(); static IP *(*_create)();
public: public:
IP_Address resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY); 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 // async resolver hostname
ResolverID resolve_hostname_queue_item(const String &p_hostname, Type p_type = TYPE_ANY); ResolverID resolve_hostname_queue_item(const String &p_hostname, Type p_type = TYPE_ANY);
ResolverStatus get_resolve_item_status(ResolverID p_id) const; ResolverStatus get_resolve_item_status(ResolverID p_id) const;
IP_Address get_resolve_item_address(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<IP_Address> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const = 0;
virtual void get_local_addresses(List<IP_Address> *r_addresses) const = 0; virtual void get_local_addresses(List<IP_Address> *r_addresses) const = 0;
void erase_resolve_item(ResolverID p_id); void erase_resolve_item(ResolverID p_id);

View file

@ -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 a resolved item address, or an empty string if an error happened or resolution didn't happen yet (see [method get_resolve_item_status]).
</description> </description>
</method> </method>
<method name="get_resolve_item_addresses" qualifiers="const">
<return type="Array">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Return 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"> <method name="get_resolve_item_status" qualifiers="const">
<return type="int"> <return type="int">
</return> </return>
@ -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. Resolved hostname is returned as an IPv4 or IPv6 depending on "ip_type".
</description> </description>
</method> </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" default="3">
</argument>
<description>
Resolve a given hostname, blocking. Addresses are returned as an Array of IPv4 or IPv6 depending on "ip_type".
</description>
</method>
<method name="resolve_hostname_queue_item"> <method name="resolve_hostname_queue_item">
<return type="int"> <return type="int">
</return> </return>

View file

@ -85,7 +85,7 @@ static IP_Address _sockaddr2ip(struct sockaddr *p_addr) {
return ip; return ip;
}; };
IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { void IP_Unix::_resolve_hostname(List<IP_Address> &r_addresses, const String &p_hostname, Type p_type) const {
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *result; 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); int s = getaddrinfo(p_hostname.utf8().get_data(), NULL, &hints, &result);
if (s != 0) { if (s != 0) {
ERR_PRINT("getaddrinfo failed!"); ERR_PRINT("getaddrinfo failed!");
return IP_Address(); return;
}; };
if (result == NULL || result->ai_addr == NULL) { if (result == NULL || result->ai_addr == NULL) {
ERR_PRINT("Invalid response from getaddrinfo"); 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); freeaddrinfo(result);
return ip;
} }
#if defined(WINDOWS_ENABLED) #if defined(WINDOWS_ENABLED)

View file

@ -37,11 +37,10 @@
class IP_Unix : public IP { class IP_Unix : public IP {
OBJ_TYPE(IP_Unix, IP); OBJ_TYPE(IP_Unix, IP);
virtual IP_Address _resolve_hostname(const String &p_hostname, IP::Type p_type);
static IP *_create_unix(); static IP *_create_unix();
public: public:
virtual void _resolve_hostname(List<IP_Address> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const;
virtual void get_local_addresses(List<IP_Address> *r_addresses) const; virtual void get_local_addresses(List<IP_Address> *r_addresses) const;
static void make_default(); static void make_default();