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