2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* ip.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "ip.h"
2018-09-11 18:13:45 +02:00
# include "core/os/semaphore.h"
# include "core/os/thread.h"
2020-11-07 23:33:38 +01:00
# include "core/templates/hash_map.h"
2022-08-05 20:35:08 +02:00
# include "core/variant/typed_array.h"
2014-02-10 02:10:30 +01:00
/************* RESOLVER ******************/
struct _IP_ResolverPrivate {
struct QueueItem {
2021-02-10 19:22:13 +01:00
SafeNumeric < IP : : ResolverStatus > status ;
2021-05-24 15:14:51 +02:00
List < IPAddress > response ;
2014-02-10 02:10:30 +01:00
String hostname ;
2016-12-01 06:34:05 +01:00
IP : : Type type ;
2014-02-10 02:10:30 +01:00
void clear ( ) {
2021-02-10 19:22:13 +01:00
status . set ( IP : : RESOLVER_STATUS_NONE ) ;
2021-05-24 15:14:51 +02:00
response . clear ( ) ;
2016-12-01 06:34:05 +01:00
type = IP : : TYPE_NONE ;
2017-03-05 16:44:50 +01:00
hostname = " " ;
2014-02-10 02:10:30 +01:00
} ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
QueueItem ( ) {
clear ( ) ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
} ;
QueueItem queue [ IP : : RESOLVER_MAX_QUERIES ] ;
IP : : ResolverID find_empty_id ( ) const {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < IP : : RESOLVER_MAX_QUERIES ; i + + ) {
2021-02-10 19:22:13 +01:00
if ( queue [ i ] . status . get ( ) = = IP : : RESOLVER_STATUS_NONE ) {
2014-02-10 02:10:30 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return IP : : RESOLVER_INVALID_ID ;
}
2020-02-26 11:28:13 +01:00
Mutex mutex ;
2020-03-03 09:26:42 +01:00
Semaphore sem ;
2014-02-10 02:10:30 +01:00
2021-01-19 13:29:41 +01:00
Thread thread ;
2023-02-22 22:49:48 +01:00
SafeFlag thread_abort ;
2014-02-10 02:10:30 +01:00
void resolve_queues ( ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < IP : : RESOLVER_MAX_QUERIES ; i + + ) {
2021-02-10 19:22:13 +01:00
if ( queue [ i ] . status . get ( ) ! = IP : : RESOLVER_STATUS_WAITING ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2021-08-02 19:25:41 +02: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 01:33:29 +01: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-02 19:25:41 +02:00
queue [ i ] . response = response ;
queue [ i ] . status . set ( response . is_empty ( ) ? IP : : RESOLVER_STATUS_ERROR : IP : : RESOLVER_STATUS_DONE ) ;
2014-02-10 02:10:30 +01:00
}
}
static void _thread_function ( void * self ) {
2022-04-05 12:40:26 +02:00
_IP_ResolverPrivate * ipr = static_cast < _IP_ResolverPrivate * > ( self ) ;
2014-02-10 02:10:30 +01:00
2023-02-22 22:49:48 +01:00
while ( ! ipr - > thread_abort . is_set ( ) ) {
2020-03-03 09:26:42 +01:00
ipr - > sem . wait ( ) ;
2014-02-10 02:10:30 +01:00
ipr - > resolve_queues ( ) ;
}
}
2021-05-24 15:14:51 +02:00
HashMap < String , List < IPAddress > > cache ;
2014-02-10 02:10:30 +01:00
2024-01-09 02:36:19 +01:00
static String get_cache_key ( const String & p_hostname , IP : : Type p_type ) {
2016-12-08 12:18:18 +01:00
return itos ( p_type ) + p_hostname ;
}
2014-02-10 02:10:30 +01:00
} ;
2021-05-06 02:48:18 +02:00
IPAddress IP : : resolve_hostname ( const String & p_hostname , IP : : Type p_type ) {
2022-08-05 20:35:08 +02:00
const PackedStringArray addresses = resolve_hostname_addresses ( p_hostname , p_type ) ;
return addresses . size ( ) ? ( IPAddress ) addresses [ 0 ] : IPAddress ( ) ;
2021-05-24 15:14:51 +02:00
}
2022-08-05 20:35:08 +02:00
PackedStringArray IP : : resolve_hostname_addresses ( const String & p_hostname , Type p_type ) {
2021-08-02 19:25:41 +02:00
List < IPAddress > res ;
2021-05-24 15:14:51 +02:00
String key = _IP_ResolverPrivate : : get_cache_key ( p_hostname , p_type ) ;
2021-08-02 19:25:41 +02: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 01:33:29 +01: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-02 19:25:41 +02:00
}
resolver - > mutex . unlock ( ) ;
2021-05-24 15:14:51 +02:00
2022-08-05 20:35:08 +02:00
PackedStringArray result ;
2024-04-15 15:18:34 +02:00
for ( const IPAddress & E : res ) {
result . push_back ( String ( E ) ) ;
2021-05-24 15:14:51 +02:00
}
return result ;
2014-02-10 02:10:30 +01:00
}
2017-05-06 17:07:35 +02:00
2017-03-05 16:44:50 +01:00
IP : : ResolverID IP : : resolve_hostname_queue_item ( const String & p_hostname , IP : : Type p_type ) {
2020-02-26 11:28:13 +01:00
MutexLock lock ( resolver - > mutex ) ;
2014-02-10 02:10:30 +01:00
ResolverID id = resolver - > find_empty_id ( ) ;
2017-03-05 16:44:50 +01:00
if ( id = = RESOLVER_INVALID_ID ) {
2014-02-10 02:10:30 +01:00
WARN_PRINT ( " Out of resolver queries " ) ;
return id ;
}
2016-12-08 12:18:18 +01:00
String key = _IP_ResolverPrivate : : get_cache_key ( p_hostname , p_type ) ;
2017-03-05 16:44:50 +01:00
resolver - > queue [ id ] . hostname = p_hostname ;
2016-10-18 23:53:18 +02:00
resolver - > queue [ id ] . type = p_type ;
2021-05-24 15:14:51 +02:00
if ( resolver - > cache . has ( key ) ) {
2017-03-05 16:44:50 +01:00
resolver - > queue [ id ] . response = resolver - > cache [ key ] ;
2021-02-10 19:22:13 +01:00
resolver - > queue [ id ] . status . set ( IP : : RESOLVER_STATUS_DONE ) ;
2014-02-10 02:10:30 +01:00
} else {
2021-05-24 15:14:51 +02:00
resolver - > queue [ id ] . response = List < IPAddress > ( ) ;
2021-02-10 19:22:13 +01:00
resolver - > queue [ id ] . status . set ( IP : : RESOLVER_STATUS_WAITING ) ;
2021-01-19 13:29:41 +01:00
if ( resolver - > thread . is_started ( ) ) {
2020-03-03 09:26:42 +01:00
resolver - > sem . post ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
resolver - > resolve_queues ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return id ;
}
IP : : ResolverStatus IP : : get_resolve_item_status ( ResolverID p_id ) const {
2022-03-23 17:07:17 +01: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 02:10:30 +01:00
2021-08-02 19:25:41 +02:00
IP : : ResolverStatus res = resolver - > queue [ p_id ] . status . get ( ) ;
if ( res = = IP : : RESOLVER_STATUS_NONE ) {
2017-05-06 17:07:35 +02:00
ERR_PRINT ( " Condition status == IP::RESOLVER_STATUS_NONE " ) ;
return IP : : RESOLVER_STATUS_NONE ;
}
2021-08-02 19:25:41 +02:00
return res ;
2014-02-10 02:10:30 +01:00
}
2017-05-06 17:07:35 +02:00
2021-05-06 02:48:18 +02:00
IPAddress IP : : get_resolve_item_address ( ResolverID p_id ) const {
2022-03-23 17:07:17 +01: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 02:10:30 +01:00
2020-02-26 11:28:13 +01:00
MutexLock lock ( resolver - > mutex ) ;
2014-02-10 02:10:30 +01:00
2021-02-10 19:22:13 +01:00
if ( resolver - > queue [ p_id ] . status . get ( ) ! = IP : : RESOLVER_STATUS_DONE ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Resolve of ' " + resolver - > queue [ p_id ] . hostname + " '' didn't complete yet. " ) ;
2021-05-06 02:48:18 +02:00
return IPAddress ( ) ;
2014-02-10 02:10:30 +01:00
}
2021-05-24 15:14:51 +02:00
List < IPAddress > res = resolver - > queue [ p_id ] . response ;
2024-04-15 15:18:34 +02:00
for ( const IPAddress & E : res ) {
if ( E . is_valid ( ) ) {
return E ;
2021-05-24 15:14:51 +02:00
}
}
return IPAddress ( ) ;
}
Array IP : : get_resolve_item_addresses ( ResolverID p_id ) const {
2022-03-23 17:07:17 +01: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 06:46:18 +02:00
MutexLock lock ( resolver - > mutex ) ;
2021-05-24 15:14:51 +02: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 ;
2024-04-15 15:18:34 +02:00
for ( const IPAddress & E : res ) {
if ( E . is_valid ( ) ) {
result . push_back ( String ( E ) ) ;
2021-05-24 15:14:51 +02:00
}
}
return result ;
2014-02-10 02:10:30 +01:00
}
2017-05-06 17:07:35 +02:00
2014-02-10 02:10:30 +01:00
void IP : : erase_resolve_item ( ResolverID p_id ) {
2022-03-23 17:07:17 +01: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 02:10:30 +01:00
2021-02-10 19:22:13 +01:00
resolver - > queue [ p_id ] . status . set ( IP : : RESOLVER_STATUS_NONE ) ;
2014-02-10 02:10:30 +01:00
}
2016-10-20 12:04:10 +02:00
void IP : : clear_cache ( const String & p_hostname ) {
2020-02-26 11:28:13 +01:00
MutexLock lock ( resolver - > mutex ) ;
2017-05-06 17:07:35 +02:00
2020-12-15 13:04:21 +01:00
if ( p_hostname . is_empty ( ) ) {
2016-10-20 12:04:10 +02:00
resolver - > cache . clear ( ) ;
} else {
2016-12-08 12:18:18 +01: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 12:04:10 +02:00
}
2017-05-06 17:07:35 +02:00
}
2014-02-10 02:10:30 +01:00
2022-08-05 20:35:08 +02:00
PackedStringArray IP : : _get_local_addresses ( ) const {
PackedStringArray addresses ;
2021-05-06 02:48:18 +02:00
List < IPAddress > ip_addresses ;
2014-05-24 06:35:47 +02:00
get_local_addresses ( & ip_addresses ) ;
2021-07-24 15:46:25 +02:00
for ( const IPAddress & E : ip_addresses ) {
2021-07-16 05:45:57 +02:00
addresses . push_back ( E ) ;
2014-05-24 06:35:47 +02:00
}
return addresses ;
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > IP : : _get_local_interfaces ( ) const {
TypedArray < Dictionary > results ;
2022-05-13 15:04:37 +02:00
HashMap < String , Interface_Info > interfaces ;
2019-05-07 10:17:00 +02:00
get_local_interfaces ( & interfaces ) ;
2021-08-09 22:13:42 +02:00
for ( KeyValue < String , Interface_Info > & E : interfaces ) {
Interface_Info & c = E . value ;
2019-05-07 10:17:00 +02:00
Dictionary rc ;
rc [ " name " ] = c . name ;
rc [ " friendly " ] = c . name_friendly ;
rc [ " index " ] = c . index ;
Array ips ;
2021-07-16 05:45:57 +02:00
for ( const IPAddress & F : c . ip_addresses ) {
ips . push_front ( F ) ;
2019-05-07 10:17:00 +02:00
}
rc [ " addresses " ] = ips ;
results . push_front ( rc ) ;
}
return results ;
}
2021-05-06 02:48:18 +02:00
void IP : : get_local_addresses ( List < IPAddress > * r_addresses ) const {
2022-05-13 15:04:37 +02:00
HashMap < String , Interface_Info > interfaces ;
2019-05-07 10:17:00 +02:00
get_local_interfaces ( & interfaces ) ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < String , Interface_Info > & E : interfaces ) {
for ( const IPAddress & F : E . value . ip_addresses ) {
2021-07-16 05:45:57 +02:00
r_addresses - > push_front ( F ) ;
2019-05-07 10:17:00 +02:00
}
}
}
2014-02-10 02:10:30 +01:00
void IP : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " resolve_hostname " , " host " , " ip_type " ) , & IP : : resolve_hostname , DEFVAL ( IP : : TYPE_ANY ) ) ;
2021-05-24 15:14:51 +02:00
ClassDB : : bind_method ( D_METHOD ( " resolve_hostname_addresses " , " host " , " ip_type " ) , & IP : : resolve_hostname_addresses , DEFVAL ( IP : : TYPE_ANY ) ) ;
2017-03-05 16:44:50 +01: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 15:14:51 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_resolve_item_addresses " , " id " ) , & IP : : get_resolve_item_addresses ) ;
2017-03-05 16:44:50 +01: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 10:17:00 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_local_interfaces " ) , & IP : : _get_local_interfaces ) ;
2017-07-18 21:03:34 +02:00
ClassDB : : bind_method ( D_METHOD ( " clear_cache " , " hostname " ) , & IP : : clear_cache , DEFVAL ( " " ) ) ;
2017-03-05 16:44:50 +01:00
2017-08-20 17:45:01 +02: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 16:44:50 +01:00
BIND_CONSTANT ( RESOLVER_MAX_QUERIES ) ;
BIND_CONSTANT ( RESOLVER_INVALID_ID ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( TYPE_NONE ) ;
BIND_ENUM_CONSTANT ( TYPE_IPV4 ) ;
BIND_ENUM_CONSTANT ( TYPE_IPV6 ) ;
BIND_ENUM_CONSTANT ( TYPE_ANY ) ;
2014-02-10 02:10:30 +01:00
}
2020-04-02 01:20:12 +02:00
IP * IP : : singleton = nullptr ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
IP * IP : : get_singleton ( ) {
2014-02-10 02:10:30 +01:00
return singleton ;
}
2020-04-02 01:20:12 +02:00
IP * ( * IP : : _create ) ( ) = nullptr ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
IP * IP : : create ( ) {
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND_V_MSG ( singleton , nullptr , " IP singleton already exist. " ) ;
2023-09-09 16:11:33 +02:00
ERR_FAIL_NULL_V ( _create , nullptr ) ;
2014-02-10 02:10:30 +01:00
return _create ( ) ;
}
IP : : IP ( ) {
2017-03-05 16:44:50 +01:00
singleton = this ;
resolver = memnew ( _IP_ResolverPrivate ) ;
2014-02-10 02:10:30 +01:00
2023-02-22 22:49:48 +01:00
resolver - > thread_abort . clear ( ) ;
2021-01-19 13:29:41 +01:00
resolver - > thread . start ( _IP_ResolverPrivate : : _thread_function , resolver ) ;
2014-02-10 02:10:30 +01:00
}
IP : : ~ IP ( ) {
2023-02-22 22:49:48 +01:00
resolver - > thread_abort . set ( ) ;
2021-01-19 13:29:41 +01:00
resolver - > sem . post ( ) ;
resolver - > thread . wait_to_finish ( ) ;
2017-05-06 17:07:35 +02:00
memdelete ( resolver ) ;
2014-02-10 02:10:30 +01:00
}