2014-02-10 09:10:30 +08:00
/*************************************************************************/
/* ip.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 20:16:55 +08:00
/* https://godotengine.org */
2014-02-10 09:10:30 +08:00
/*************************************************************************/
2022-01-04 04:27:34 +08:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 09:10:30 +08:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 07:50:27 +08:00
2014-02-10 09:10:30 +08:00
# include "ip.h"
2018-09-12 00:13:45 +08:00
# include "core/os/semaphore.h"
# include "core/os/thread.h"
2020-11-08 06:33:38 +08:00
# include "core/templates/hash_map.h"
2022-08-06 02:35:08 +08:00
# include "core/variant/typed_array.h"
2014-02-10 09:10:30 +08:00
2014-12-16 02:42:58 +08:00
VARIANT_ENUM_CAST ( IP : : ResolverStatus ) ;
2014-02-10 09:10:30 +08:00
/************* RESOLVER ******************/
struct _IP_ResolverPrivate {
struct QueueItem {
2021-02-11 02:22:13 +08:00
SafeNumeric < IP : : ResolverStatus > status ;
2021-05-24 21:14:51 +08:00
List < IPAddress > response ;
2014-02-10 09:10:30 +08:00
String hostname ;
2016-12-01 13:34:05 +08:00
IP : : Type type ;
2014-02-10 09:10:30 +08:00
void clear ( ) {
2021-02-11 02:22:13 +08:00
status . set ( IP : : RESOLVER_STATUS_NONE ) ;
2021-05-24 21:14:51 +08:00
response . clear ( ) ;
2016-12-01 13:34:05 +08:00
type = IP : : TYPE_NONE ;
2014-02-10 09:10:30 +08:00
hostname = " " ;
} ;
2016-03-09 07:00:52 +08:00
2014-02-10 09:10:30 +08:00
QueueItem ( ) {
clear ( ) ;
2020-05-19 21:46:49 +08:00
}
2014-02-10 09:10:30 +08:00
} ;
QueueItem queue [ IP : : RESOLVER_MAX_QUERIES ] ;
IP : : ResolverID find_empty_id ( ) const {
for ( int i = 0 ; i < IP : : RESOLVER_MAX_QUERIES ; i + + ) {
2021-02-11 02:22:13 +08:00
if ( queue [ i ] . status . get ( ) = = IP : : RESOLVER_STATUS_NONE ) {
2014-02-10 09:10:30 +08:00
return i ;
2020-05-14 22:41:43 +08:00
}
2014-02-10 09:10:30 +08:00
}
return IP : : RESOLVER_INVALID_ID ;
}
2020-02-26 18:28:13 +08:00
Mutex mutex ;
2020-03-03 16:26:42 +08:00
Semaphore sem ;
2014-02-10 09:10:30 +08:00
2021-01-19 20:29:41 +08:00
Thread thread ;
2022-05-02 22:28:25 +08:00
bool thread_abort = false ;
2014-02-10 09:10:30 +08:00
void resolve_queues ( ) {
for ( int i = 0 ; i < IP : : RESOLVER_MAX_QUERIES ; i + + ) {
2021-02-11 02:22:13 +08:00
if ( queue [ i ] . status . get ( ) ! = IP : : RESOLVER_STATUS_WAITING ) {
2014-02-10 09:10:30 +08:00
continue ;
2020-05-14 22:41:43 +08:00
}
2014-02-10 09:10:30 +08:00
2021-08-03 01:25:41 +08:00
mutex . lock ( ) ;
List < IPAddress > response ;
String hostname = queue [ i ] . hostname ;
IP : : Type type = queue [ i ] . type ;
mutex . unlock ( ) ;
// We should not lock while resolving the hostname,
// only when modifying the queue.
IP : : get_singleton ( ) - > _resolve_hostname ( response , hostname , type ) ;
MutexLock lock ( mutex ) ;
// Could have been completed by another function, or deleted.
if ( queue [ i ] . status . get ( ) ! = IP : : RESOLVER_STATUS_WAITING ) {
continue ;
}
2022-01-29 08:33:29 +08:00
// We might be overriding another result, but we don't care as long as the result is valid.
if ( response . size ( ) ) {
String key = get_cache_key ( hostname , type ) ;
cache [ key ] = response ;
}
2021-08-03 01:25:41 +08:00
queue [ i ] . response = response ;
queue [ i ] . status . set ( response . is_empty ( ) ? IP : : RESOLVER_STATUS_ERROR : IP : : RESOLVER_STATUS_DONE ) ;
2014-02-10 09:10:30 +08:00
}
}
static void _thread_function ( void * self ) {
2022-04-05 18:40:26 +08:00
_IP_ResolverPrivate * ipr = static_cast < _IP_ResolverPrivate * > ( self ) ;
2014-02-10 09:10:30 +08:00
while ( ! ipr - > thread_abort ) {
2020-03-03 16:26:42 +08:00
ipr - > sem . wait ( ) ;
2014-02-10 09:10:30 +08:00
ipr - > resolve_queues ( ) ;
}
}
2021-05-24 21:14:51 +08:00
HashMap < String , List < IPAddress > > cache ;
2014-02-10 09:10:30 +08:00
2016-12-08 19:18:18 +08:00
static String get_cache_key ( String p_hostname , IP : : Type p_type ) {
return itos ( p_type ) + p_hostname ;
}
2014-02-10 09:10:30 +08:00
} ;
2021-05-06 08:48:18 +08:00
IPAddress IP : : resolve_hostname ( const String & p_hostname , IP : : Type p_type ) {
2022-08-06 02:35:08 +08:00
const PackedStringArray addresses = resolve_hostname_addresses ( p_hostname , p_type ) ;
return addresses . size ( ) ? ( IPAddress ) addresses [ 0 ] : IPAddress ( ) ;
2021-05-24 21:14:51 +08:00
}
2022-08-06 02:35:08 +08:00
PackedStringArray IP : : resolve_hostname_addresses ( const String & p_hostname , Type p_type ) {
2021-08-03 01:25:41 +08:00
List < IPAddress > res ;
2021-05-24 21:14:51 +08:00
String key = _IP_ResolverPrivate : : get_cache_key ( p_hostname , p_type ) ;
2021-08-03 01:25:41 +08:00
resolver - > mutex . lock ( ) ;
if ( resolver - > cache . has ( key ) ) {
res = resolver - > cache [ key ] ;
} else {
// This should be run unlocked so the resolver thread can keep resolving
// other requests.
resolver - > mutex . unlock ( ) ;
_resolve_hostname ( res , p_hostname , p_type ) ;
resolver - > mutex . lock ( ) ;
2022-01-29 08:33:29 +08:00
// We might be overriding another result, but we don't care as long as the result is valid.
if ( res . size ( ) ) {
resolver - > cache [ key ] = res ;
}
2021-08-03 01:25:41 +08:00
}
resolver - > mutex . unlock ( ) ;
2021-05-24 21:14:51 +08:00
2022-08-06 02:35:08 +08:00
PackedStringArray result ;
2021-05-24 21:14:51 +08:00
for ( int i = 0 ; i < res . size ( ) ; + + i ) {
2022-01-29 08:33:29 +08:00
result . push_back ( String ( res [ i ] ) ) ;
2021-05-24 21:14:51 +08:00
}
return result ;
2014-02-10 09:10:30 +08:00
}
2017-05-06 23:07:35 +08:00
2016-12-01 13:34:05 +08:00
IP : : ResolverID IP : : resolve_hostname_queue_item ( const String & p_hostname , IP : : Type p_type ) {
2020-02-26 18:28:13 +08:00
MutexLock lock ( resolver - > mutex ) ;
2014-02-10 09:10:30 +08:00
ResolverID id = resolver - > find_empty_id ( ) ;
if ( id = = RESOLVER_INVALID_ID ) {
WARN_PRINT ( " Out of resolver queries " ) ;
return id ;
}
2016-12-08 19:18:18 +08:00
String key = _IP_ResolverPrivate : : get_cache_key ( p_hostname , p_type ) ;
2014-02-10 09:10:30 +08:00
resolver - > queue [ id ] . hostname = p_hostname ;
2016-10-19 05:53:18 +08:00
resolver - > queue [ id ] . type = p_type ;
2021-05-24 21:14:51 +08:00
if ( resolver - > cache . has ( key ) ) {
2016-12-08 19:18:18 +08:00
resolver - > queue [ id ] . response = resolver - > cache [ key ] ;
2021-02-11 02:22:13 +08:00
resolver - > queue [ id ] . status . set ( IP : : RESOLVER_STATUS_DONE ) ;
2014-02-10 09:10:30 +08:00
} else {
2021-05-24 21:14:51 +08:00
resolver - > queue [ id ] . response = List < IPAddress > ( ) ;
2021-02-11 02:22:13 +08:00
resolver - > queue [ id ] . status . set ( IP : : RESOLVER_STATUS_WAITING ) ;
2021-01-19 20:29:41 +08:00
if ( resolver - > thread . is_started ( ) ) {
2020-03-03 16:26:42 +08:00
resolver - > sem . post ( ) ;
2020-05-14 22:41:43 +08:00
} else {
2014-02-10 09:10:30 +08:00
resolver - > resolve_queues ( ) ;
2020-05-14 22:41:43 +08:00
}
2014-02-10 09:10:30 +08:00
}
return id ;
}
IP : : ResolverStatus IP : : get_resolve_item_status ( ResolverID p_id ) const {
2022-03-24 00:07:17 +08:00
ERR_FAIL_INDEX_V_MSG ( p_id , IP : : RESOLVER_MAX_QUERIES , IP : : RESOLVER_STATUS_NONE , vformat ( " Too many concurrent DNS resolver queries (%d, but should be %d at most). Try performing less network requests at once. " , p_id , IP : : RESOLVER_MAX_QUERIES ) ) ;
2014-02-10 09:10:30 +08:00
2021-08-03 01:25:41 +08:00
IP : : ResolverStatus res = resolver - > queue [ p_id ] . status . get ( ) ;
if ( res = = IP : : RESOLVER_STATUS_NONE ) {
2017-05-06 23:07:35 +08:00
ERR_PRINT ( " Condition status == IP::RESOLVER_STATUS_NONE " ) ;
return IP : : RESOLVER_STATUS_NONE ;
}
2021-08-03 01:25:41 +08:00
return res ;
2014-02-10 09:10:30 +08:00
}
2017-05-06 23:07:35 +08:00
2021-05-06 08:48:18 +08:00
IPAddress IP : : get_resolve_item_address ( ResolverID p_id ) const {
2022-03-24 00:07:17 +08:00
ERR_FAIL_INDEX_V_MSG ( p_id , IP : : RESOLVER_MAX_QUERIES , IPAddress ( ) , vformat ( " Too many concurrent DNS resolver queries (%d, but should be %d at most). Try performing less network requests at once. " , p_id , IP : : RESOLVER_MAX_QUERIES ) ) ;
2014-02-10 09:10:30 +08:00
2020-02-26 18:28:13 +08:00
MutexLock lock ( resolver - > mutex ) ;
2014-02-10 09:10:30 +08:00
2021-02-11 02:22:13 +08:00
if ( resolver - > queue [ p_id ] . status . get ( ) ! = IP : : RESOLVER_STATUS_DONE ) {
2019-11-07 00:03:04 +08:00
ERR_PRINT ( " Resolve of ' " + resolver - > queue [ p_id ] . hostname + " '' didn't complete yet. " ) ;
2021-05-06 08:48:18 +08:00
return IPAddress ( ) ;
2014-02-10 09:10:30 +08:00
}
2021-05-24 21:14:51 +08:00
List < IPAddress > res = resolver - > queue [ p_id ] . response ;
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 {
2022-03-24 00:07:17 +08:00
ERR_FAIL_INDEX_V_MSG ( p_id , IP : : RESOLVER_MAX_QUERIES , Array ( ) , vformat ( " Too many concurrent DNS resolver queries (%d, but should be %d at most). Try performing less network requests at once. " , p_id , IP : : RESOLVER_MAX_QUERIES ) ) ;
2021-06-03 12:46:18 +08:00
MutexLock lock ( resolver - > mutex ) ;
2021-05-24 21:14:51 +08:00
if ( resolver - > queue [ p_id ] . status . get ( ) ! = IP : : RESOLVER_STATUS_DONE ) {
ERR_PRINT ( " Resolve of ' " + resolver - > queue [ p_id ] . hostname + " '' didn't complete yet. " ) ;
return Array ( ) ;
}
List < IPAddress > res = resolver - > queue [ p_id ] . response ;
Array result ;
for ( int i = 0 ; i < res . size ( ) ; + + i ) {
if ( res [ i ] . is_valid ( ) ) {
result . push_back ( String ( res [ i ] ) ) ;
}
}
return result ;
2014-02-10 09:10:30 +08:00
}
2017-05-06 23:07:35 +08:00
2014-02-10 09:10:30 +08:00
void IP : : erase_resolve_item ( ResolverID p_id ) {
2022-03-24 00:07:17 +08:00
ERR_FAIL_INDEX_MSG ( p_id , IP : : RESOLVER_MAX_QUERIES , vformat ( " Too many concurrent DNS resolver queries (%d, but should be %d at most). Try performing less network requests at once. " , p_id , IP : : RESOLVER_MAX_QUERIES ) ) ;
2014-02-10 09:10:30 +08:00
2021-02-11 02:22:13 +08:00
resolver - > queue [ p_id ] . status . set ( IP : : RESOLVER_STATUS_NONE ) ;
2014-02-10 09:10:30 +08:00
}
2016-10-20 18:04:10 +08:00
void IP : : clear_cache ( const String & p_hostname ) {
2020-02-26 18:28:13 +08:00
MutexLock lock ( resolver - > mutex ) ;
2017-05-06 23:07:35 +08:00
2020-12-15 20:04:21 +08:00
if ( p_hostname . is_empty ( ) ) {
2016-10-20 18:04:10 +08:00
resolver - > cache . clear ( ) ;
} else {
2016-12-08 19:18:18 +08:00
resolver - > cache . erase ( _IP_ResolverPrivate : : get_cache_key ( p_hostname , IP : : TYPE_NONE ) ) ;
resolver - > cache . erase ( _IP_ResolverPrivate : : get_cache_key ( p_hostname , IP : : TYPE_IPV4 ) ) ;
resolver - > cache . erase ( _IP_ResolverPrivate : : get_cache_key ( p_hostname , IP : : TYPE_IPV6 ) ) ;
resolver - > cache . erase ( _IP_ResolverPrivate : : get_cache_key ( p_hostname , IP : : TYPE_ANY ) ) ;
2016-10-20 18:04:10 +08:00
}
2017-05-06 23:07:35 +08:00
}
2014-02-10 09:10:30 +08:00
2022-08-06 02:35:08 +08:00
PackedStringArray IP : : _get_local_addresses ( ) const {
PackedStringArray addresses ;
2021-05-06 08:48:18 +08:00
List < IPAddress > ip_addresses ;
2014-05-24 12:35:47 +08:00
get_local_addresses ( & ip_addresses ) ;
2021-07-24 21:46:25 +08:00
for ( const IPAddress & E : ip_addresses ) {
2021-07-16 11:45:57 +08:00
addresses . push_back ( E ) ;
2014-05-24 12:35:47 +08:00
}
return addresses ;
}
2022-08-06 02:35:08 +08:00
TypedArray < Dictionary > IP : : _get_local_interfaces ( ) const {
TypedArray < Dictionary > results ;
2022-05-13 21:04:37 +08:00
HashMap < String , Interface_Info > interfaces ;
2019-05-07 16:17:00 +08:00
get_local_interfaces ( & interfaces ) ;
2021-08-10 04:13:42 +08:00
for ( KeyValue < String , Interface_Info > & E : interfaces ) {
Interface_Info & c = E . value ;
2019-05-07 16:17:00 +08:00
Dictionary rc ;
rc [ " name " ] = c . name ;
rc [ " friendly " ] = c . name_friendly ;
rc [ " index " ] = c . index ;
Array ips ;
2021-07-16 11:45:57 +08:00
for ( const IPAddress & F : c . ip_addresses ) {
ips . push_front ( F ) ;
2019-05-07 16:17:00 +08:00
}
rc [ " addresses " ] = ips ;
results . push_front ( rc ) ;
}
return results ;
}
2021-05-06 08:48:18 +08:00
void IP : : get_local_addresses ( List < IPAddress > * r_addresses ) const {
2022-05-13 21:04:37 +08:00
HashMap < String , Interface_Info > interfaces ;
2019-05-07 16:17:00 +08:00
get_local_interfaces ( & interfaces ) ;
2021-08-10 04:13:42 +08:00
for ( const KeyValue < String , Interface_Info > & E : interfaces ) {
for ( const IPAddress & F : E . value . ip_addresses ) {
2021-07-16 11:45:57 +08:00
r_addresses - > push_front ( F ) ;
2019-05-07 16:17:00 +08:00
}
}
}
2014-02-10 09:10:30 +08:00
void IP : : _bind_methods ( ) {
2017-02-13 19:47:24 +08:00
ClassDB : : bind_method ( D_METHOD ( " resolve_hostname " , " host " , " ip_type " ) , & IP : : resolve_hostname , DEFVAL ( IP : : TYPE_ANY ) ) ;
2021-05-24 21:14:51 +08:00
ClassDB : : bind_method ( D_METHOD ( " resolve_hostname_addresses " , " host " , " ip_type " ) , & IP : : resolve_hostname_addresses , DEFVAL ( IP : : TYPE_ANY ) ) ;
2017-02-13 19:47:24 +08:00
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 ) ;
2021-05-24 21:14:51 +08:00
ClassDB : : bind_method ( D_METHOD ( " get_resolve_item_addresses " , " id " ) , & IP : : get_resolve_item_addresses ) ;
2017-02-13 19:47:24 +08:00
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 ) ;
2019-05-07 16:17:00 +08:00
ClassDB : : bind_method ( D_METHOD ( " get_local_interfaces " ) , & IP : : _get_local_interfaces ) ;
2017-07-19 03:03:34 +08:00
ClassDB : : bind_method ( D_METHOD ( " clear_cache " , " hostname " ) , & IP : : clear_cache , DEFVAL ( " " ) ) ;
2017-03-05 23:44:50 +08:00
2017-08-20 23:45:01 +08:00
BIND_ENUM_CONSTANT ( RESOLVER_STATUS_NONE ) ;
BIND_ENUM_CONSTANT ( RESOLVER_STATUS_WAITING ) ;
BIND_ENUM_CONSTANT ( RESOLVER_STATUS_DONE ) ;
BIND_ENUM_CONSTANT ( RESOLVER_STATUS_ERROR ) ;
2017-03-05 23:44:50 +08:00
2014-02-10 09:10:30 +08:00
BIND_CONSTANT ( RESOLVER_MAX_QUERIES ) ;
BIND_CONSTANT ( RESOLVER_INVALID_ID ) ;
2017-03-05 23:44:50 +08:00
2017-08-20 23:45:01 +08:00
BIND_ENUM_CONSTANT ( TYPE_NONE ) ;
BIND_ENUM_CONSTANT ( TYPE_IPV4 ) ;
BIND_ENUM_CONSTANT ( TYPE_IPV6 ) ;
BIND_ENUM_CONSTANT ( TYPE_ANY ) ;
2014-02-10 09:10:30 +08:00
}
2020-04-02 07:20:12 +08:00
IP * IP : : singleton = nullptr ;
2014-02-10 09:10:30 +08:00
IP * IP : : get_singleton ( ) {
return singleton ;
}
2020-04-02 07:20:12 +08:00
IP * ( * IP : : _create ) ( ) = nullptr ;
2014-02-10 09:10:30 +08:00
IP * IP : : create ( ) {
2020-04-02 07:20:12 +08:00
ERR_FAIL_COND_V_MSG ( singleton , nullptr , " IP singleton already exist. " ) ;
ERR_FAIL_COND_V ( ! _create , nullptr ) ;
2014-02-10 09:10:30 +08:00
return _create ( ) ;
}
IP : : IP ( ) {
singleton = this ;
resolver = memnew ( _IP_ResolverPrivate ) ;
2020-03-03 16:26:42 +08:00
resolver - > thread_abort = false ;
2021-01-19 20:29:41 +08:00
resolver - > thread . start ( _IP_ResolverPrivate : : _thread_function , resolver ) ;
2014-02-10 09:10:30 +08:00
}
IP : : ~ IP ( ) {
2021-01-19 20:29:41 +08:00
resolver - > thread_abort = true ;
resolver - > sem . post ( ) ;
resolver - > thread . wait_to_finish ( ) ;
2017-05-06 23:07:35 +08:00
memdelete ( resolver ) ;
2014-02-10 09:10:30 +08:00
}