blob: ad031230b54259b1b16f697d9d94c24b26a404a3 [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"
Adrien Béraud80c14e12023-07-18 16:13:15 -040018#include "sip_utils.h"
19#include "string_utils.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040020
Adrien Béraud80c14e12023-07-18 16:13:15 -040021#include <fmt/format.h>
Adrien Béraud612b55b2023-05-29 10:42:04 -040022
Adrien Béraud4822fa02023-07-30 00:21:05 -040023extern "C" {
Adrien Béraud612b55b2023-05-29 10:42:04 -040024#include <sys/types.h>
25#include <unistd.h>
26#include <limits.h>
27
28#ifdef _WIN32
29#define InetPtonA inet_pton
30WINSOCK_API_LINKAGE INT WSAAPI InetPtonA(INT Family, LPCSTR pStringBuf, PVOID pAddr);
31#else
32#include <arpa/inet.h>
33#include <arpa/nameser.h>
34#include <resolv.h>
35#include <netdb.h>
36#include <netinet/ip.h>
37#include <net/if.h>
38#include <ifaddrs.h>
39#include <sys/ioctl.h>
40#endif
Adrien Béraud4822fa02023-07-30 00:21:05 -040041}
Adrien Béraud612b55b2023-05-29 10:42:04 -040042
43#ifndef HOST_NAME_MAX
44#ifdef MAX_COMPUTERNAME_LENGTH
45#define HOST_NAME_MAX MAX_COMPUTERNAME_LENGTH
46#else
47// Max 255 chars as per RFC 1035
48#define HOST_NAME_MAX 255
49#endif
50#endif
51
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040052namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040053
Adrien Béraud9ed4dd92023-07-19 00:13:05 -040054namespace sip_utils {
Adrien Béraud612b55b2023-05-29 10:42:04 -040055std::string_view
56sip_strerror(pj_status_t code)
57{
58 thread_local char err_msg[PJ_ERR_MSG_SIZE];
Adrien Béraud80c14e12023-07-18 16:13:15 -040059 return sip_utils::as_view(pj_strerror(code, err_msg, sizeof err_msg));
Adrien Béraud612b55b2023-05-29 10:42:04 -040060}
Adrien Béraud9ed4dd92023-07-19 00:13:05 -040061}
Adrien Béraud612b55b2023-05-29 10:42:04 -040062
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
Adrien Béraud63a1fac2023-08-23 09:31:17 -040072
73ip_utils::IpInterfaceAddress
74ip_utils::getHostName()
Adrien Béraud612b55b2023-05-29 10:42:04 -040075{
Adrien Béraud63a1fac2023-08-23 09:31:17 -040076 IpInterfaceAddress ret;
Adrien Béraud612b55b2023-05-29 10:42:04 -040077 char tempstr[INET_ADDRSTRLEN];
78 const char* p = NULL;
79#ifdef _WIN32
80 struct hostent* h = NULL;
81 struct sockaddr_in localAddr;
82 memset(&localAddr, 0, sizeof(localAddr));
83 gethostname(out, out_len);
84 h = gethostbyname(out);
85 if (h != NULL) {
86 memcpy(&localAddr.sin_addr, h->h_addr_list[0], 4);
87 p = inet_ntop(AF_INET, &localAddr.sin_addr, tempstr, sizeof(tempstr));
88 if (p)
Adrien Béraud63a1fac2023-08-23 09:31:17 -040089 ret.address = p;
Adrien Béraud612b55b2023-05-29 10:42:04 -040090 }
91#elif (defined(BSD) && BSD >= 199306) || defined(__FreeBSD_kernel__)
Adrien Béraud612b55b2023-05-29 10:42:04 -040092 struct ifaddrs* ifap;
93 struct ifaddrs* ifa;
94 if (getifaddrs(&ifap) != 0)
Adrien Béraud63a1fac2023-08-23 09:31:17 -040095 return {};
Adrien Béraud612b55b2023-05-29 10:42:04 -040096 // Cycle through available interfaces.
97 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
98 // Skip loopback, point-to-point and down interfaces.
99 // except don't skip down interfaces if we're trying to get
100 // a list of configurable interfaces.
101 if ((ifa->ifa_flags & IFF_LOOPBACK) || (!(ifa->ifa_flags & IFF_UP)))
102 continue;
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400103 auto family = ifa->ifa_addr->sa_family;
104 if (family == AF_INET) {
105 void* addr;
106 if (family == AF_INET) {
107 if (((struct sockaddr_in*) (ifa->ifa_addr))->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
108 continue;
109 addr = &((struct sockaddr_in*) (ifa->ifa_addr))->sin_addr;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400110 }
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400111
112 ret.interface = ifa->ifa_name;
113 p = inet_ntop(family,
114 addr,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400115 tempstr,
116 sizeof(tempstr));
117 if (p)
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400118 ret.address = p;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400119 break;
120 }
121 }
122 freeifaddrs(ifap);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400123#else
124 struct ifconf ifConf;
125 struct ifreq ifReq;
126 struct sockaddr_in localAddr;
127 char szBuffer[MAX_INTERFACE * sizeof(struct ifreq)];
128 int nResult;
129 int localSock;
130 memset(&ifConf, 0, sizeof(ifConf));
131 memset(&ifReq, 0, sizeof(ifReq));
132 memset(szBuffer, 0, sizeof(szBuffer));
133 memset(&localAddr, 0, sizeof(localAddr));
134 // Create an unbound datagram socket to do the SIOCGIFADDR ioctl on.
135 localSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
136 if (localSock == INVALID_SOCKET)
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400137 return ret;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400138 /* Get the interface configuration information... */
139 ifConf.ifc_len = (int) sizeof szBuffer;
140 ifConf.ifc_ifcu.ifcu_buf = (caddr_t) szBuffer;
141 nResult = ioctl(localSock, SIOCGIFCONF, &ifConf);
142 if (nResult < 0) {
143 close(localSock);
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400144 return ret;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400145 }
146 unsigned int i;
147 unsigned int j = 0;
148 // Cycle through the list of interfaces looking for IP addresses.
149 for (i = 0u; i < (unsigned int) ifConf.ifc_len && j < MIN_INTERFACE;) {
150 struct ifreq* pifReq = (struct ifreq*) ((caddr_t) ifConf.ifc_req + i);
151 i += sizeof *pifReq;
152 // See if this is the sort of interface we want to deal with.
153 memset(ifReq.ifr_name, 0, sizeof(ifReq.ifr_name));
154 strncpy(ifReq.ifr_name, pifReq->ifr_name, sizeof(ifReq.ifr_name));
155 ioctl(localSock, SIOCGIFFLAGS, &ifReq);
156 // Skip loopback, point-to-point and down interfaces.
157 // except don't skip down interfaces if we're trying to get
158 // a list of configurable interfaces.
159 if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP)))
160 continue;
161 if (pifReq->ifr_addr.sa_family == AF_INET) {
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400162 if (((sockaddr_in*)&pifReq->ifr_addr)->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400163 // We don't want the loopback interface. Go to the next one.
164 continue;
165 }
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400166 ret.interface = pifReq->ifr_name;
167 p = inet_ntop(pifReq->ifr_addr.sa_family,
Adrien Beraud64bb00f2023-08-23 19:06:46 -0400168 &((sockaddr_in*)&pifReq->ifr_addr)->sin_addr,
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400169 tempstr,
170 sizeof(tempstr));
171 if (p)
172 ret.address = p;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400173 }
174 j++; // Increment j if we found an address which is not loopback and is up.
175 }
176 close(localSock);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400177#endif
Adrien Beraud64bb00f2023-08-23 19:06:46 -0400178 return ret;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400179}
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400180
Adrien Béraud612b55b2023-05-29 10:42:04 -0400181std::string
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400182ip_utils::getGateway(std::string_view localHost, ip_utils::subnet_mask prefix)
Adrien Béraud612b55b2023-05-29 10:42:04 -0400183{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400184 if (prefix == ip_utils::subnet_mask::prefix_32bit)
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400185 return std::string(localHost);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400186 std::string defaultGw {};
187 // Make a vector of each individual number in the ip address.
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400188 std::vector<std::string_view> tokens = split_string(localHost, '.');
Adrien Béraud612b55b2023-05-29 10:42:04 -0400189 // Build a gateway address from the individual ip components.
190 for (unsigned i = 0; i <= (unsigned) prefix; i++)
Adrien Béraud80c14e12023-07-18 16:13:15 -0400191 defaultGw = fmt::format("{:s}{:s}.", defaultGw, tokens[i]);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400192 for (unsigned i = (unsigned) ip_utils::subnet_mask::prefix_32bit;
193 i > (unsigned) prefix + 1;
194 i--)
195 defaultGw += "0.";
196 defaultGw += "1";
197 return defaultGw;
198}
199
200IpAddr
201ip_utils::getLocalGateway()
202{
203 char localHostBuf[INET_ADDRSTRLEN];
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400204 auto hostInfo = ip_utils::getHostName();
205 if (hostInfo.address.empty()) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400206 // JAMI_WARN("Couldn't find local host");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400207 return {};
208 } else {
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400209 return IpAddr(ip_utils::getGateway(hostInfo.address, ip_utils::subnet_mask::prefix_24bit));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400210 }
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) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400230 // JAMI_ERR("Error resolving %.*s : %s",
231 // (int) name.size(),
232 // name.data(),
233 // sip_utils::sip_strerror(status).c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400234 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 }
Morteza Namvar5f639522023-07-04 17:08:58 -0400271 // JAMI_WARN("Could not get preferred address familly (%s)",
272 // (family == pj_AF_INET6()) ? "IPv6" : "IPv4");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400273 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 }
Morteza Namvar5f639522023-07-04 17:08:58 -0400278 // JAMI_ERR("Could not get local IP");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400279 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) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400295 // JAMI_ERR("Could not open socket: %m");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400296 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) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400302 // JAMI_ERR("Could not setsockopt: %m");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400303 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) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400332 // JAMI_ERR("getaddrinfo failed with error: %lu", dwRetval);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400333 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
Morteza Namvar5f639522023-07-04 17:08:58 -0400378 // JAMI_ERR("Not implemented yet. (iphlpapi.h problem)");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400379#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;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400390 if (pj_enum_ip_interface(pj_AF_UNSPEC(), &addrCnt, addrList) == PJ_SUCCESS) {
391 for (unsigned i = 0; i < addrCnt; i++) {
392 char addr[PJ_INET6_ADDRSTRLEN];
393 pj_sockaddr_print(&addrList[i], addr, sizeof(addr), 0);
Adrien Béraud63a1fac2023-08-23 09:31:17 -0400394 ifaceList.emplace_back(addr);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400395 }
396 }
397
398 return ifaceList;
399}
400
401std::vector<IpAddr>
402ip_utils::getLocalNameservers()
403{
404 std::vector<IpAddr> res;
405#if defined __ANDROID__ || defined _WIN32 || TARGET_OS_IPHONE
406#ifdef _MSC_VER
407#pragma message(__FILE__ "(" STR2(__LINE__) ") : -NOTE- " \
408 "Not implemented")
409#else
410#warning "Not implemented"
411#endif
412#else
413 if (not(_res.options & RES_INIT))
414 res_init();
415 res.insert(res.end(), _res.nsaddr_list, _res.nsaddr_list + _res.nscount);
416#endif
417 return res;
418}
419
420bool
421IpAddr::isValid(std::string_view address, pj_uint16_t family)
422{
423 const pj_str_t pjstring(sip_utils::CONST_PJ_STR(address));
424 pj_str_t ret_str;
425 pj_uint16_t ret_port;
426 int ret_family;
427 auto status = pj_sockaddr_parse2(pj_AF_UNSPEC(), 0, &pjstring, &ret_str, &ret_port, &ret_family);
428 if (status != PJ_SUCCESS || (family != pj_AF_UNSPEC() && ret_family != family))
429 return false;
430
431 char buf[PJ_INET6_ADDRSTRLEN];
432 pj_str_t addr_with_null = {buf, 0};
433 pj_strncpy_with_null(&addr_with_null, &ret_str, sizeof(buf));
434 struct sockaddr sa;
435 return inet_pton(ret_family == pj_AF_INET6() ? AF_INET6 : AF_INET, buf, &(sa.sa_data)) == 1;
436}
437
438bool
439IpAddr::isUnspecified() const
440{
441 switch (addr.addr.sa_family) {
442 case AF_INET:
443 return IN_IS_ADDR_UNSPECIFIED(&addr.ipv4.sin_addr);
444 case AF_INET6:
445 return IN6_IS_ADDR_UNSPECIFIED(reinterpret_cast<const in6_addr*>(&addr.ipv6.sin6_addr));
446 default:
447 return true;
448 }
449}
450
451bool
452IpAddr::isLoopback() const
453{
454 switch (addr.addr.sa_family) {
455 case AF_INET: {
456 auto addr_host = ntohl(addr.ipv4.sin_addr.s_addr);
457 uint8_t b1 = (uint8_t)(addr_host >> 24);
458 return b1 == 127;
459 }
460 case AF_INET6:
461 return IN6_IS_ADDR_LOOPBACK(reinterpret_cast<const in6_addr*>(&addr.ipv6.sin6_addr));
462 default:
463 return false;
464 }
465}
466
467bool
468IpAddr::isPrivate() const
469{
470 if (isLoopback()) {
471 return true;
472 }
473 switch (addr.addr.sa_family) {
474 case AF_INET: {
475 auto addr_host = ntohl(addr.ipv4.sin_addr.s_addr);
476 uint8_t b1, b2;
477 b1 = (uint8_t)(addr_host >> 24);
478 b2 = (uint8_t)((addr_host >> 16) & 0x0ff);
479 // 10.x.y.z
480 if (b1 == 10)
481 return true;
482 // 172.16.0.0 - 172.31.255.255
483 if ((b1 == 172) && (b2 >= 16) && (b2 <= 31))
484 return true;
485 // 192.168.0.0 - 192.168.255.255
486 if ((b1 == 192) && (b2 == 168))
487 return true;
488 return false;
489 }
490 case AF_INET6: {
491 const pj_uint8_t* addr6 = reinterpret_cast<const pj_uint8_t*>(&addr.ipv6.sin6_addr);
492 if (addr6[0] == 0xfc)
493 return true;
494 return false;
495 }
496 default:
497 return false;
498 }
499}
Sébastien Blin464bdff2023-07-19 08:02:53 -0400500} // namespace dhtnet