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