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 | |
74 | static 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 | |
88 | void 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 | |
137 | void 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 | |
185 | void 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 | |
224 | void IPUnix::make_default() { |
225 | _create = _create_unix; |
226 | } |
227 | |
228 | IP *IPUnix::_create_unix() { |
229 | return memnew(IPUnix); |
230 | } |
231 | |
232 | IPUnix::IPUnix() { |
233 | } |
234 | |
235 | #endif // UNIX_ENABLED || WINDOWS_ENABLED |
236 | |