1 /*
2 Copyright (c) 2007, Adobe Systems, Incorporated
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 * Neither the name of Adobe Systems, Network Resonance nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <csi_platform.h>
34 #include <assert.h>
35 #include <string.h>
36
37 #ifdef WIN32
38 #include <winsock2.h>
39 #include <iphlpapi.h>
40 #include <tchar.h>
41 #else /* !WIN32 */
42
43 #include <sys/socket.h>
44 #include <sys/ioctl.h>
45 #include <errno.h>
46
47 #ifndef ANDROID
48 /* This works on linux and BSD, but not android */
49 #include <sys/types.h> /* getifaddrs */
50 #include <ifaddrs.h> /* getifaddrs */
51 #else
52 #include "ifaddrs-android.h"
53 #define getifaddrs android_getifaddrs
54 #define freeifaddrs android_freeifaddrs
55 #endif
56
57 #ifdef LINUX
58
59 #ifdef ANDROID
60 /* Work around an Android NDK < r8c bug */
61 #undef __unused
62 #else
63 #include <linux/if.h> /* struct ifreq, IFF_POINTTOPOINT */
64 #include <linux/wireless.h> /* struct iwreq */
65 #include <linux/ethtool.h> /* struct ethtool_cmd */
66 #include <linux/sockios.h> /* SIOCETHTOOL */
67 #endif /* ANDROID */
68
69 #endif /* LINUX */
70
71 #endif /* !WIN32 */
72
73 #include "stun.h"
74 #include "addrs.h"
75 #include "nr_crypto.h"
76 #include "util.h"
77
78 #if defined(WIN32)
79
80 #define WIN32_MAX_NUM_INTERFACES 20
81
82 #define NR_MD5_HASH_LENGTH 16
83
84 #define _NR_MAX_KEY_LENGTH 256
85 #define _NR_MAX_NAME_LENGTH 512
86
87 #define _ADAPTERS_BASE_REG "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
88
nr_win32_get_adapter_friendly_name(char * adapter_GUID,char ** friendly_name)89 static int nr_win32_get_adapter_friendly_name(char *adapter_GUID, char **friendly_name)
90 {
91 int r,_status;
92 HKEY adapter_reg;
93 TCHAR adapter_key[_NR_MAX_KEY_LENGTH];
94 TCHAR keyval_buf[_NR_MAX_KEY_LENGTH];
95 TCHAR adapter_GUID_tchar[_NR_MAX_NAME_LENGTH];
96 DWORD keyval_len, key_type;
97 size_t converted_chars, newlen;
98 char *my_fn = 0;
99
100 #ifdef _UNICODE
101 mbstowcs_s(&converted_chars, adapter_GUID_tchar, strlen(adapter_GUID)+1,
102 adapter_GUID, _TRUNCATE);
103 #else
104 strlcpy(adapter_GUID_tchar, adapter_GUID, _NR_MAX_NAME_LENGTH);
105 #endif
106
107 _tcscpy_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT(_ADAPTERS_BASE_REG));
108 _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\"));
109 _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, adapter_GUID_tchar);
110 _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\Connection"));
111
112 r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, adapter_key, 0, KEY_READ, &adapter_reg);
113
114 if (r != ERROR_SUCCESS) {
115 r_log(NR_LOG_STUN, LOG_ERR, "Got error %d opening adapter reg key\n", r);
116 ABORT(R_INTERNAL);
117 }
118
119 keyval_len = sizeof(keyval_buf);
120 r = RegQueryValueEx(adapter_reg, TEXT("Name"), NULL, &key_type,
121 (BYTE *)keyval_buf, &keyval_len);
122
123 RegCloseKey(adapter_reg);
124
125 #ifdef UNICODE
126 newlen = wcslen(keyval_buf)+1;
127 my_fn = (char *) RCALLOC(newlen);
128 if (!my_fn) {
129 ABORT(R_NO_MEMORY);
130 }
131 wcstombs_s(&converted_chars, my_fn, newlen, keyval_buf, _TRUNCATE);
132 #else
133 my_fn = r_strdup(keyval_buf);
134 #endif
135
136 *friendly_name = my_fn;
137 _status=0;
138
139 abort:
140 if (_status) {
141 if (my_fn) free(my_fn);
142 }
143 return(_status);
144 }
145
146 static int
stun_get_win32_addrs(nr_local_addr addrs[],int maxaddrs,int * count)147 stun_get_win32_addrs(nr_local_addr addrs[], int maxaddrs, int *count)
148 {
149 int r, _status;
150 PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL, tmpAddress = NULL;
151 // recomended per https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx
152 static const ULONG initialBufLen = 15000;
153 ULONG buflen = initialBufLen;
154 char bin_hashed_ifname[NR_MD5_HASH_LENGTH];
155 char hex_hashed_ifname[MAXIFNAME];
156 int n = 0;
157
158 *count = 0;
159
160 if (maxaddrs <= 0)
161 ABORT(R_BAD_ARGS);
162
163 /* According to MSDN (see above) we have try GetAdapterAddresses() multiple times */
164 for (n = 0; n < 5; n++) {
165 AdapterAddresses = (PIP_ADAPTER_ADDRESSES) RMALLOC(buflen);
166 if (AdapterAddresses == NULL) {
167 r_log(NR_LOG_STUN, LOG_ERR, "Error allocating buf for GetAdaptersAddresses()");
168 ABORT(R_NO_MEMORY);
169 }
170
171 r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, AdapterAddresses, &buflen);
172 if (r == NO_ERROR) {
173 break;
174 }
175 r_log(NR_LOG_STUN, LOG_ERR, "GetAdaptersAddresses() returned error (%d)", r);
176 RFREE(AdapterAddresses);
177 AdapterAddresses = NULL;
178 }
179
180 if (n >= 5) {
181 r_log(NR_LOG_STUN, LOG_ERR, "5 failures calling GetAdaptersAddresses()");
182 ABORT(R_INTERNAL);
183 }
184
185 n = 0;
186
187 /* Loop through the adapters */
188
189 for (tmpAddress = AdapterAddresses; tmpAddress != NULL; tmpAddress = tmpAddress->Next) {
190
191 if (tmpAddress->OperStatus != IfOperStatusUp)
192 continue;
193
194 if ((tmpAddress->IfIndex != 0) || (tmpAddress->Ipv6IfIndex != 0)) {
195 IP_ADAPTER_UNICAST_ADDRESS *u = 0;
196
197 if(r=nr_crypto_md5((UCHAR *)tmpAddress->FriendlyName,
198 wcslen(tmpAddress->FriendlyName) * sizeof(wchar_t),
199 bin_hashed_ifname))
200 ABORT(r);
201 if(r=nr_bin2hex(bin_hashed_ifname, sizeof(bin_hashed_ifname),
202 hex_hashed_ifname))
203 ABORT(r);
204
205 for (u = tmpAddress->FirstUnicastAddress; u != 0; u = u->Next) {
206 SOCKET_ADDRESS *sa_addr = &u->Address;
207
208 if ((sa_addr->lpSockaddr->sa_family == AF_INET) ||
209 (sa_addr->lpSockaddr->sa_family == AF_INET6)) {
210 if ((r=nr_sockaddr_to_transport_addr((struct sockaddr*)sa_addr->lpSockaddr, IPPROTO_UDP, 0, &(addrs[n].addr))))
211 ABORT(r);
212 }
213 else {
214 r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized sa_family for address on adapter %lu", tmpAddress->IfIndex);
215 continue;
216 }
217
218 strlcpy(addrs[n].addr.ifname, hex_hashed_ifname, sizeof(addrs[n].addr.ifname));
219 if (tmpAddress->IfType == IF_TYPE_ETHERNET_CSMACD) {
220 addrs[n].interface.type = NR_INTERFACE_TYPE_WIRED;
221 } else if (tmpAddress->IfType == IF_TYPE_IEEE80211) {
222 /* Note: this only works for >= Win Vista */
223 addrs[n].interface.type = NR_INTERFACE_TYPE_WIFI;
224 } else {
225 addrs[n].interface.type = NR_INTERFACE_TYPE_UNKNOWN;
226 }
227 #if (_WIN32_WINNT >= 0x0600)
228 /* Note: only >= Vista provide link speed information */
229 addrs[n].interface.estimated_speed = tmpAddress->TransmitLinkSpeed / 1000;
230 #else
231 addrs[n].interface.estimated_speed = 0;
232 #endif
233 if (++n >= maxaddrs)
234 goto done;
235 }
236 }
237 }
238
239 done:
240 *count = n;
241 _status = 0;
242
243 abort:
244 RFREE(AdapterAddresses);
245 return _status;
246 }
247
248 #else /* WIN32 */
249
250 static int
251 nr_stun_is_duplicate_addr(nr_local_addr addrs[], int count, nr_local_addr *addr);
252
253 static int
stun_getifaddrs(nr_local_addr addrs[],int maxaddrs,int * count)254 stun_getifaddrs(nr_local_addr addrs[], int maxaddrs, int *count)
255 {
256 int r,_status;
257 struct ifaddrs* if_addrs_head=NULL;
258 struct ifaddrs* if_addr;
259
260 *count = 0;
261
262 if (maxaddrs <= 0)
263 ABORT(R_BAD_ARGS);
264
265 if (getifaddrs(&if_addrs_head) == -1) {
266 r_log(NR_LOG_STUN, LOG_ERR, "getifaddrs error e = %d", errno);
267 ABORT(R_INTERNAL);
268 }
269
270 if_addr = if_addrs_head;
271
272 while (if_addr && *count < maxaddrs) {
273 /* This can be null */
274 if (if_addr->ifa_addr) {
275 switch (if_addr->ifa_addr->sa_family) {
276 case AF_INET:
277 case AF_INET6:
278 if (r=nr_sockaddr_to_transport_addr(if_addr->ifa_addr, IPPROTO_UDP, 0, &(addrs[*count].addr))) {
279 r_log(NR_LOG_STUN, LOG_ERR, "nr_sockaddr_to_transport_addr error r = %d", r);
280 } else {
281 #if defined(LINUX) && !defined(ANDROID)
282 struct ethtool_cmd ecmd;
283 struct ifreq ifr;
284 struct iwreq wrq;
285 int e;
286 int s = socket(AF_INET, SOCK_DGRAM, 0);
287
288 strncpy(ifr.ifr_name, if_addr->ifa_name, sizeof(ifr.ifr_name));
289 /* TODO (Bug 896851): interface property for Android */
290 /* Getting ethtool for ethernet information. */
291 ecmd.cmd = ETHTOOL_GSET;
292 /* In/out param */
293 ifr.ifr_data = (void*)&ecmd;
294
295 e = ioctl(s, SIOCETHTOOL, &ifr);
296 if (e == 0)
297 {
298 /* For wireless network, we won't get ethtool, it's a wired
299 * connection */
300 addrs[*count].interface.type = NR_INTERFACE_TYPE_WIRED;
301 #ifdef DONT_HAVE_ETHTOOL_SPEED_HI
302 addrs[*count].interface.estimated_speed = ecmd.speed;
303 #else
304 addrs[*count].interface.estimated_speed = ((ecmd.speed_hi << 16) | ecmd.speed) * 1000;
305 #endif
306 }
307
308 strncpy(wrq.ifr_name, if_addr->ifa_name, sizeof(wrq.ifr_name));
309 e = ioctl(s, SIOCGIWRATE, &wrq);
310 if (e == 0)
311 {
312 addrs[*count].interface.type = NR_INTERFACE_TYPE_WIFI;
313 addrs[*count].interface.estimated_speed = wrq.u.bitrate.value / 1000;
314 }
315
316 close(s);
317
318 if (if_addr->ifa_flags & IFF_POINTOPOINT)
319 {
320 addrs[*count].interface.type = NR_INTERFACE_TYPE_UNKNOWN | NR_INTERFACE_TYPE_VPN;
321 /* TODO (Bug 896913): find backend network type of this VPN */
322 }
323 #else
324 addrs[*count].interface.type = NR_INTERFACE_TYPE_UNKNOWN;
325 addrs[*count].interface.estimated_speed = 0;
326 #endif
327 (void)strlcpy(addrs[*count].addr.ifname, if_addr->ifa_name, sizeof(addrs[*count].addr.ifname));
328 ++(*count);
329 }
330 break;
331 default:
332 ;
333 }
334 }
335
336 if_addr = if_addr->ifa_next;
337 }
338
339 _status=0;
340 abort:
341 if (if_addrs_head) {
342 freeifaddrs(if_addrs_head);
343 }
344 return(_status);
345 }
346
347 #endif
348
349 static int
nr_stun_is_duplicate_addr(nr_local_addr addrs[],int count,nr_local_addr * addr)350 nr_stun_is_duplicate_addr(nr_local_addr addrs[], int count, nr_local_addr *addr)
351 {
352 int i;
353 int different;
354
355 for (i = 0; i < count; ++i) {
356 different = nr_transport_addr_cmp(&addrs[i].addr, &(addr->addr),
357 NR_TRANSPORT_ADDR_CMP_MODE_ALL);
358 if (!different)
359 return 1; /* duplicate */
360 }
361
362 return 0;
363 }
364
365 int
nr_stun_remove_duplicate_addrs(nr_local_addr addrs[],int remove_loopback,int remove_link_local,int * count)366 nr_stun_remove_duplicate_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count)
367 {
368 int r, _status;
369 nr_local_addr *tmp = 0;
370 int i;
371 int n;
372 int contains_regular_ipv6 = 0;
373
374 tmp = RMALLOC(*count * sizeof(*tmp));
375 if (!tmp)
376 ABORT(R_NO_MEMORY);
377
378 for (i = 0; i < *count; ++i) {
379 if (nr_transport_addr_is_teredo(&addrs[i].addr)) {
380 addrs[i].interface.type |= NR_INTERFACE_TYPE_TEREDO;
381 }
382 else if (addrs[i].addr.ip_version == NR_IPV6 &&
383 !nr_transport_addr_is_mac_based(&addrs[i].addr)) {
384 contains_regular_ipv6 = 1;
385 }
386 }
387
388 n = 0;
389 for (i = 0; i < *count; ++i) {
390 if (nr_stun_is_duplicate_addr(tmp, n, &addrs[i])) {
391 /* skip addrs[i], it's a duplicate */
392 }
393 else if (remove_loopback && nr_transport_addr_is_loopback(&addrs[i].addr)) {
394 /* skip addrs[i], it's a loopback */
395 }
396 else if (remove_link_local &&
397 nr_transport_addr_is_link_local(&addrs[i].addr)) {
398 /* skip addrs[i], it's a link-local address */
399 }
400 else if (contains_regular_ipv6 &&
401 nr_transport_addr_is_mac_based(&addrs[i].addr)) {
402 /* skip addrs[i], it's MAC based */
403 }
404 else if (contains_regular_ipv6 &&
405 nr_transport_addr_is_teredo(&addrs[i].addr)) {
406 /* skip addrs[i], it's a Teredo address */
407 }
408 else {
409 /* otherwise, copy it to the temporary array */
410 if ((r=nr_local_addr_copy(&tmp[n], &addrs[i])))
411 ABORT(r);
412 ++n;
413 }
414 }
415
416 *count = n;
417
418 memset(addrs, 0, *count * sizeof(*addrs));
419 /* copy temporary array into passed in/out array */
420 for (i = 0; i < *count; ++i) {
421 if ((r=nr_local_addr_copy(&addrs[i], &tmp[i])))
422 ABORT(r);
423 }
424
425 _status = 0;
426 abort:
427 RFREE(tmp);
428 return _status;
429 }
430
431 #ifndef USE_PLATFORM_NR_STUN_GET_ADDRS
432
433 int
nr_stun_get_addrs(nr_local_addr addrs[],int maxaddrs,int * count)434 nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int *count)
435 {
436 int _status=0;
437 int i;
438 char typestr[100];
439
440 // Ensure output records are always fully defined. See bug 1589990.
441 if (maxaddrs > 0) {
442 memset(addrs, 0, maxaddrs * sizeof(nr_local_addr));
443 }
444
445 #ifdef WIN32
446 _status = stun_get_win32_addrs(addrs, maxaddrs, count);
447 #else
448 _status = stun_getifaddrs(addrs, maxaddrs, count);
449 #endif
450
451 for (i = 0; i < *count; ++i) {
452 nr_local_addr_fmt_info_string(addrs+i,typestr,sizeof(typestr));
453 r_log(NR_LOG_STUN, LOG_DEBUG, "Address %d: %s on %s, type: %s\n",
454 i,addrs[i].addr.as_string,addrs[i].addr.ifname,typestr);
455 }
456
457 return _status;
458 }
459
460 #endif
461