diff --git a/CMakeLists.txt b/CMakeLists.txt index d78795898045cf06935d6c780b9e9d5544eb1e3c..682501b68bb24ecabe808f6efda5cf9b5e07cf4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,26 +14,69 @@ set (libdir "${CMAKE_INSTALL_FULL_LIBDIR}") set (includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}") set (VERSION ${CMAKE_PROJECT_VERSION}) -find_package (PkgConfig REQUIRED) +option(DHTNET_PUPNP "Enable UPnP support" ON) +option(DHTNET_NATPMP "Enable NAT-PMP support" ON) +option(DHTNET_TESTABLE "Enable API for tests" ON) +option(BUILD_TOOLS "Build tools" ON) -find_package(msgpack-cxx CONFIG) -if(msgpack-cxx_FOUND) - set(MSGPACK_LIB msgpack-cxx) -else() - find_package(msgpackc-cxx CONFIG REQUIRED NAMES msgpackc-cxx msgpack) - set(MSGPACK_LIB msgpackc-cxx) -endif() +if (NOT MSVC) + find_package (PkgConfig REQUIRED) -find_package(fmt) -pkg_check_modules (opendht REQUIRED IMPORTED_TARGET opendht>=2.6.0) -pkg_check_modules (pjproject REQUIRED IMPORTED_TARGET libpjproject) + find_package(msgpack-cxx CONFIG) + if(msgpack-cxx_FOUND) + set(MSGPACK_LIB msgpack-cxx) + else() + find_package(msgpackc-cxx CONFIG REQUIRED NAMES msgpackc-cxx msgpack) + set(MSGPACK_LIB msgpackc-cxx) + endif() -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_NO_BOOST -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT") + find_package(fmt) + pkg_check_modules (opendht REQUIRED IMPORTED_TARGET opendht>=2.6.0) + pkg_check_modules (pjproject REQUIRED IMPORTED_TARGET libpjproject) +else() + set (WIN32_DEP_DIR ${PROJECT_SOURCE_DIR}/../) + include_directories( + ${WIN32_DEP_DIR}/../msvc/include + ${WIN32_DEP_DIR}/msgpack-c/include + ${WIN32_DEP_DIR}/asio/asio/include + ${WIN32_DEP_DIR}/fmt/include + ${WIN32_DEP_DIR}/pjproject/pjlib/include + ${WIN32_DEP_DIR}/pjproject/pjlib-util/include + ${WIN32_DEP_DIR}/pjproject/pjnath/include + ${WIN32_DEP_DIR}/opendht/include + ${WIN32_DEP_DIR}/opendht/src/compat/msvc + ${WIN32_DEP_DIR}/openssl/include + ${WIN32_DEP_DIR}/restinio/dev + ${WIN32_DEP_DIR}/http_parser + ${WIN32_DEP_DIR}/pupnp/include + ${WIN32_DEP_DIR}/natpmp/include + ) + # windirent.h + include_directories(include/compat/msvc) +endif() -option(BUILD_TOOLS "Build dnc" ON) -option(DHTNET_PUPNP "Enable UPnP support" ON) -option(DHTNET_NATPMP "Enable NAT-PMP support" ON) -option(DHTNET_TESTABLE "Enable API for tests" ON) +if (NOT MSVC) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ + -DMSGPACK_NO_BOOST \ + -DMSGPACK_DISABLE_LEGACY_NIL \ + -DMSGPACK_DISABLE_LEGACY_CONVERT") +else() + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ + -DGNUTLS_INTERNAL_BUILD \ + -D_USE_MATH_DEFINES \ + -D_SCL_SECURE_NO_WARNINGS \ + -D_CRT_SECURE_NO_WARNINGS \ + -D_WINSOCK_DEPRECATED_NO_WARNINGS \ + -DASIO_STANDALONE \ + -DWIN32_LEAN_AND_MEAN \ + -D_WIN32_WINNT=0x0601 \ + -DNATPMP_STATICLIB \ + -DMSGPACK_NO_BOOST \ + -DMSGPACK_DISABLE_LEGACY_NIL \ + -DMSGPACK_DISABLE_LEGACY_CONVERT \ + -DUNICODE \ + -D_UNICODE") +endif() # Sources list (APPEND dhtnet_SOURCES @@ -73,20 +116,15 @@ list (APPEND dhtnet_HEADERS include/upnp/upnp_control.h ) -if (DHTNET_PUPNP) +# Port mapping dependencies - add sources and libraries +if (DHTNET_PUPNP AND NOT MSVC) pkg_search_module (upnp IMPORTED_TARGET upnp libupnp) if (NOT upnp_FOUND) message("libupnp not found: disabling") set(DHTNET_PUPNP Off) - else() - list (APPEND dhtnet_SOURCES - src/upnp/protocol/pupnp/pupnp.cpp - src/upnp/protocol/pupnp/upnp_igd.cpp - ) - set (requiresprivate "${requiresprivate} libupnp") endif() endif() -if (DHTNET_NATPMP) +if (DHTNET_NATPMP AND NOT MSVC) pkg_search_module (natpmp IMPORTED_TARGET natpmp) if (NOT natpmp_FOUND) find_library(natpmp_LIBRARIES natpmp) @@ -97,17 +135,35 @@ if (DHTNET_NATPMP) message("NAT-PMP found: ${natpmp_LIBRARIES}") endif() endif() - if (DHTNET_NATPMP) - list (APPEND dhtnet_SOURCES - src/upnp/protocol/natpmp/nat_pmp.cpp - src/upnp/protocol/natpmp/pmp_igd.cpp - ) - set (libsprivate "${libsprivate} ${natpmp_LIBRARIES}") - endif() +endif() + +if (DHTNET_PUPNP) + list (APPEND dhtnet_PRIVATE_DEFS HAVE_LIBUPNP) + list (APPEND dhtnet_SOURCES + src/upnp/protocol/pupnp/pupnp.cpp + src/upnp/protocol/pupnp/upnp_igd.cpp + ) + list (APPEND dhtnet_PRIVATELIBS ${upnp_LIBRARIES}) +endif() +if (DHTNET_NATPMP) + list (APPEND dhtnet_PRIVATE_DEFS HAVE_LIBNATPMP) + list (APPEND dhtnet_SOURCES + src/upnp/protocol/natpmp/nat_pmp.cpp + src/upnp/protocol/natpmp/pmp_igd.cpp + ) + list (APPEND dhtnet_PRIVATELIBS ${natpmp_LIBRARIES}) endif() add_library(dhtnet ${dhtnet_SOURCES}) -target_link_libraries(dhtnet PUBLIC PkgConfig::opendht PkgConfig::pjproject fmt::fmt ${MSGPACK_LIB}) +if (NOT MSVC) + target_link_libraries(dhtnet PUBLIC PkgConfig::opendht PkgConfig::pjproject fmt::fmt ${MSGPACK_LIB}) +else() + target_link_libraries(dhtnet PUBLIC + ${WIN32_DEP_DIR}/../msvc/lib/libopendht.lib + ${WIN32_DEP_DIR}/../msvc/lib/libpjproject.lib + ${WIN32_DEP_DIR}/../msvc/lib/libfmt.lib + ${WIN32_DEP_DIR}/../msvc/lib/libmsgpackc-cxx.lib) +endif() if (APPLE) target_link_libraries(dhtnet PRIVATE "-framework CoreFoundation" "-framework Security" "resolv") endif() @@ -116,19 +172,35 @@ target_include_directories(dhtnet PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include> ) -if (DHTNET_PUPNP) - target_compile_definitions(dhtnet PRIVATE HAVE_LIBUPNP) - target_link_libraries(dhtnet PRIVATE PkgConfig::upnp) -endif() -if (DHTNET_NATPMP) - target_compile_definitions(dhtnet PRIVATE HAVE_LIBNATPMP) - target_link_libraries(dhtnet PRIVATE ${natpmp_LIBRARIES}) + +target_compile_definitions(dhtnet PRIVATE ${dhtnet_PRIVATE_DEFS}) +target_link_libraries(dhtnet PRIVATE ${dhtnet_PRIVATELIBS}) +if (MSVC) + target_compile_definitions(dhtnet PRIVATE + _CRT_SECURE_NO_WARNINGS + _WINSOCK_DEPRECATED_NO_WARNINGS + ASIO_STANDALONE + _WIN32_WINNT=0x0601 + MSGPACK_NO_BOOST + MSGPACK_DISABLE_LEGACY_NIL + MSGPACK_DISABLE_LEGACY_CONVERT + DHTNET_STATIC + DHTNET_STATIC_DEFINE + DHTNET_EXPORTS + DHTNET_BUILDING + DHT) + target_compile_options(dhtnet PRIVATE + /O2; /Oi; ${DEFAULT_CXX_RUNTIME_LIBRARY}; /Gy; /MP; /Oy-; /sdl-; /W0; + /FC; /FS; /nologo; /Zi; /wd4996; /wd4503; /wd4180; /wd4244; /wd4267; + /Zc:__cplusplus; + ${DEFAULT_CXX_EXCEPTION_HANDLING}) +else() + target_compile_definitions(dhtnet PUBLIC PJ_AUTOCONF=1) endif() -if (DHTNET_TESTABLE) + +if (BUILD_TESTING AND NOT MSVC) target_compile_definitions(dhtnet PUBLIC DHTNET_TESTABLE) endif() -target_compile_definitions(dhtnet PUBLIC PJ_AUTOCONF=1) -# set_target_properties(dhtnet PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/include/${dhtnet_HEADERS}") configure_file(dhtnet.pc.in dhtnet.pc @ONLY) @@ -137,7 +209,7 @@ install(TARGETS dhtnet) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dhtnet) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dhtnet.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -if (BUILD_TOOLS) +if (BUILD_TOOLS AND NOT MSVC) add_executable(dnc tools/dnc/main.cpp tools/dnc/dnc.cpp diff --git a/include/compat/msvc/windirent.h b/include/compat/msvc/windirent.h new file mode 100644 index 0000000000000000000000000000000000000000..ee9f5528537b23828dea59dd0d34f819e29fc597 --- /dev/null +++ b/include/compat/msvc/windirent.h @@ -0,0 +1,885 @@ +/* + * Dirent interface for Microsoft Visual Studio + * Version 1.21 + * + * Copyright (C) 2006-2012 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> +#include <string.h> +#include <stdlib.h> +#include <malloc.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +#define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +#define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +#define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +#define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +#define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +#define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +#define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +#define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +#define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +#define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +#define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +#define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +#define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +#define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +#define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +#define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +#define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +#define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +#define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +#define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +#define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +#define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +#define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +#define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +#define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) &S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +#define S_ISFIFO(mode) (((mode) &S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +#define S_ISDIR(mode) (((mode) &S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +#define S_ISREG(mode) (((mode) &S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +#define S_ISLNK(mode) (((mode) &S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +#define S_ISSOCK(mode) (((mode) &S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +#define S_ISCHR(mode) (((mode) &S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +#define S_ISBLK(mode) (((mode) &S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of d_namlen without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return number of bytes needed to store d_namlen */ +#define _D_ALLOC_NAMLEN(p) (PATH_MAX) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Wide-character version */ +struct _wdirent +{ + /* Always zero */ + long d_ino; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR +{ + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t* patt; +}; +typedef struct _WDIR _WDIR; + +static _WDIR* _wopendir(const wchar_t* dirname); +static struct _wdirent* _wreaddir(_WDIR* dirp); +static int _wclosedir(_WDIR* dirp); +static void _wrewinddir(_WDIR* dirp); + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + +/* Multi-byte character versions */ +struct dirent +{ + /* Always zero */ + long d_ino; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX]; +}; +typedef struct dirent dirent; + +struct DIR +{ + struct dirent ent; + struct _WDIR* wdirp; +}; +typedef struct DIR DIR; + +static DIR* opendir(const char* dirname); +static struct dirent* readdir(DIR* dirp); +static int closedir(DIR* dirp); +static void rewinddir(DIR* dirp); + +/* Internal utility functions */ +static WIN32_FIND_DATAW* dirent_first(_WDIR* dirp); +static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp); + +static int dirent_mbstowcs_s( + size_t* pReturnValue, wchar_t* wcstr, size_t sizeInWords, const char* mbstr, size_t count); + +static int dirent_wcstombs_s( + size_t* pReturnValue, char* mbstr, size_t sizeInBytes, const wchar_t* wcstr, size_t count); + +static void dirent_set_errno(int error); + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir(const wchar_t* dirname) +{ + _WDIR* dirp = NULL; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc(sizeof(struct _WDIR)); + if (dirp != NULL) { + DWORD n; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + n = wcslen(dirname); +#else + n = GetFullPathNameW(dirname, 0, NULL, NULL); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16); + if (dirp->patt) { + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + wcsncpy_s(dirp->patt, n + 1, dirname, n); +#else + n = GetFullPathNameW(dirname, n, dirp->patt, NULL); +#endif + if (n > 0) { + wchar_t* p; + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + if (dirp->patt < p) { + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (dirent_first(dirp)) { + /* Directory stream opened successfully */ + error = 0; + } else { + /* Cannot retrieve first entry */ + error = 1; + dirent_set_errno(ENOENT); + } + + } else { + /* Cannot retrieve full path name */ + dirent_set_errno(ENOENT); + error = 1; + } + + } else { + /* Cannot allocate memory for search pattern */ + error = 1; + } + + } else { + /* Cannot allocate _WDIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + _wclosedir(dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. The directory entry is returned in dirent + * structure in the d_name field. Individual directory entries returned by + * this function include regular files, sub-directories, pseudo-directories + * "." and ".." as well as volume labels, hidden files and system files. + */ +static struct _wdirent* +_wreaddir(_WDIR* dirp) +{ + WIN32_FIND_DATAW* datap; + struct _wdirent* entp; + + /* Read next directory entry */ + datap = dirent_next(dirp); + if (datap) { + size_t n; + DWORD attr; + + /* Pointer to directory entry to return */ + entp = &dirp->ent; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { + entp->d_name[n] = datap->cFileName[n]; + n++; + } + dirp->ent.d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof(struct _wdirent); + + } else { + /* Last directory entry read */ + entp = NULL; + } + + return entp; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir(_WDIR* dirp) +{ + int ok; + if (dirp) { + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + /* Release search pattern */ + if (dirp->patt) { + free(dirp->patt); + dirp->patt = NULL; + } + + /* Release directory structure */ + free(dirp); + ok = /*success*/ 0; + + } else { + /* Invalid directory stream */ + dirent_set_errno(EBADF); + ok = /*failure*/ -1; + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir(_WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + } + + /* Open new search handle */ + dirent_first(dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first(_WDIR* dirp) +{ + WIN32_FIND_DATAW* datap; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW(dirp->patt, + FindExInfoStandard, + &dirp->data, + FindExSearchNameMatch, + NULL, + 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + /* Failed to re-open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + } + return datap; +} + +/* Get next directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_next(_WDIR* dirp) +{ + WIN32_FIND_DATAW* p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + /* Get the next directory entry from stream */ + if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + /* End of directory stream reached */ + p = NULL; + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir(const char* dirname) +{ + struct DIR* dirp; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc(sizeof(struct DIR)); + if (dirp) { + wchar_t wname[PATH_MAX]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s(&n, wname, PATH_MAX, dirname, PATH_MAX); + if (!error) { + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir(wname); + if (dirp->wdirp) { + /* Directory stream opened */ + error = 0; + } else { + /* Failed to open directory stream */ + error = 1; + } + + } else { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + error = 1; + } + + } else { + /* Cannot allocate DIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + free(dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + * + * When working with text consoles, please note that file names returned by + * readdir() are represented in the default ANSI code page while any output to + * console is typically formatted on another code page. Thus, non-ASCII + * characters in file names will not usually display correctly on console. The + * problem can be fixed in two ways: (1) change the character set of console + * to 1252 using chcp utility and use Lucida Console font, or (2) use + * _cprintf function when writing to console. The _cprinf() will re-encode + * ANSI strings to the console code page so many non-ASCII characters will + * display correcly. + */ +static struct dirent* +readdir(DIR* dirp) +{ + WIN32_FIND_DATAW* datap; + struct dirent* entp; + + /* Read next directory entry */ + datap = dirent_next(dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s(&n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s(&n, + dirp->ent.d_name, + PATH_MAX, + datap->cAlternateFileName, + PATH_MAX); + } + + if (!error) { + DWORD attr; + + /* Initialize directory entry for return */ + entp = &dirp->ent; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof(struct dirent); + + } else { + /* + * Cannot convert file name to multi-byte string so construct + * an errornous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entp = &dirp->ent; + entp->d_name[0] = '?'; + entp->d_name[1] = '\0'; + entp->d_namlen = 1; + entp->d_type = DT_UNKNOWN; + entp->d_ino = 0; + entp->d_reclen = 0; + } + + } else { + /* No more directory entries */ + entp = NULL; + } + + return entp; +} + +/* + * Close directory stream. + */ +static int +closedir(DIR* dirp) +{ + int ok; + if (dirp) { + /* Close wide-character directory stream */ + ok = _wclosedir(dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free(dirp); + + } else { + /* Invalid directory stream */ + dirent_set_errno(EBADF); + ok = /*failure*/ -1; + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir(DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir(dirp->wdirp); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t* pReturnValue, wchar_t* wcstr, size_t sizeInWords, const char* mbstr, size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs(wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resuting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + /* Could not convert string */ + error = 1; + } + +#endif + + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s(size_t* pReturnValue, + char* mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t* wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs(mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + /* Cannot convert string */ + error = 1; + } + +#endif + + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno(int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno(error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/include/ice_transport.h b/include/ice_transport.h index c5047b3b33696a10e517e4a50c0a6c0b44590a71..8c69e93f4521dcda0cf389dc67556a493e98c670 100644 --- a/include/ice_transport.h +++ b/include/ice_transport.h @@ -26,6 +26,11 @@ #include <vector> #include <chrono> +#if defined(_MSC_VER) +#include <BaseTsd.h> +using ssize_t = SSIZE_T; +#endif + extern "C" { struct pj_ice_sess_cand; } diff --git a/src/ip_utils.cpp b/src/ip_utils.cpp index ad031230b54259b1b16f697d9d94c24b26a404a3..9cd5c0f47c3fe8f6df3149a8433c3ab9e7392b30 100644 --- a/src/ip_utils.cpp +++ b/src/ip_utils.cpp @@ -69,7 +69,6 @@ ip_utils::getHostname() return hostname; } - ip_utils::IpInterfaceAddress ip_utils::getHostName() { @@ -80,42 +79,47 @@ ip_utils::getHostName() struct hostent* h = NULL; struct sockaddr_in localAddr; memset(&localAddr, 0, sizeof(localAddr)); - gethostname(out, out_len); + char out[256]; + if (gethostname(out, sizeof(out)) == SOCKET_ERROR) { + return {}; + } h = gethostbyname(out); if (h != NULL) { - memcpy(&localAddr.sin_addr, h->h_addr_list[0], 4); + memcpy(&localAddr.sin_addr, h->h_addr_list[0], sizeof(localAddr.sin_addr)); p = inet_ntop(AF_INET, &localAddr.sin_addr, tempstr, sizeof(tempstr)); - if (p) + if (p) { ret.address = p; + } else { + return {}; + } + } else { + return {}; } #elif (defined(BSD) && BSD >= 199306) || defined(__FreeBSD_kernel__) struct ifaddrs* ifap; struct ifaddrs* ifa; - if (getifaddrs(&ifap) != 0) + if (getifaddrs(&ifap) != 0) { return {}; + } // Cycle through available interfaces. for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { // Skip loopback, point-to-point and down interfaces. // except don't skip down interfaces if we're trying to get // a list of configurable interfaces. - if ((ifa->ifa_flags & IFF_LOOPBACK) || (!(ifa->ifa_flags & IFF_UP))) + if ((ifa->ifa_flags & IFF_LOOPBACK) || (!(ifa->ifa_flags & IFF_UP))) { continue; + } auto family = ifa->ifa_addr->sa_family; if (family == AF_INET) { - void* addr; - if (family == AF_INET) { - if (((struct sockaddr_in*) (ifa->ifa_addr))->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) - continue; - addr = &((struct sockaddr_in*) (ifa->ifa_addr))->sin_addr; + void* addr = &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr; + if (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { + continue; } - ret.interface = ifa->ifa_name; - p = inet_ntop(family, - addr, - tempstr, - sizeof(tempstr)); - if (p) + p = inet_ntop(family, addr, tempstr, sizeof(tempstr)); + if (p) { ret.address = p; + } break; } } @@ -133,21 +137,22 @@ ip_utils::getHostName() memset(&localAddr, 0, sizeof(localAddr)); // Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. localSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (localSock == INVALID_SOCKET) - return ret; + if (localSock == INVALID_SOCKET) { + return {}; + } /* Get the interface configuration information... */ - ifConf.ifc_len = (int) sizeof szBuffer; - ifConf.ifc_ifcu.ifcu_buf = (caddr_t) szBuffer; + ifConf.ifc_len = sizeof(szBuffer); + ifConf.ifc_buf = (caddr_t)szBuffer; nResult = ioctl(localSock, SIOCGIFCONF, &ifConf); if (nResult < 0) { close(localSock); - return ret; + return {}; } unsigned int i; unsigned int j = 0; // Cycle through the list of interfaces looking for IP addresses. - for (i = 0u; i < (unsigned int) ifConf.ifc_len && j < MIN_INTERFACE;) { - struct ifreq* pifReq = (struct ifreq*) ((caddr_t) ifConf.ifc_req + i); + for (i = 0u; i < ifConf.ifc_len && j < MIN_INTERFACE;) { + struct ifreq* pifReq = (struct ifreq*)((caddr_t)ifConf.ifc_req + i); i += sizeof *pifReq; // See if this is the sort of interface we want to deal with. memset(ifReq.ifr_name, 0, sizeof(ifReq.ifr_name)); @@ -156,8 +161,9 @@ ip_utils::getHostName() // Skip loopback, point-to-point and down interfaces. // except don't skip down interfaces if we're trying to get // a list of configurable interfaces. - if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP))) + if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP))) { continue; + } if (pifReq->ifr_addr.sa_family == AF_INET) { if (((sockaddr_in*)&pifReq->ifr_addr)->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { // We don't want the loopback interface. Go to the next one. @@ -174,6 +180,9 @@ ip_utils::getHostName() j++; // Increment j if we found an address which is not loopback and is up. } close(localSock); + if (p == NULL) { + return {}; + } #endif return ret; }