blob: 494dc9b16b716d419981bee6dbbab2e329b77790 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
4 * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "ip_utils.h"
22#include "logger.h"
23
24#include "connectivity/sip_utils.h"
25
26#include <sys/types.h>
27#include <unistd.h>
28#include <limits.h>
29
30#ifdef _WIN32
31#define InetPtonA inet_pton
32WINSOCK_API_LINKAGE INT WSAAPI InetPtonA(INT Family, LPCSTR pStringBuf, PVOID pAddr);
33#else
34#include <arpa/inet.h>
35#include <arpa/nameser.h>
36#include <resolv.h>
37#include <netdb.h>
38#include <netinet/ip.h>
39#include <net/if.h>
40#include <ifaddrs.h>
41#include <sys/ioctl.h>
42#endif
43
44#ifndef HOST_NAME_MAX
45#ifdef MAX_COMPUTERNAME_LENGTH
46#define HOST_NAME_MAX MAX_COMPUTERNAME_LENGTH
47#else
48// Max 255 chars as per RFC 1035
49#define HOST_NAME_MAX 255
50#endif
51#endif
52
53namespace jami {
54
55std::string_view
56sip_strerror(pj_status_t code)
57{
58 thread_local char err_msg[PJ_ERR_MSG_SIZE];
59 return as_view(pj_strerror(code, err_msg, sizeof err_msg));
60}
61
62
63std::string
64ip_utils::getHostname()
65{
66 char hostname[HOST_NAME_MAX];
67 if (gethostname(hostname, HOST_NAME_MAX))
68 return {};
69 return hostname;
70}
71
72int
73ip_utils::getHostName(char* out, size_t out_len)
74{
75 char tempstr[INET_ADDRSTRLEN];
76 const char* p = NULL;
77#ifdef _WIN32
78 struct hostent* h = NULL;
79 struct sockaddr_in localAddr;
80 memset(&localAddr, 0, sizeof(localAddr));
81 gethostname(out, out_len);
82 h = gethostbyname(out);
83 if (h != NULL) {
84 memcpy(&localAddr.sin_addr, h->h_addr_list[0], 4);
85 p = inet_ntop(AF_INET, &localAddr.sin_addr, tempstr, sizeof(tempstr));
86 if (p)
87 strncpy(out, p, out_len);
88 else
89 return -1;
90 } else {
91 return -1;
92 }
93#elif (defined(BSD) && BSD >= 199306) || defined(__FreeBSD_kernel__)
94 int retVal = 0;
95 struct ifaddrs* ifap;
96 struct ifaddrs* ifa;
97 if (getifaddrs(&ifap) != 0)
98 return -1;
99 // Cycle through available interfaces.
100 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
101 // Skip loopback, point-to-point and down interfaces.
102 // except don't skip down interfaces if we're trying to get
103 // a list of configurable interfaces.
104 if ((ifa->ifa_flags & IFF_LOOPBACK) || (!(ifa->ifa_flags & IFF_UP)))
105 continue;
106 if (ifa->ifa_addr->sa_family == AF_INET) {
107 if (((struct sockaddr_in*) (ifa->ifa_addr))->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
108 // We don't want the loopback interface. Go to next one.
109 continue;
110 }
111 p = inet_ntop(AF_INET,
112 &((struct sockaddr_in*) (ifa->ifa_addr))->sin_addr,
113 tempstr,
114 sizeof(tempstr));
115 if (p)
116 strncpy(out, p, out_len);
117 else
118 retVal = -1;
119 break;
120 }
121 }
122 freeifaddrs(ifap);
123 retVal = ifa ? 0 : -1;
124 return retVal;
125#else
126 struct ifconf ifConf;
127 struct ifreq ifReq;
128 struct sockaddr_in localAddr;
129 char szBuffer[MAX_INTERFACE * sizeof(struct ifreq)];
130 int nResult;
131 int localSock;
132 memset(&ifConf, 0, sizeof(ifConf));
133 memset(&ifReq, 0, sizeof(ifReq));
134 memset(szBuffer, 0, sizeof(szBuffer));
135 memset(&localAddr, 0, sizeof(localAddr));
136 // Create an unbound datagram socket to do the SIOCGIFADDR ioctl on.
137 localSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
138 if (localSock == INVALID_SOCKET)
139 return -1;
140 /* Get the interface configuration information... */
141 ifConf.ifc_len = (int) sizeof szBuffer;
142 ifConf.ifc_ifcu.ifcu_buf = (caddr_t) szBuffer;
143 nResult = ioctl(localSock, SIOCGIFCONF, &ifConf);
144 if (nResult < 0) {
145 close(localSock);
146 return -1;
147 }
148 unsigned int i;
149 unsigned int j = 0;
150 // Cycle through the list of interfaces looking for IP addresses.
151 for (i = 0u; i < (unsigned int) ifConf.ifc_len && j < MIN_INTERFACE;) {
152 struct ifreq* pifReq = (struct ifreq*) ((caddr_t) ifConf.ifc_req + i);
153 i += sizeof *pifReq;
154 // See if this is the sort of interface we want to deal with.
155 memset(ifReq.ifr_name, 0, sizeof(ifReq.ifr_name));
156 strncpy(ifReq.ifr_name, pifReq->ifr_name, sizeof(ifReq.ifr_name));
157 ioctl(localSock, SIOCGIFFLAGS, &ifReq);
158 // Skip loopback, point-to-point and down interfaces.
159 // except don't skip down interfaces if we're trying to get
160 // a list of configurable interfaces.
161 if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP)))
162 continue;
163 if (pifReq->ifr_addr.sa_family == AF_INET) {
164 memcpy(&localAddr, &pifReq->ifr_addr, sizeof pifReq->ifr_addr);
165 if (localAddr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
166 // We don't want the loopback interface. Go to the next one.
167 continue;
168 }
169 }
170 j++; // Increment j if we found an address which is not loopback and is up.
171 }
172 close(localSock);
173 p = inet_ntop(AF_INET, &localAddr.sin_addr, tempstr, sizeof(tempstr));
174 if (p)
175 strncpy(out, p, out_len);
176 else
177 return -1;
178#endif
179 return 0;
180}
181std::string
182ip_utils::getGateway(char* localHost, ip_utils::subnet_mask prefix)
183{
184 std::string_view localHostStr(localHost);
185 if (prefix == ip_utils::subnet_mask::prefix_32bit)
186 return std::string(localHostStr);
187 std::string defaultGw {};
188 // Make a vector of each individual number in the ip address.
189 std::vector<std::string_view> tokens = split_string(localHostStr, '.');
190 // Build a gateway address from the individual ip components.
191 for (unsigned i = 0; i <= (unsigned) prefix; i++)
192 defaultGw += tokens[i] + ".";
193 for (unsigned i = (unsigned) ip_utils::subnet_mask::prefix_32bit;
194 i > (unsigned) prefix + 1;
195 i--)
196 defaultGw += "0.";
197 defaultGw += "1";
198 return defaultGw;
199}
200
201IpAddr
202ip_utils::getLocalGateway()
203{
204 char localHostBuf[INET_ADDRSTRLEN];
205 if (ip_utils::getHostName(localHostBuf, INET_ADDRSTRLEN) < 0) {
206 JAMI_WARN("Couldn't find local host");
207 return {};
208 } else {
209 return IpAddr(ip_utils::getGateway(localHostBuf, ip_utils::subnet_mask::prefix_24bit));
210 }
211}
212
213std::vector<IpAddr>
214ip_utils::getAddrList(std::string_view name, pj_uint16_t family)
215{
216 std::vector<IpAddr> ipList;
217 if (name.empty())
218 return ipList;
219 if (IpAddr::isValid(name, family)) {
220 ipList.emplace_back(name);
221 return ipList;
222 }
223
224 static constexpr unsigned MAX_ADDR_NUM = 128;
225 pj_addrinfo res[MAX_ADDR_NUM];
226 unsigned addr_num = MAX_ADDR_NUM;
227 const pj_str_t pjname(sip_utils::CONST_PJ_STR(name));
228 auto status = pj_getaddrinfo(family, &pjname, &addr_num, res);
229 if (status != PJ_SUCCESS) {
230 JAMI_ERR("Error resolving %.*s : %s",
231 (int) name.size(),
232 name.data(),
233 sip_utils::sip_strerror(status).c_str());
234 return ipList;
235 }
236
237 for (unsigned i = 0; i < addr_num; i++) {
238 bool found = false;
239 for (const auto& ip : ipList)
240 if (!pj_sockaddr_cmp(&ip, &res[i].ai_addr)) {
241 found = true;
242 break;
243 }
244 if (!found)
245 ipList.emplace_back(res[i].ai_addr);
246 }
247
248 return ipList;
249}
250
251bool
252ip_utils::haveCommonAddr(const std::vector<IpAddr>& a, const std::vector<IpAddr>& b)
253{
254 for (const auto& i : a) {
255 for (const auto& j : b) {
256 if (i == j)
257 return true;
258 }
259 }
260 return false;
261}
262
263IpAddr
264ip_utils::getLocalAddr(pj_uint16_t family)
265{
266 IpAddr ip_addr {};
267 pj_status_t status = pj_gethostip(family, ip_addr.pjPtr());
268 if (status == PJ_SUCCESS) {
269 return ip_addr;
270 }
271 JAMI_WARN("Could not get preferred address familly (%s)",
272 (family == pj_AF_INET6()) ? "IPv6" : "IPv4");
273 family = (family == pj_AF_INET()) ? pj_AF_INET6() : pj_AF_INET();
274 status = pj_gethostip(family, ip_addr.pjPtr());
275 if (status == PJ_SUCCESS) {
276 return ip_addr;
277 }
278 JAMI_ERR("Could not get local IP");
279 return ip_addr;
280}
281
282IpAddr
283ip_utils::getInterfaceAddr(const std::string& interface, pj_uint16_t family)
284{
285 if (interface == DEFAULT_INTERFACE)
286 return getLocalAddr(family);
287
288 IpAddr addr {};
289
290#ifndef _WIN32
291 const auto unix_family = family == pj_AF_INET() ? AF_INET : AF_INET6;
292
293 int fd = socket(unix_family, SOCK_DGRAM, 0);
294 if (fd < 0) {
295 JAMI_ERR("Could not open socket: %m");
296 return addr;
297 }
298
299 if (unix_family == AF_INET6) {
300 int val = family != pj_AF_UNSPEC();
301 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*) &val, sizeof(val)) < 0) {
302 JAMI_ERR("Could not setsockopt: %m");
303 close(fd);
304 return addr;
305 }
306 }
307
308 ifreq ifr;
309 strncpy(ifr.ifr_name, interface.c_str(), sizeof ifr.ifr_name);
310 // guarantee that ifr_name is NULL-terminated
311 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
312
313 memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
314 ifr.ifr_addr.sa_family = unix_family;
315
316 ioctl(fd, SIOCGIFADDR, &ifr);
317 close(fd);
318
319 addr = ifr.ifr_addr;
320 if (addr.isUnspecified())
321 return getLocalAddr(addr.getFamily());
322#else // _WIN32
323 struct addrinfo hints;
324 struct addrinfo* result = NULL;
325 struct sockaddr_in* sockaddr_ipv4;
326 struct sockaddr_in6* sockaddr_ipv6;
327
328 ZeroMemory(&hints, sizeof(hints));
329
330 DWORD dwRetval = getaddrinfo(interface.c_str(), "0", &hints, &result);
331 if (dwRetval != 0) {
332 JAMI_ERR("getaddrinfo failed with error: %lu", dwRetval);
333 return addr;
334 }
335
336 switch (result->ai_family) {
337 sockaddr_ipv4 = (struct sockaddr_in*) result->ai_addr;
338 addr = sockaddr_ipv4->sin_addr;
339 break;
340 case AF_INET6:
341 sockaddr_ipv6 = (struct sockaddr_in6*) result->ai_addr;
342 addr = sockaddr_ipv6->sin6_addr;
343 break;
344 default:
345 break;
346 }
347
348 if (addr.isUnspecified())
349 return getLocalAddr(addr.getFamily());
350#endif // !_WIN32
351
352 return addr;
353}
354
355std::vector<std::string>
356ip_utils::getAllIpInterfaceByName()
357{
358 std::vector<std::string> ifaceList;
359 ifaceList.push_back("default");
360#ifndef _WIN32
361 static ifreq ifreqs[20];
362 ifconf ifconf;
363
364 ifconf.ifc_buf = (char*) (ifreqs);
365 ifconf.ifc_len = sizeof(ifreqs);
366
367 int sock = socket(AF_INET6, SOCK_STREAM, 0);
368
369 if (sock >= 0) {
370 if (ioctl(sock, SIOCGIFCONF, &ifconf) >= 0)
371 for (unsigned i = 0; i < ifconf.ifc_len / sizeof(ifreq); ++i)
372 ifaceList.push_back(std::string(ifreqs[i].ifr_name));
373
374 close(sock);
375 }
376
377#else
378 JAMI_ERR("Not implemented yet. (iphlpapi.h problem)");
379#endif
380 return ifaceList;
381}
382
383std::vector<std::string>
384ip_utils::getAllIpInterface()
385{
386 pj_sockaddr addrList[16];
387 unsigned addrCnt = PJ_ARRAY_SIZE(addrList);
388
389 std::vector<std::string> ifaceList;
390
391 if (pj_enum_ip_interface(pj_AF_UNSPEC(), &addrCnt, addrList) == PJ_SUCCESS) {
392 for (unsigned i = 0; i < addrCnt; i++) {
393 char addr[PJ_INET6_ADDRSTRLEN];
394 pj_sockaddr_print(&addrList[i], addr, sizeof(addr), 0);
395 ifaceList.push_back(std::string(addr));
396 }
397 }
398
399 return ifaceList;
400}
401
402std::vector<IpAddr>
403ip_utils::getLocalNameservers()
404{
405 std::vector<IpAddr> res;
406#if defined __ANDROID__ || defined _WIN32 || TARGET_OS_IPHONE
407#ifdef _MSC_VER
408#pragma message(__FILE__ "(" STR2(__LINE__) ") : -NOTE- " \
409 "Not implemented")
410#else
411#warning "Not implemented"
412#endif
413#else
414 if (not(_res.options & RES_INIT))
415 res_init();
416 res.insert(res.end(), _res.nsaddr_list, _res.nsaddr_list + _res.nscount);
417#endif
418 return res;
419}
420
421bool
422IpAddr::isValid(std::string_view address, pj_uint16_t family)
423{
424 const pj_str_t pjstring(sip_utils::CONST_PJ_STR(address));
425 pj_str_t ret_str;
426 pj_uint16_t ret_port;
427 int ret_family;
428 auto status = pj_sockaddr_parse2(pj_AF_UNSPEC(), 0, &pjstring, &ret_str, &ret_port, &ret_family);
429 if (status != PJ_SUCCESS || (family != pj_AF_UNSPEC() && ret_family != family))
430 return false;
431
432 char buf[PJ_INET6_ADDRSTRLEN];
433 pj_str_t addr_with_null = {buf, 0};
434 pj_strncpy_with_null(&addr_with_null, &ret_str, sizeof(buf));
435 struct sockaddr sa;
436 return inet_pton(ret_family == pj_AF_INET6() ? AF_INET6 : AF_INET, buf, &(sa.sa_data)) == 1;
437}
438
439bool
440IpAddr::isUnspecified() const
441{
442 switch (addr.addr.sa_family) {
443 case AF_INET:
444 return IN_IS_ADDR_UNSPECIFIED(&addr.ipv4.sin_addr);
445 case AF_INET6:
446 return IN6_IS_ADDR_UNSPECIFIED(reinterpret_cast<const in6_addr*>(&addr.ipv6.sin6_addr));
447 default:
448 return true;
449 }
450}
451
452bool
453IpAddr::isLoopback() const
454{
455 switch (addr.addr.sa_family) {
456 case AF_INET: {
457 auto addr_host = ntohl(addr.ipv4.sin_addr.s_addr);
458 uint8_t b1 = (uint8_t)(addr_host >> 24);
459 return b1 == 127;
460 }
461 case AF_INET6:
462 return IN6_IS_ADDR_LOOPBACK(reinterpret_cast<const in6_addr*>(&addr.ipv6.sin6_addr));
463 default:
464 return false;
465 }
466}
467
468bool
469IpAddr::isPrivate() const
470{
471 if (isLoopback()) {
472 return true;
473 }
474 switch (addr.addr.sa_family) {
475 case AF_INET: {
476 auto addr_host = ntohl(addr.ipv4.sin_addr.s_addr);
477 uint8_t b1, b2;
478 b1 = (uint8_t)(addr_host >> 24);
479 b2 = (uint8_t)((addr_host >> 16) & 0x0ff);
480 // 10.x.y.z
481 if (b1 == 10)
482 return true;
483 // 172.16.0.0 - 172.31.255.255
484 if ((b1 == 172) && (b2 >= 16) && (b2 <= 31))
485 return true;
486 // 192.168.0.0 - 192.168.255.255
487 if ((b1 == 192) && (b2 == 168))
488 return true;
489 return false;
490 }
491 case AF_INET6: {
492 const pj_uint8_t* addr6 = reinterpret_cast<const pj_uint8_t*>(&addr.ipv6.sin6_addr);
493 if (addr6[0] == 0xfc)
494 return true;
495 return false;
496 }
497 default:
498 return false;
499 }
500}
501} // namespace jami