diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 420e48f839e..3d87131b51c 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -234,6 +234,41 @@ Array IP::_get_local_addresses() const { return addresses; } +Array IP::_get_local_interfaces() const { + + Array results; + Map interfaces; + get_local_interfaces(&interfaces); + for (Map::Element *E = interfaces.front(); E; E = E->next()) { + Interface_Info &c = E->get(); + Dictionary rc; + rc["name"] = c.name; + rc["friendly"] = c.name_friendly; + rc["index"] = c.index; + + Array ips; + for (const List::Element *F = c.ip_addresses.front(); F; F = F->next()) { + ips.push_front(F->get()); + } + rc["addresses"] = ips; + + results.push_front(rc); + } + + return results; +} + +void IP::get_local_addresses(List *r_addresses) const { + + Map interfaces; + get_local_interfaces(&interfaces); + for (Map::Element *E = interfaces.front(); E; E = E->next()) { + for (const List::Element *F = E->get().ip_addresses.front(); F; F = F->next()) { + r_addresses->push_front(F->get()); + } + } +} + void IP::_bind_methods() { ClassDB::bind_method(D_METHOD("resolve_hostname", "host", "ip_type"), &IP::resolve_hostname, DEFVAL(IP::TYPE_ANY)); @@ -242,6 +277,7 @@ void IP::_bind_methods() { ClassDB::bind_method(D_METHOD("get_resolve_item_address", "id"), &IP::get_resolve_item_address); 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); ClassDB::bind_method(D_METHOD("clear_cache", "hostname"), &IP::clear_cache, DEFVAL("")); BIND_ENUM_CONSTANT(RESOLVER_STATUS_NONE); diff --git a/core/io/ip.h b/core/io/ip.h index ead71ebb54b..59b18ef9868 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -73,16 +73,25 @@ protected: virtual IP_Address _resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY) = 0; Array _get_local_addresses() const; + Array _get_local_interfaces() const; static IP *(*_create)(); public: + struct Interface_Info { + String name; + String name_friendly; + String index; + List ip_addresses; + }; + IP_Address resolve_hostname(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; IP_Address get_resolve_item_address(ResolverID p_id) const; - virtual void get_local_addresses(List *r_addresses) const = 0; + virtual void get_local_addresses(List *r_addresses) const; + virtual void get_local_interfaces(Map *r_interfaces) const = 0; void erase_resolve_item(ResolverID p_id); void clear_cache(const String &p_hostname = ""); diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index 763a5fbb9ac..9305afac5fa 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -40,6 +40,9 @@ IP_Address::operator Variant() const { IP_Address::operator String() const { + if (wildcard) + return "*"; + if (!valid) return ""; diff --git a/doc/classes/IP.xml b/doc/classes/IP.xml index 3616a9ec50d..65b17003335 100644 --- a/doc/classes/IP.xml +++ b/doc/classes/IP.xml @@ -34,6 +34,22 @@ Returns all of the user's current IPv4 and IPv6 addresses as an array. + + + + + Returns all network adapters as an array. + Each adapter is a dictionary of the form: + [codeblock] + { + "index": "1", # Interface index. + "name": "eth0", # Interface name. + "friendly": "Ethernet One", # A friendly name (might be empty). + "addresses": ["192.168.1.101"], # An array of IP addresses associated to this interface. + } + [/codeblock] + + diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index b5feaabc328..39ca9a823e4 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -56,6 +56,7 @@ #endif // MINGW hack #endif #else // UNIX +#include #include #ifdef ANDROID_ENABLED // We could drop this file once we up our API level to 24, @@ -77,6 +78,7 @@ static IP_Address _sockaddr2ip(struct sockaddr *p_addr) { IP_Address ip; + if (p_addr->sa_family == AF_INET) { struct sockaddr_in *addr = (struct sockaddr_in *)p_addr; ip.set_ipv4((uint8_t *)&(addr->sin_addr)); @@ -129,24 +131,42 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { #if defined(UWP_ENABLED) -void IP_Unix::get_local_addresses(List *r_addresses) const { +void IP_Unix::get_local_interfaces(Map *r_interfaces) const { using namespace Windows::Networking; using namespace Windows::Networking::Connectivity; + // Returns addresses, not interfaces. auto hostnames = NetworkInformation::GetHostNames(); for (int i = 0; i < hostnames->Size; i++) { - if (hostnames->GetAt(i)->Type == HostNameType::Ipv4 || hostnames->GetAt(i)->Type == HostNameType::Ipv6 && hostnames->GetAt(i)->IPInformation != nullptr) { + auto hostname = hostnames->GetAt(i); - r_addresses->push_back(IP_Address(String(hostnames->GetAt(i)->CanonicalName->Data()))); + if (hostname->Type != HostNameType::Ipv4 && hostname->Type != HostNameType::Ipv6) + continue; + + String name = hostname->RawName->Data(); + Map::Element *E = r_interfaces->find(name); + if (!E) { + Interface_Info info; + info.name = name; + info.name_friendly = hostname->DisplayName->Data(); + info.index = 0; + E = r_interfaces->insert(name, info); + ERR_CONTINUE(!E); } + + Interface_Info &info = E->get(); + + IP_Address ip = IP_Address(hostname->CanonicalName->Data()); + info.ip_addresses.push_front(ip); } -}; +} + #else -void IP_Unix::get_local_addresses(List *r_addresses) const { +void IP_Unix::get_local_interfaces(Map *r_interfaces) const { ULONG buf_size = 1024; IP_ADAPTER_ADDRESSES *addrs; @@ -173,29 +193,23 @@ void IP_Unix::get_local_addresses(List *r_addresses) const { while (adapter != NULL) { + Interface_Info info; + info.name = adapter->AdapterName; + info.name_friendly = adapter->FriendlyName; + info.index = String::num_uint64(adapter->IfIndex); + IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress; while (address != NULL) { - - IP_Address ip; - - if (address->Address.lpSockaddr->sa_family == AF_INET) { - - SOCKADDR_IN *ipv4 = reinterpret_cast(address->Address.lpSockaddr); - - ip.set_ipv4((uint8_t *)&(ipv4->sin_addr)); - r_addresses->push_back(ip); - - } else if (address->Address.lpSockaddr->sa_family == AF_INET6) { // ipv6 - - SOCKADDR_IN6 *ipv6 = reinterpret_cast(address->Address.lpSockaddr); - - ip.set_ipv6(ipv6->sin6_addr.s6_addr); - r_addresses->push_back(ip); - }; - + int family = address->Address.lpSockaddr->sa_family; + if (family != AF_INET && family != AF_INET6) + continue; + info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr)); address = address->Next; - }; + } adapter = adapter->Next; + // Only add interface if it has at least one IP + if (info.ip_addresses.size() > 0) + r_interfaces->insert(info.name, info); }; memfree(addrs); @@ -205,7 +219,7 @@ void IP_Unix::get_local_addresses(List *r_addresses) const { #else // UNIX -void IP_Unix::get_local_addresses(List *r_addresses) const { +void IP_Unix::get_local_interfaces(Map *r_interfaces) const { struct ifaddrs *ifAddrStruct = NULL; struct ifaddrs *ifa = NULL; @@ -222,8 +236,18 @@ void IP_Unix::get_local_addresses(List *r_addresses) const { if (family != AF_INET && family != AF_INET6) continue; - IP_Address ip = _sockaddr2ip(ifa->ifa_addr); - r_addresses->push_back(ip); + Map::Element *E = r_interfaces->find(ifa->ifa_name); + if (!E) { + Interface_Info info; + info.name = ifa->ifa_name; + info.name_friendly = ifa->ifa_name; + info.index = String::num_uint64(if_nametoindex(ifa->ifa_name)); + E = r_interfaces->insert(ifa->ifa_name, info); + ERR_CONTINUE(!E); + } + + Interface_Info &info = E->get(); + info.ip_addresses.push_front(_sockaddr2ip(ifa->ifa_addr)); } if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index e36535146e2..f9bb626cc44 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -43,7 +43,7 @@ class IP_Unix : public IP { static IP *_create_unix(); public: - virtual void get_local_addresses(List *r_addresses) const; + virtual void get_local_interfaces(Map *r_interfaces) const; static void make_default(); IP_Unix();