/** * platform_compat — общие заголовки POSIX/Windows и адаптеры отсутствующих POSIX-функций. * Также предоставляет криптографический random_bytes, адреса интерфейсов, выбор default route * и классификацию публичных IPv4. Сокетные типы/ошибки и nonblocking API — в socket_compat.h. * Подключать этот заголовок вместо прямых POSIX includes для общего кода. * Windows: htobe16/32/64 и be16/32/64toh через byteswap, strcasecmp/strncasecmp через _stricmp/_strnicmp, * memmem через inline-поиск (пустой образец возвращает начало), poll через WSAPoll, mkdir через _mkdir. * utun_gettimeofday — календарное Unix-время (не монотонное); Windows игнорирует tz. * Windows pipe — _pipe(4096, _O_BINARY). fcntl: F_GETFL возвращает 0, F_SETFL с O_NONBLOCK включает * неблокирующий режим; остальные флаги игнорируются, обратное переключение этим адаптером не выполняется. */ #ifndef PLATFORM_COMPAT_H #define PLATFORM_COMPAT_H #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 #include #include #include #include #include #include #include // Byte order conversion functions #ifdef _WIN32 #include #define htobe16(x) _byteswap_ushort(x) #define be16toh(x) _byteswap_ushort(x) #define htobe32(x) _byteswap_ulong(x) #define be32toh(x) _byteswap_ulong(x) #define htobe64(x) _byteswap_uint64(x) #define be64toh(x) _byteswap_uint64(x) #define htole64(x) (x) #define le64toh(x) (x) #else #include #endif // POSIX functions missing on Windows #define strcasecmp _stricmp #define strncasecmp _strnicmp #include // memmem — not available on Windows #define memmem compat_memmem static inline void* compat_memmem(const void* h, size_t hl, const void* n, size_t nl) { if (nl == 0) return (void*)h; if (hl < nl) return NULL; const unsigned char* hay = (const unsigned char*)h; const unsigned char* ndl = (const unsigned char*)n; for (size_t i = 0; i <= hl - nl; i++) if (hay[i] == ndl[0] && memcmp(hay + i, ndl, nl) == 0) return (void*)(hay + i); return NULL; } // poll() and pollfd are already defined in winsock2.h on MSYS2 UCRT64 // Only define if not already present #ifndef POLLIN #define POLLIN 0x0001 #endif #ifndef POLLOUT #define POLLOUT 0x0004 #endif #ifndef POLLERR #define POLLERR 0x0008 #endif #ifndef POLLHUP #define POLLHUP 0x0010 #endif #ifndef POLLNVAL #define POLLNVAL 0x0020 #endif #ifndef POLLPRI #define POLLPRI 0x0002 #endif // ssize_t for Windows (signed size type) #ifndef _SSIZE_T_DEFINED typedef long ssize_t; #define _SSIZE_T_DEFINED #endif // gettimeofday for Windows - provide our own implementation if not already defined #ifndef _GETTIMEOFDAY_DEFINED #define _GETTIMEOFDAY_DEFINED static inline int utun_gettimeofday(struct timeval *tv, void *tz) { FILETIME ft; unsigned __int64 tmpres = 0; if (tv != NULL) { GetSystemTimeAsFileTime(&ft); tmpres |= ft.dwHighDateTime; tmpres <<= 32; tmpres |= ft.dwLowDateTime; // Convert from 100-nanosecond intervals to microseconds tmpres /= 10; // Convert to Unix epoch (January 1, 1970) tmpres -= 11644473600000000ULL; tv->tv_sec = (long)(tmpres / 1000000UL); tv->tv_usec = (long)(tmpres % 1000000UL); } // tz parameter is ignored (for compatibility with POSIX) return 0; } #endif // _GETTIMEOFDAY_DEFINED // Pipe creation for Windows static inline int platform_pipe(int pipefd[2]) { return _pipe(pipefd, 4096, _O_BINARY); } #define pipe platform_pipe // nfds_t already defined in winsock2.h on MSYS2 // fcntl replacement for Windows (simplified - only supports F_GETFL/F_SETFL with O_NONBLOCK) #ifndef F_GETFL #define F_GETFL 3 #endif #ifndef F_SETFL #define F_SETFL 4 #endif #ifndef O_NONBLOCK #define O_NONBLOCK 0x4000 #endif static inline int platform_fcntl(int fd, int cmd, ... /* arg */ ) { va_list ap; va_start(ap, cmd); if (cmd == F_GETFL) { va_end(ap); // Return current flags (always 0 on Windows, can't query easily) return 0; } else if (cmd == F_SETFL) { int flags = va_arg(ap, int); va_end(ap); if (flags & O_NONBLOCK) { // Set non-blocking mode u_long mode = 1; return ioctlsocket(fd, FIONBIO, &mode); } return 0; } va_end(ap); errno = EINVAL; return -1; } #define fcntl platform_fcntl // nanosleep already defined in pthread_time.h on MSYS2 // poll() and pollfd already defined in winsock2.h on MSYS2 // Use WSAPoll on Windows (available in Vista and later) #define poll WSAPoll // sa_family_t might not be defined on some Windows setups #ifndef sa_family_t typedef unsigned short sa_family_t; #endif #else // POSIX - include standard headers #include #include #include #include #include #include #include #include #include #include #define utun_gettimeofday(A,B) gettimeofday(A,B) #endif #include // Заполняет buffer криптографическими случайными байтами: POSIX /dev/urandom, Windows BCryptGenRandom. // buffer не NULL, len > 0 (Windows: помещается в ULONG). 0 — всё заполнено, -1 — ошибка/неполное чтение. int random_bytes(uint8_t *buffer, size_t len); // Get IPv4 address of interface by its index (ifindex) // POSIX: getifaddrs; Windows: IP Helper. Адрес в network byte order, 0 при ошибке/отсутствии адреса. uint32_t get_interface_ip_by_index(uint32_t netif_index); // Get IPv6 address of interface by its index (ifindex) // POSIX фильтрует по IFA_F_TEMPORARY в ifa_flags, но getifaddrs не гарантирует адресные флаги; // для точного выбора temporary/stable на Linux использовать get_interface_ipv6_addr_nl(). // Windows игнорирует temporary: предпочитает глобальный адрес, затем локальный, исключая link-local/loopback/::. // Returns 0 on success (fills addr_out with 16 bytes), -1 on error or not found int get_interface_ipv6_by_index(uint32_t netif_index, int temporary, uint8_t* addr_out); // Выбрать IPv6: Linux использует netlink с IFA_F_*, прочие POSIX — getifaddrs(), Windows — IP Helper. // POSIX: prefer_stable=1 предпочитает постоянный адрес, 0 — temporary; допускается fallback на другой тип. // Windows игнорирует prefer_stable, выбор как у get_interface_ipv6_by_index(). // Returns 0 on success (fills addr_out with 16 bytes), -1 on error int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t* addr_out); // Determine the interface index for the default route (outgoing traffic) // POSIX: UDP connect к 8.8.8.8:53 или [2001:4860:4860::8888]:53, сопоставление локального IP с getifaddrs(). // Windows: GetBestInterfaceEx к тому же адресу. Результат — выбранный маршрут к этому адресу, // который может отличаться от default route при более специфичных маршрутах. // family: AF_INET or AF_INET6, returns ifindex (0 on failure) uint32_t get_default_route_netif_index(int family); // Классифицирует IPv4 (network byte order) по встроенному списку: 0 для 0/8, 10/8, 127/8, 169.254/16, // 172.16/12, 192.168/16, 100.64/10, 224/4 и 240/4; 1 для остальных. Это не проверка достижимости адреса. int ip_is_public(uint32_t addr_be); // Cross-platform mkdir #ifdef _WIN32 #include #define utun_mkdir(path, mode) _mkdir(path) #else #include #define utun_mkdir(path, mode) mkdir(path, mode) #endif #ifdef __cplusplus } #endif #endif // PLATFORM_COMPAT_H