1/**************************************************************************/
2/* ip_unix.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "ip_unix.h"
32
33#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
34
35#ifdef WINDOWS_ENABLED
36
37#define WIN32_LEAN_AND_MEAN
38#include <windows.h>
39#include <winsock2.h>
40#include <ws2tcpip.h>
41
42#include <iphlpapi.h>
43
44#include <stdio.h>
45
46#else // UNIX
47
48#include <netdb.h>
49
50#ifdef ANDROID_ENABLED
51// We could drop this file once we up our API level to 24,
52// where the NDK's ifaddrs.h supports to needed getifaddrs.
53#include "thirdparty/misc/ifaddrs-android.h"
54#else
55#ifdef __FreeBSD__
56#include <sys/types.h>
57#endif
58#include <ifaddrs.h>
59#endif
60
61#include <arpa/inet.h>
62#include <sys/socket.h>
63
64#ifdef __FreeBSD__
65#include <netinet/in.h>
66#endif
67
68#include <net/if.h> // Order is important on OpenBSD, leave as last.
69
70#endif // UNIX
71
72#include <string.h>
73
74static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
75 IPAddress ip;
76
77 if (p_addr->sa_family == AF_INET) {
78 struct sockaddr_in *addr = (struct sockaddr_in *)p_addr;
79 ip.set_ipv4((uint8_t *)&(addr->sin_addr));
80 } else if (p_addr->sa_family == AF_INET6) {
81 struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
82 ip.set_ipv6(addr6->sin6_addr.s6_addr);
83 }
84
85 return ip;
86}
87
88void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const {
89 struct addrinfo hints;
90 struct addrinfo *result = nullptr;
91
92 memset(&hints, 0, sizeof(struct addrinfo));
93 if (p_type == TYPE_IPV4) {
94 hints.ai_family = AF_INET;
95 } else if (p_type == TYPE_IPV6) {
96 hints.ai_family = AF_INET6;
97 hints.ai_flags = 0;
98 } else {
99 hints.ai_family = AF_UNSPEC;
100 hints.ai_flags = AI_ADDRCONFIG;
101 }
102 hints.ai_flags &= ~AI_NUMERICHOST;
103
104 int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result);
105 if (s != 0) {
106 print_verbose("getaddrinfo failed! Cannot resolve hostname.");
107 return;
108 }
109
110 if (result == nullptr || result->ai_addr == nullptr) {
111 print_verbose("Invalid response from getaddrinfo");
112 if (result) {
113 freeaddrinfo(result);
114 }
115 return;
116 }
117
118 struct addrinfo *next = result;
119
120 do {
121 if (next->ai_addr == nullptr) {
122 next = next->ai_next;
123 continue;
124 }
125 IPAddress ip = _sockaddr2ip(next->ai_addr);
126 if (ip.is_valid() && !r_addresses.find(ip)) {
127 r_addresses.push_back(ip);
128 }
129 next = next->ai_next;
130 } while (next);
131
132 freeaddrinfo(result);
133}
134
135#if defined(WINDOWS_ENABLED)
136
137void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
138 ULONG buf_size = 1024;
139 IP_ADAPTER_ADDRESSES *addrs;
140
141 while (true) {
142 addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
143 int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
144 nullptr, addrs, &buf_size);
145 if (err == NO_ERROR) {
146 break;
147 }
148 memfree(addrs);
149 if (err == ERROR_BUFFER_OVERFLOW) {
150 continue; // will go back and alloc the right size
151 }
152
153 ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + ".");
154 }
155
156 IP_ADAPTER_ADDRESSES *adapter = addrs;
157
158 while (adapter != nullptr) {
159 Interface_Info info;
160 info.name = adapter->AdapterName;
161 info.name_friendly = adapter->FriendlyName;
162 info.index = String::num_uint64(adapter->IfIndex);
163
164 IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
165 while (address != nullptr) {
166 int family = address->Address.lpSockaddr->sa_family;
167 if (family != AF_INET && family != AF_INET6) {
168 continue;
169 }
170 info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr));
171 address = address->Next;
172 }
173 adapter = adapter->Next;
174 // Only add interface if it has at least one IP
175 if (info.ip_addresses.size() > 0) {
176 r_interfaces->insert(info.name, info);
177 }
178 }
179
180 memfree(addrs);
181}
182
183#else // UNIX
184
185void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
186 struct ifaddrs *ifAddrStruct = nullptr;
187 struct ifaddrs *ifa = nullptr;
188 int family;
189
190 getifaddrs(&ifAddrStruct);
191
192 for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next) {
193 if (!ifa->ifa_addr) {
194 continue;
195 }
196
197 family = ifa->ifa_addr->sa_family;
198
199 if (family != AF_INET && family != AF_INET6) {
200 continue;
201 }
202
203 HashMap<String, Interface_Info>::Iterator E = r_interfaces->find(ifa->ifa_name);
204 if (!E) {
205 Interface_Info info;
206 info.name = ifa->ifa_name;
207 info.name_friendly = ifa->ifa_name;
208 info.index = String::num_uint64(if_nametoindex(ifa->ifa_name));
209 E = r_interfaces->insert(ifa->ifa_name, info);
210 ERR_CONTINUE(!E);
211 }
212
213 Interface_Info &info = E->value;
214 info.ip_addresses.push_front(_sockaddr2ip(ifa->ifa_addr));
215 }
216
217 if (ifAddrStruct != nullptr) {
218 freeifaddrs(ifAddrStruct);
219 }
220}
221
222#endif // UNIX
223
224void IPUnix::make_default() {
225 _create = _create_unix;
226}
227
228IP *IPUnix::_create_unix() {
229 return memnew(IPUnix);
230}
231
232IPUnix::IPUnix() {
233}
234
235#endif // UNIX_ENABLED || WINDOWS_ENABLED
236