/ libi2pd / util.cpp
util.cpp
  1  /*
  2  * Copyright (c) 2013-2026, The PurpleI2P Project
  3  *
  4  * This file is part of Purple i2pd project and licensed under BSD3
  5  *
  6  * See full license text in LICENSE file at top of project tree
  7  */
  8  
  9  #include <cstdlib>
 10  #include <string>
 11  #include <array>
 12  #include <unordered_set>
 13  #include <boost/asio.hpp>
 14  
 15  #include "util.h"
 16  #include "Log.h"
 17  #include "I2PEndian.h"
 18  
 19  #if !defined (__FreeBSD__) && !defined(_MSC_VER)
 20  #include <pthread.h>
 21  #endif
 22  
 23  #if defined(__OpenBSD__) || defined(__FreeBSD__)
 24  #include <pthread_np.h>
 25  #endif
 26  
 27  #if defined(__APPLE__)
 28  # include <AvailabilityMacros.h>
 29  #endif
 30  
 31  #if defined(__HAIKU__)
 32  #include <gnu/pthread.h>
 33  #include <posix/pthread.h>
 34  #include <posix/sys/sockio.h>
 35  #include <posix/sys/ioctl.h>
 36  #ifndef _DEFAULT_SOURCE
 37  #define _DEFAULT_SOURCE
 38  #include <bsd/ifaddrs.h>
 39  #endif
 40  #endif
 41  
 42  #ifdef _WIN32
 43  #include <stdlib.h>
 44  #include <string.h>
 45  #include <stdio.h>
 46  #include <sysinfoapi.h>
 47  #include <winsock2.h>
 48  #include <ws2tcpip.h>
 49  #include <iphlpapi.h>
 50  #include <shlobj.h>
 51  
 52  #if defined(_MSC_VER)
 53  const DWORD MS_VC_EXCEPTION = 0x406D1388;
 54  #pragma pack(push,8)
 55  typedef struct tagTHREADNAME_INFO
 56  {
 57  	DWORD dwType;
 58  	LPCSTR szName;
 59  	DWORD dwThreadID;
 60  	DWORD dwFlags;
 61  } THREADNAME_INFO;
 62  #pragma pack(pop)
 63  #endif
 64  
 65  #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
 66  #define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
 67  
 68  // inet_pton and inet_ntop have been in Windows since Vista, but XP doesn't have these functions!
 69  // This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
 70  int inet_pton_xp (int af, const char *src, void *dst)
 71  {
 72  	struct sockaddr_storage ss;
 73  	int size = sizeof (ss);
 74  	char src_copy[INET6_ADDRSTRLEN + 1];
 75  
 76  	ZeroMemory (&ss, sizeof (ss));
 77  	strncpy (src_copy, src, INET6_ADDRSTRLEN + 1);
 78  	src_copy[INET6_ADDRSTRLEN] = 0;
 79  
 80  	if (WSAStringToAddress (src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0)
 81  	{
 82  		switch (af)
 83  		{
 84  		case AF_INET:
 85  			*(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
 86  			return 1;
 87  		case AF_INET6:
 88  			*(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
 89  			return 1;
 90  		}
 91  	}
 92  	return 0;
 93  }
 94  
 95  const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size)
 96  {
 97  	struct sockaddr_storage ss;
 98  	unsigned long s = size;
 99  
100  	ZeroMemory(&ss, sizeof(ss));
101  	ss.ss_family = af;
102  
103  	switch (af)
104  	{
105  		case AF_INET:
106  			((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
107  			break;
108  		case AF_INET6:
109  			((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
110  			break;
111  		default:
112  			return NULL;
113  	}
114  	/* cannot directly use &size because of strict aliasing rules */
115  	return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL;
116  }
117  
118  #else /* !_WIN32 => UNIX */
119  #include <sys/types.h>
120  #ifdef ANDROID
121  #include "ifaddrs.h"
122  #else
123  #include <ifaddrs.h>
124  #endif
125  #endif
126  
127  #define address_pair_v4(a,b) std::pair{ boost::asio::ip::make_address (a).to_v4 ().to_uint (), boost::asio::ip::make_address(b).to_v4 ().to_uint () }
128  #define address_pair_v6(a,b) std::pair{ boost::asio::ip::make_address (a).to_v6 ().to_bytes (), boost::asio::ip::make_address(b).to_v6 ().to_bytes () }
129  
130  namespace i2p
131  {
132  namespace util
133  {
134  
135  	void RunnableService::StartIOService ()
136  	{
137  		if (!m_IsRunning)
138  		{
139  			m_IsRunning = true;
140  			m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this)));
141  		}
142  	}
143  
144  	void RunnableService::StopIOService ()
145  	{
146  		if (m_IsRunning)
147  		{
148  			m_IsRunning = false;
149  			m_Service.stop ();
150  			if (m_Thread)
151  			{
152  				m_Thread->join ();
153  				m_Thread = nullptr;
154  			}
155  		}
156  	}
157  
158  	void RunnableService::Run ()
159  	{
160  		SetThreadName(m_Name.c_str());
161  
162  		while (m_IsRunning)
163  		{
164  			try
165  			{
166  				m_Service.run ();
167  			}
168  			catch (std::exception& ex)
169  			{
170  				LogPrint (eLogError, m_Name, ": Runtime exception: ", ex.what ());
171  			}
172  		}
173  	}
174  
175  	void RunnableService::SetName (std::string_view name)
176  	{
177  		if (name.length() < 16)
178  			m_Name = name;
179  		else
180  			m_Name = name.substr(0,15);
181  	}
182  
183  	void SetThreadName (const char *name) {
184  #if defined(__APPLE__)
185  # if (!defined(MAC_OS_X_VERSION_10_6) || \
186  		(MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || \
187  		defined(__POWERPC__))
188  		/* pthread_setname_np is not there on <10.6 and all PPC.
189  		So do nothing. */
190  # else
191  		pthread_setname_np((char*)name);
192  # endif
193  #elif defined(__FreeBSD__) || defined(__OpenBSD__)
194  		pthread_set_name_np(pthread_self(), name);
195  #elif defined(__NetBSD__)
196  		pthread_setname_np(pthread_self(), "%s", (void *)name);
197  #elif !defined(__gnu_hurd__)
198  	#if defined(_MSC_VER)
199  		THREADNAME_INFO info;
200  		info.dwType = 0x1000;
201  		info.szName = name;
202  		info.dwThreadID = -1;
203  		info.dwFlags = 0;
204  		#pragma warning(push)
205  		#pragma warning(disable: 6320 6322)
206  		__try {
207  			RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
208  		}
209  		__except (EXCEPTION_EXECUTE_HANDLER) {
210  		}
211  		#pragma warning(pop)
212  	#else
213  		pthread_setname_np(pthread_self(), name);
214  	#endif
215  #endif
216  	}
217  
218  	size_t Mapping::FromBuffer (const uint8_t * buf, size_t len)
219  	{
220  		if (len < 2) return 0;
221  		return FromBuffer (bufbe16toh (buf), buf + 2, len - 2) + 2;
222  	}
223  
224  	size_t Mapping::FromBuffer (size_t size, const uint8_t * buf, size_t len)
225  	{
226  		if (!size || len < size) return 0;
227  		size_t offset = 0;
228  		while (offset < size)
229  		{
230  			auto param = ExtractString (buf + offset, size - offset);
231  			offset += param.length () + 1;
232  			if (offset + 1 > size)
233  			{
234  				LogPrint (eLogWarning, "Mapping: Param length ",  param.length (), " is too long");
235  				break;
236  			}
237  			if (buf[offset] != '=')
238  			{
239  				LogPrint (eLogWarning, "Mapping: Unexpected character ", buf[offset], " instead '=' after ", param);
240  				break;
241  			}
242  			offset++;
243  
244  			auto value = ExtractString (buf + offset, size - offset);
245  			offset += value.length () + 1;
246  			if (offset + 1 > size)
247  			{
248  				LogPrint (eLogWarning, "Mapping: Value length ",  param.length (), " is too long");
249  				break;
250  			}
251  			if (buf[offset] != ';')
252  			{
253  				LogPrint (eLogWarning, "Mapping: Unexpected character ", buf[offset], " instead ';' after ", value);
254  				break;
255  			}
256  			offset++;
257  			m_Options.emplace (param, value);
258  		}
259  		return size;
260  	}
261  
262  	std::string_view Mapping::ExtractString (const uint8_t * buf, size_t len)
263  	{
264  		uint8_t l = buf[0];
265  		if (l > len) l = len;
266  		return { (const char *)(buf + 1), l };
267  	}
268  
269  	size_t Mapping::ToBuffer (uint8_t * buf, size_t len) const
270  	{
271  		size_t offset = 2;
272  		for (auto it: m_Options)
273  		{
274  			if (len <= offset) break;
275  			size_t l = WriteOption (it.first, it.second, buf + offset, len - offset);
276  			if (!l) break;
277  			offset += l;
278  		}
279  		htobe16buf (buf, offset - 2);
280  		return offset;
281  	}
282  
283  	size_t Mapping::WriteString (std::string_view str, uint8_t * buf, size_t len)
284  	{
285  		auto l = str.length ();
286  		if (l + 1 >= len) l = len - 1;
287  		if (l > 255) l = 255; // 1 byte max
288  		buf[0] = l;
289  		memcpy (buf + 1, str.data (), l);
290  		return l + 1;
291  	}
292  
293  	size_t Mapping::WriteOption (std::string_view param, std::string_view value, uint8_t * buf, size_t len)
294  	{
295  		if (param.length () + value.length () + 4 > len) return 0;
296  		size_t offset = 0;
297  		offset += WriteString (param, buf + offset, len - offset);
298  		buf[offset] = '='; offset++;
299  		offset += WriteString (value, buf + offset, len - offset);
300  		buf[offset] = ';'; offset++;
301  		return offset;
302  	}
303  
304  	std::string_view Mapping::operator[](std::string_view param) const
305  	{
306  		auto it = m_Options.find (param);
307  		if (it != m_Options.end ()) return it->second;
308  		return std::string_view (); // empty string
309  	}
310  
311  	bool Mapping::Insert (std::string_view param, std::string_view value)
312  	{
313  		return m_Options.emplace (param, value).second;
314  	}
315  
316  	bool Mapping::Contains (std::string_view param) const
317  	{
318  #if __cplusplus >= 202002L // C++20
319  		return m_Options.contains (param);
320  #else
321  		auto it = m_Options.find (param);
322  		return it != m_Options.end ();
323  #endif
324  	}
325  
326  	void Mapping::CleanUp ()
327  	{
328  		if (!m_Options.empty ())
329  		{
330  			decltype(m_Options) tmp;
331  			m_Options.swap (tmp);
332  		}
333  	}
334  
335  	bool Mapping::GetBoolParamValue (std::string_view s, bool& value)
336  	{
337  		bool ret = true;
338  		value = false;
339  		if (s == "true")
340  			value = true;
341  		else if (s == "false")
342  			value = false;
343  		else
344  		{
345  			int v = 0;
346  			auto res = std::from_chars(s.data(), s.data() + s.size(), v);
347  			if (res.ec == std::errc())
348  				value = v;
349  			else
350  			{
351  				LogPrint (eLogError, "Mapping: Unable to parse bool param value ", s, ": ",  std::make_error_code (res.ec).message ());
352  				ret = false;
353  			}
354  		}
355  		return ret;
356  	}
357  
358  namespace net
359  {
360  #ifdef _WIN32
361  	int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback)
362  	{
363  		typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
364  		IPN inetntop = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
365  		if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
366  
367  		ULONG outBufLen = 0;
368  		PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
369  		PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
370  		PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
371  
372  		if (GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
373  			== ERROR_BUFFER_OVERFLOW)
374  		{
375  			FREE(pAddresses);
376  			pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
377  		}
378  
379  		DWORD dwRetVal = GetAdaptersAddresses(
380  			AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
381  		);
382  
383  		if (dwRetVal != NO_ERROR)
384  		{
385  			LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
386  			FREE(pAddresses);
387  			return fallback;
388  		}
389  
390  		pCurrAddresses = pAddresses;
391  		while (pCurrAddresses)
392  		{
393  			pUnicast = pCurrAddresses->FirstUnicastAddress;
394  			if (pUnicast == nullptr)
395  				LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported");
396  
397  			while (pUnicast != nullptr)
398  			{
399  				LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
400  				sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
401  				if (localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr)
402  				{
403  					char addr[INET_ADDRSTRLEN];
404  					inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN);
405  
406  					auto result = pCurrAddresses->Mtu;
407  					FREE(pAddresses);
408  					pAddresses = nullptr;
409  					LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr);
410  					return result;
411  				}
412  				pUnicast = pUnicast->Next;
413  			}
414  			pCurrAddresses = pCurrAddresses->Next;
415  		}
416  
417  		LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found");
418  		FREE(pAddresses);
419  		return fallback;
420  	}
421  
422  	int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback)
423  	{
424  		typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
425  		IPN inetntop = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
426  		if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
427  
428  		ULONG outBufLen = 0;
429  		PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
430  		PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
431  		PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
432  
433  		if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
434  			== ERROR_BUFFER_OVERFLOW)
435  		{
436  			FREE(pAddresses);
437  			pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
438  		}
439  
440  		DWORD dwRetVal = GetAdaptersAddresses(
441  			AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
442  		);
443  
444  		if (dwRetVal != NO_ERROR)
445  		{
446  			LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
447  			FREE(pAddresses);
448  			return fallback;
449  		}
450  
451  		bool found_address = false;
452  		pCurrAddresses = pAddresses;
453  		while (pCurrAddresses)
454  		{
455  			pUnicast = pCurrAddresses->FirstUnicastAddress;
456  			if (pUnicast == nullptr)
457  				LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported");
458  
459  			while (pUnicast != nullptr)
460  			{
461  				LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
462  				sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr;
463  
464  				for (int j = 0; j != 8; ++j)
465  				{
466  					if (localInterfaceAddress->sin6_addr.u.Word[j] != inputAddress.sin6_addr.u.Word[j])
467  						break;
468  					else
469  						found_address = true;
470  				}
471  
472  				if (found_address)
473  				{
474  					char addr[INET6_ADDRSTRLEN];
475  					inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN);
476  
477  					auto result = pCurrAddresses->Mtu;
478  					FREE(pAddresses);
479  					pAddresses = nullptr;
480  					LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr);
481  					return result;
482  				}
483  				pUnicast = pUnicast->Next;
484  			}
485  
486  			pCurrAddresses = pCurrAddresses->Next;
487  		}
488  
489  		LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found");
490  		FREE(pAddresses);
491  		return fallback;
492  	}
493  
494  	int GetMTUWindows (const boost::asio::ip::address& localAddress, int fallback)
495  	{
496  #ifdef UNICODE
497  		string localAddress_temporary = localAddress.to_string();
498  		wstring localAddressUniversal(localAddress_temporary.begin(), localAddress_temporary.end());
499  #else
500  		std::string localAddressUniversal = localAddress.to_string();
501  #endif
502  
503  		typedef int (* IPN)(int af, const char *src, void *dst);
504  		IPN inetpton = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton");
505  		if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found
506  
507  		if (localAddress.is_v4())
508  		{
509  			sockaddr_in inputAddress;
510  			inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
511  			return GetMTUWindowsIpv4(inputAddress, fallback);
512  		}
513  		else if (localAddress.is_v6())
514  		{
515  			sockaddr_in6 inputAddress;
516  			inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
517  			return GetMTUWindowsIpv6(inputAddress, fallback);
518  		}
519  		else
520  		{
521  			LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported");
522  			return fallback;
523  		}
524  	}
525  #else // assume unix
526  	int GetMTUUnix (const boost::asio::ip::address& localAddress, int fallback)
527  	{
528  		ifaddrs* ifaddr, *ifa = nullptr;
529  		if (getifaddrs(&ifaddr) == -1)
530  		{
531  			LogPrint(eLogError, "NetIface: Can't call getifaddrs(): ", strerror(errno));
532  			return fallback;
533  		}
534  
535  		int family = 0;
536  		// look for interface matching local address
537  		for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
538  		{
539  			if (!ifa->ifa_addr)
540  				continue;
541  
542  			family = ifa->ifa_addr->sa_family;
543  			if (family == AF_INET && localAddress.is_v4())
544  			{
545  				sockaddr_in* sa = (sockaddr_in*) ifa->ifa_addr;
546  				if (!memcmp(&sa->sin_addr, localAddress.to_v4().to_bytes().data(), 4))
547  					break; // address matches
548  			}
549  			else if (family == AF_INET6 && localAddress.is_v6())
550  			{
551  				sockaddr_in6* sa = (sockaddr_in6*) ifa->ifa_addr;
552  				if (!memcmp(&sa->sin6_addr, localAddress.to_v6().to_bytes().data(), 16))
553  					break; // address matches
554  			}
555  		}
556  		int mtu = fallback;
557  		if (ifa && family)
558  		{ // interface found?
559  			int fd = socket(family, SOCK_DGRAM, 0);
560  			if (fd > 0)
561  			{
562  				ifreq ifr;
563  				strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ-1); // set interface for query
564  				if (ioctl(fd, SIOCGIFMTU, &ifr) >= 0)
565  					mtu = ifr.ifr_mtu; // MTU
566  				else
567  					LogPrint (eLogError, "NetIface: Failed to run ioctl: ", strerror(errno));
568  				close(fd);
569  			}
570  			else
571  				LogPrint(eLogError, "NetIface: Failed to create datagram socket");
572  		}
573  		else
574  			LogPrint(eLogWarning, "NetIface: Interface for local address", localAddress.to_string(), " not found");
575  		freeifaddrs(ifaddr);
576  
577  		return mtu;
578  	}
579  #endif // _WIN32
580  
581  	int GetMTU (const boost::asio::ip::address& localAddress)
582  	{
583  		int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU
584  
585  #ifdef _WIN32
586  		return GetMTUWindows(localAddress, fallback);
587  #else
588  		return GetMTUUnix(localAddress, fallback);
589  #endif
590  		return fallback;
591  	}
592  
593  	const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6)
594  	{
595  #ifdef _WIN32
596  		LogPrint(eLogError, "NetIface: Cannot get address by interface name, not implemented on WIN32");
597  		if (ipv6)
598  			return boost::asio::ip::make_address("::1");
599  		else
600  			return boost::asio::ip::make_address("127.0.0.1");
601  #else
602  		int af = (ipv6 ? AF_INET6 : AF_INET);
603  		ifaddrs *addrs;
604  		try
605  		{
606  			if (!getifaddrs(&addrs))
607  			{
608  				for (auto cur = addrs; cur; cur = cur->ifa_next)
609  				{
610  					std::string cur_ifname(cur->ifa_name);
611  					if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af)
612  					{
613  						// match
614  						char addr[INET6_ADDRSTRLEN];
615  						memset (addr, 0, INET6_ADDRSTRLEN);
616  						if (af == AF_INET)
617  							inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN);
618  						else
619  							inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN);
620  						freeifaddrs(addrs);
621  						std::string cur_ifaddr(addr);
622  						return boost::asio::ip::make_address(cur_ifaddr);
623  					}
624  				}
625  			}
626  		}
627  		catch (std::exception& ex)
628  		{
629  			LogPrint(eLogError, "NetIface: Exception while searching address using ifaddr: ", ex.what());
630  		}
631  
632  		if (addrs) freeifaddrs(addrs);
633  		std::string fallback;
634  		if (ipv6)
635  		{
636  			fallback = "::1";
637  			LogPrint(eLogWarning, "NetIface: Cannot find IPv6 address for interface ", ifname);
638  		} else {
639  			fallback = "127.0.0.1";
640  			LogPrint(eLogWarning, "NetIface: Cannot find IPv4 address for interface ", ifname);
641  		}
642  		return boost::asio::ip::make_address(fallback);
643  #endif
644  	}
645  
646  	int GetMaxMTU (const boost::asio::ip::address_v6& localAddress)
647  	{
648  		uint32_t prefix = bufbe32toh (localAddress.to_bytes ().data ());
649  		switch (prefix)
650  		{
651  			case 0x20010470:
652  			case 0x260070ff:
653  			// Hurricane Electric
654  				return 1480;
655  			break;
656  			default: ;
657  		}
658  		return 1500;
659  	}
660  
661  	static bool IsYggdrasilAddress (const uint8_t addr[16])
662  	{
663  		return addr[0] == 0x02 || addr[0] == 0x03;
664  	}
665  
666  	bool IsYggdrasilAddress (const boost::asio::ip::address& addr)
667  	{
668  		if (!addr.is_v6 ()) return false;
669  		return IsYggdrasilAddress (addr.to_v6 ().to_bytes ().data ());
670  	}
671  
672  	bool IsPortInReservedRange (const uint16_t port) noexcept
673  	{
674  		// https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers (Feb. 3, 2023) + Tor browser (9150)
675  		static const std::unordered_set<uint16_t> reservedPorts{
676  			9119,9150,9306,9312,9389,9418,9535,9536,9695,
677  			9800,9899,10000,10050,10051,10110,10212,
678  			10933,11001,11112,11235,11371,12222,12223,
679  			13075,13400,13720,13721,13724,13782,13783,
680  			13785,13786,15345,17224,17225,17500,18104,
681  			19788,19812,19813,19814,19999,20000,24465,
682  			24554,26000,27000,27001,27002,27003,27004,
683  			27005,27006,27007,27008,27009,28000};
684  
685  		return (reservedPorts.find(port) != reservedPorts.end());
686  	}
687  
688  	template<typename CheckAddress>
689  	static boost::asio::ip::address_v6 GetLocalIPV6Address (CheckAddress checkAddress)
690  	{
691  #if defined(_WIN32)
692  		ULONG outBufLen = 0;
693  		PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
694  		PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
695  		PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
696  
697  		if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
698  			== ERROR_BUFFER_OVERFLOW)
699  		{
700  			FREE(pAddresses);
701  			pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
702  		}
703  
704  		DWORD dwRetVal = GetAdaptersAddresses(
705  			AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
706  		);
707  
708  		if (dwRetVal != NO_ERROR)
709  		{
710  			LogPrint(eLogError, "NetIface: GetYggdrasilAddress(): enclosed GetAdaptersAddresses() call has failed");
711  			FREE(pAddresses);
712  			return boost::asio::ip::address_v6 ();
713  		}
714  
715  		pCurrAddresses = pAddresses;
716  		while (pCurrAddresses)
717  		{
718  			pUnicast = pCurrAddresses->FirstUnicastAddress;
719  
720  			while (pUnicast != nullptr)
721  			{
722  				LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
723  				sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr;
724  				if (checkAddress(localInterfaceAddress->sin6_addr.u.Byte)) {
725  					boost::asio::ip::address_v6::bytes_type bytes;
726  					memcpy (bytes.data (), &localInterfaceAddress->sin6_addr.u.Byte, 16);
727  					FREE(pAddresses);
728  					return boost::asio::ip::address_v6 (bytes);
729  				}
730  				pUnicast = pUnicast->Next;
731  			}
732  			pCurrAddresses = pCurrAddresses->Next;
733  		}
734  		LogPrint(eLogWarning, "NetIface: Interface with ipv6 network address not found");
735  		FREE(pAddresses);
736  		return boost::asio::ip::address_v6 ();
737  #else
738  		ifaddrs *addrs;
739  		try
740  		{
741  			if (!getifaddrs(&addrs))
742  			{
743  				for (auto cur = addrs; cur; cur = cur->ifa_next)
744  				{
745  					if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6)
746  					{
747  						sockaddr_in6* sa = (sockaddr_in6*)cur->ifa_addr;
748  						if (checkAddress(sa->sin6_addr.s6_addr))
749  						{
750  							boost::asio::ip::address_v6::bytes_type bytes;
751  							memcpy (bytes.data (), &sa->sin6_addr, 16);
752  							freeifaddrs(addrs);
753  							return boost::asio::ip::address_v6 (bytes);
754  						}
755  					}
756  				}
757  			}
758  		}
759  		catch (std::exception& ex)
760  		{
761  			LogPrint(eLogError, "NetIface: Exception while searching ipv6 address using ifaddr: ", ex.what());
762  		}
763  		LogPrint(eLogWarning, "NetIface: Interface with ipv6 network address not found");
764  		if (addrs) freeifaddrs(addrs);
765  		return boost::asio::ip::address_v6 ();
766  #endif
767  	}
768  
769  	boost::asio::ip::address_v6 GetYggdrasilAddress ()
770  	{
771  		return GetLocalIPV6Address ([](const uint8_t addr[16]) { return IsYggdrasilAddress (addr); });
772  	}
773  
774  	boost::asio::ip::address_v6 GetClearnetIPV6Address ()
775  	{
776  		return GetLocalIPV6Address ([](const uint8_t addr[16])
777  			{
778  				return (addr[0] & 0xF0) == 0x20 || (addr[0] & 0xFC) == 0xFC; // 2000::/3 or FC00:/7
779  			});
780  	}
781  
782  	bool IsLocalAddress (const boost::asio::ip::address& addr)
783  	{
784  		auto mtu = // TODO: implement better
785  #ifdef _WIN32
786  		GetMTUWindows(addr, 0);
787  #else
788  		GetMTUUnix(addr, 0);
789  #endif
790  		return mtu > 0;
791  	}
792  
793  	bool IsInReservedRange (const boost::asio::ip::address& host)
794  	{
795  		// https://en.wikipedia.org/wiki/Reserved_IP_addresses
796  		if (host.is_unspecified ()) return false;
797  		if (host.is_v4())
798  		{
799  			static const std::array reservedIPv4Ranges
800  			{
801  				address_pair_v4("0.0.0.0",      "0.255.255.255"),
802  				address_pair_v4("10.0.0.0",     "10.255.255.255"),
803  				address_pair_v4("100.64.0.0",   "100.127.255.255"),
804  				address_pair_v4("127.0.0.0",    "127.255.255.255"),
805  				address_pair_v4("169.254.0.0",  "169.254.255.255"),
806  				address_pair_v4("172.16.0.0",   "172.31.255.255"),
807  				address_pair_v4("192.0.0.0",    "192.0.0.255"),
808  				address_pair_v4("192.0.2.0",    "192.0.2.255"),
809  				address_pair_v4("192.88.99.0",  "192.88.99.255"),
810  				address_pair_v4("192.168.0.0",  "192.168.255.255"),
811  				address_pair_v4("198.18.0.0",   "192.19.255.255"),
812  				address_pair_v4("198.51.100.0", "198.51.100.255"),
813  				address_pair_v4("203.0.113.0",  "203.0.113.255"),
814  				address_pair_v4("224.0.0.0",    "255.255.255.255")
815  			};
816  
817  			uint32_t ipv4_address = host.to_v4 ().to_uint ();
818  			for (const auto& it : reservedIPv4Ranges) {
819  				if (ipv4_address >= it.first && ipv4_address <= it.second)
820  					return true;
821  			}
822  		}
823  		if (host.is_v6())
824  		{
825  			static std::array reservedIPv6Ranges
826  			{
827  				address_pair_v6("64:ff9b::",  "64:ff9b:ffff:ffff:ffff:ffff:ffff:ffff"),  // NAT64
828  				address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"),
829  				address_pair_v6("fc00::",     "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
830  				address_pair_v6("fe80::",     "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
831  				address_pair_v6("ff00::",     "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
832  				address_pair_v6("::",         "::"),
833  				address_pair_v6("::1",        "::1")
834  			};
835  
836  			boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6 ().to_bytes ();
837  			for (const auto& it : reservedIPv6Ranges) {
838  				if (ipv6_address >= it.first && ipv6_address <= it.second)
839  					return true;
840  			}
841  			if (IsYggdrasilAddress (ipv6_address.data ())) // yggdrasil?
842  				return true;
843  		}
844  		return false;
845  	}
846  } // net
847  } // util
848  } // i2p