From b5ee6aa8fb49986b549c1a3cebea3d88613d4a40 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 13:49:55 +0200 Subject: [PATCH 01/10] m4/nut_check_headers_windows.m4: introduce NUT_CHECK_HEADER_IPHLPAPI [#2516] Signed-off-by: Jim Klimov --- m4/nut_check_headers_windows.m4 | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/m4/nut_check_headers_windows.m4 b/m4/nut_check_headers_windows.m4 index ce0286d045..4d52a15961 100644 --- a/m4/nut_check_headers_windows.m4 +++ b/m4/nut_check_headers_windows.m4 @@ -208,3 +208,41 @@ AC_DEFUN([NUT_CHECK_HEADER_WS2TCPIP], [ esac AM_CONDITIONAL(HAVE_WS2TCPIP_H, test "x$nut_cv_header_ws2tcpip_h" = xyes) ]) + +dnl NUT_CHECK_HEADER_IPHLPAPI +dnl ------------------------------------------------- +dnl Check for compilable and valid iphlpapi.h header + +AC_DEFUN([NUT_CHECK_HEADER_IPHLPAPI], [ + AC_REQUIRE([NUT_CHECK_HEADER_WINSOCK2])dnl + AC_CACHE_CHECK([for iphlpapi.h], [nut_cv_header_iphlpapi_h], [ + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ +#undef inline +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include + ]],[[ + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + IP_ADAPTER_PREFIX *pPrefix = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + ]]) + ],[ + nut_cv_header_iphlpapi_h="yes" + ],[ + nut_cv_header_iphlpapi_h="no" + ]) + AC_LANG_POP([C]) + ]) + AS_CASE([$nut_cv_header_iphlpapi_h], + [yes], [ + AC_DEFINE_UNQUOTED(HAVE_IPHLPAPI_H, 1, + [Define to 1 if you have the iphlpapi.h header file.]) + ] + ) + AM_CONDITIONAL(HAVE_IPHLPAPI_H, test "x$nut_cv_header_iphlpapi_h" = xyes) +]) From 1ff7c5285dba0e5b468f03324ae7687fb367e40b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 13:50:39 +0200 Subject: [PATCH 02/10] configure.ac: avoid checking "${PWD}" as a tool name (is a shell builtin) Signed-off-by: Jim Klimov --- configure.ac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 1c0e51a5d5..143fad0bc8 100644 --- a/configure.ac +++ b/configure.ac @@ -4950,10 +4950,10 @@ AS_CASE([${target_os}], tmp="`${CYGPATH} -m "${ABS_TOP_SRCDIR}" | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_SRCDIR="$tmp" ],[ dnl MSYS pwd with -W option to resolve - AC_CHECK_TOOL([PWD], [pwd], [none]) - AS_IF([test "x${PWD}" != "xnone"], [ - tmp="`(cd "${ABS_TOP_BUILDDIR}" && ${PWD} -W) | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_BUILDDIR="$tmp" - tmp="`(cd "${ABS_TOP_SRCDIR}" && ${PWD} -W) | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_SRCDIR="$tmp" + AC_CHECK_TOOL([PWDTOOL], [pwd], [none]) + AS_IF([test "x${PWDTOOL}" != "xnone"], [ + tmp="`(cd "${ABS_TOP_BUILDDIR}" && ${PWDTOOL} -W) | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_BUILDDIR="$tmp" + tmp="`(cd "${ABS_TOP_SRCDIR}" && ${PWDTOOL} -W) | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_SRCDIR="$tmp" ]) ]) ]) From 4004eedc8a97d0fc707f3aaefabbe13449a80d4a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 14:33:12 +0200 Subject: [PATCH 03/10] configure.ac: clarify why we call "cygpath" or "pwd -W", constrain their warnings if they fail (wrong "pwd"), and report the outcome Signed-off-by: Jim Klimov --- configure.ac | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 143fad0bc8..4dc681bfbb 100644 --- a/configure.ac +++ b/configure.ac @@ -4943,19 +4943,23 @@ dnl When building ON Windows (mingw/MSYS2, cygwin, etc.) fudge these dnl path strings back to what native OS methods would recognize. AS_CASE([${target_os}], [*mingw*], [ + AC_MSG_NOTICE([Will try to resolve Windows paths to ABS_TOP_SRCDIR and ABS_TOP_BUILDDIR with cygpath or mingw/msys pwd -W tool (would fail if not a native build)]) + dnl Cygwin path resolver AC_CHECK_TOOL([CYGPATH], [cygpath], [none]) - AS_IF([test "x${CYGPATH}" != "xnone"], [ + AS_IF([test "x${CYGPATH}" != "xnone" && test x"`${CYGPATH}`" != x], [ tmp="`${CYGPATH} -m "${ABS_TOP_BUILDDIR}" | sed -e 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_BUILDDIR="$tmp" tmp="`${CYGPATH} -m "${ABS_TOP_SRCDIR}" | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_SRCDIR="$tmp" ],[ dnl MSYS pwd with -W option to resolve AC_CHECK_TOOL([PWDTOOL], [pwd], [none]) - AS_IF([test "x${PWDTOOL}" != "xnone"], [ + AS_IF([test "x${PWDTOOL}" != "xnone" && test x"`${PWDTOOL} -W`" != x], [ tmp="`(cd "${ABS_TOP_BUILDDIR}" && ${PWDTOOL} -W) | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_BUILDDIR="$tmp" tmp="`(cd "${ABS_TOP_SRCDIR}" && ${PWDTOOL} -W) | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_SRCDIR="$tmp" ]) ]) + + AC_MSG_NOTICE([FWIW, assuming ABS_TOP_SRCDIR="$ABS_TOP_SRCDIR" and ABS_TOP_BUILDDIR="$ABS_TOP_BUILDDIR"]) ]) dnl Use these at best for tests (e.g. nutconf), not production code: From 4184fd6006224584e40f8dd5af28f2b25fe61dbf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 14:34:22 +0200 Subject: [PATCH 04/10] configure.ac, tools/nut-scanner/Makefile.am: detect WIN32 support of newer GetAdaptersAddresses() and/or older GetAdaptersInfo() [#2516] Signed-off-by: Jim Klimov --- configure.ac | 134 ++++++++++++++++++++++++++++++++++ tools/nut-scanner/Makefile.am | 2 + 2 files changed, 136 insertions(+) diff --git a/configure.ac b/configure.ac index 4dc681bfbb..c1efc09c15 100644 --- a/configure.ac +++ b/configure.ac @@ -1470,6 +1470,140 @@ AS_IF([test x"${ac_cv_struct_pollfd}" = xyes], ] ) +NETLIBS_GETADDRS="" +dnl For `nut-scanner -m auto` modes, see also: +dnl https://stackoverflow.com/a/41151132/4715872 +dnl https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses (since ~Windows Vista) +dnl https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersinfo (before Windows XP; not recommended later) +AC_CHECK_FUNCS([getifaddrs], [], [ + AS_CASE([${target_os}], + [*mingw*], [ + dnl Check for GetAdaptersAddresses / GetAdaptersInfo + NUT_CHECK_HEADER_IPHLPAPI + AS_IF([test x"${nut_cv_header_iphlpapi_h}" = xyes], [ + + myIPHLPAPI_TEST_HEADERS=' +#if HAVE_WINDOWS_H +# undef inline +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# if HAVE_WINSOCK2_H +# include +# endif +# if HAVE_IPHLPAPI_H +# include +# endif +#endif +#include +' + + myIPHLPAPI_TEST_GAA=' +/* ULONG GetAdaptersAddresses(ULONG af, ULONG flags, void* rsvd, PIP_ADAPTER_ADDRESSES addrs, PULONG sizeptr); */ +IP_ADAPTER_ADDRESSES buf@<:@8@:>@; +ULONG bufsz = sizeof(buf); +printf("%ld ", GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST, NULL, buf, &bufsz)); +printf("%ld ", GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_DNS_SERVER, NULL, buf, &bufsz)); +printf("%ld ", GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_ANYCAST, NULL, buf, &bufsz)) +/* autoconf adds ";return 0;" */ +' + + AC_CACHE_CHECK([for GetAdaptersAddresses() with IPv4 and IPv6 support], + [ac_cv_func_GetAdaptersAddresses], + [AC_LANG_PUSH([C]) + dnl e.g. add "-lws2_32" for mingw builds, maybe "-liphlpapi" + dnl the NETLIBS are set by NUT_CHECK_SOCKETLIB above + SAVED_LIBS="$LIBS" + LIBS="$LIBS $NETLIBS" + AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM( + [${myIPHLPAPI_TEST_HEADERS}], + [${myIPHLPAPI_TEST_GAA}])], + [ac_cv_func_GetAdaptersAddresses=yes + ], [ + NETLIBS_GETADDRS="-liphlpapi" + LIBS="$LIBS $NETLIBS $NETLIBS_GETADDRS" + AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM( + [${myIPHLPAPI_TEST_HEADERS}], + [${myIPHLPAPI_TEST_GAA}])], + [ + ac_cv_func_GetAdaptersAddresses=yes + ], [ + ac_cv_func_GetAdaptersAddresses=no + NETLIBS_GETADDRS="" + ] + ) + ] + ) + AC_LANG_POP([C]) + LIBS="$SAVED_LIBS" + ]) + AS_IF([test x"${ac_cv_func_GetAdaptersAddresses}" = xyes], + [AC_DEFINE([HAVE_GETADAPTERSADDRESSES], 1, [defined if system has the GetAdaptersAddresses() method])], + [dnl AC_MSG_WARN([WIN32 library routine GetAdaptersAddresses() not found]) + AS_CASE([${target_os}], + [*mingw*], [AC_MSG_WARN([Windows antivirus might block this test])] + ) + ] + ) + + myIPHLPAPI_TEST_GAI=' +/* ULONG GetAdaptersInfo(PIP_ADAPTER_INFO addrs, PULONG sizeptr); */ +IP_ADAPTER_INFO buf@<:@8@:>@; +ULONG bufsz = sizeof(buf); +printf("%ld ", GetAdaptersInfo(buf, &bufsz)) +/* autoconf adds ";return 0;" */ +' + + AC_CACHE_CHECK([for GetAdaptersInfo() with IPv4 only support], + [ac_cv_func_GetAdaptersInfo], + [AC_LANG_PUSH([C]) + dnl e.g. add "-lws2_32" for mingw builds + dnl the NETLIBS are set by NUT_CHECK_SOCKETLIB above + SAVED_LIBS="$LIBS" + LIBS="$LIBS $NETLIBS $NETLIBS_GETADDRS" + AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM( + [${myIPHLPAPI_TEST_HEADERS}], + [${myIPHLPAPI_TEST_GAI}])], + [ac_cv_func_GetAdaptersInfo=yes + ], [ + NETLIBS_GETADDRS="-liphlpapi" + LIBS="$LIBS $NETLIBS $NETLIBS_GETADDRS" + AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM( + [${myIPHLPAPI_TEST_HEADERS}], + [${myIPHLPAPI_TEST_GAI}])], + [ + ac_cv_func_GetAdaptersInfo=yes + ], [ + ac_cv_func_GetAdaptersInfo=no + NETLIBS_GETADDRS="" + ] + ) + ] + ) + AC_LANG_POP([C]) + LIBS="$SAVED_LIBS" + ]) + AS_IF([test x"${ac_cv_func_GetAdaptersInfo}" = xyes], + [AC_DEFINE([HAVE_GETADAPTERSINFO], 1, [defined if system has the GetAdaptersInfo() method])], + [dnl AC_MSG_WARN([WIN32 library routine GetAdaptersInfo() not found]) + AS_CASE([${target_os}], + [*mingw*], [AC_MSG_WARN([Windows antivirus might block this test])] + ) + ] + ) + + ]) + ] + )] +) +AC_SUBST([NETLIBS_GETADDRS]) + + dnl ---------------------------------------------------------------------- dnl Check for Python binary program names per language version dnl to embed into scripts and Make rules diff --git a/tools/nut-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index 480c104762..aa7965236c 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -75,6 +75,8 @@ if HAVE_WINDOWS libnutscan_la_LIBADD += $(top_builddir)/common/libnutwincompat.la libnutscan_la_LDFLAGS += -no-undefined endif HAVE_WINDOWS +# Technically, we might only have this one set on Windows so far... +libnutscan_la_LDFLAGS += @NETLIBS_GETADDRS@ # # Below we set API versions of public libraries From 3710a766e87f455f91b879af558328d92a3ae2b6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 21:41:09 +0200 Subject: [PATCH 05/10] configure.ac: fix location of NUT_CHECK_HEADER_IPHLPAPI call [#2516] Signed-off-by: Jim Klimov --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c1efc09c15..6afc03c33c 100644 --- a/configure.ac +++ b/configure.ac @@ -1475,11 +1475,12 @@ dnl For `nut-scanner -m auto` modes, see also: dnl https://stackoverflow.com/a/41151132/4715872 dnl https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses (since ~Windows Vista) dnl https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersinfo (before Windows XP; not recommended later) +dnl Must check in global context, to have it not-defined where appropriate too +NUT_CHECK_HEADER_IPHLPAPI AC_CHECK_FUNCS([getifaddrs], [], [ AS_CASE([${target_os}], [*mingw*], [ dnl Check for GetAdaptersAddresses / GetAdaptersInfo - NUT_CHECK_HEADER_IPHLPAPI AS_IF([test x"${nut_cv_header_iphlpapi_h}" = xyes], [ myIPHLPAPI_TEST_HEADERS=' From 2df35f8a89b628ac6d09533727c19be9201b4412 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 15:56:52 +0200 Subject: [PATCH 06/10] common/common.c: revise debug-tracing of get_libname*() methods Signed-off-by: Jim Klimov --- common/common.c | 82 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/common/common.c b/common/common.c index 1ce56aecd1..f7ffee93d9 100644 --- a/common/common.c +++ b/common/common.c @@ -3193,24 +3193,33 @@ static char * get_libname_in_dir(const char* base_libname, size_t base_libname_l char *libname_path = NULL, *libname_alias = NULL; char current_test_path[LARGEBUF]; + upsdebugx(3, "%s('%s', %" PRIuSIZE ", '%s', %i): Entering method...", + __func__, base_libname, base_libname_length, dirname, index); + memset(current_test_path, 0, LARGEBUF); if ((dp = opendir(dirname)) == NULL) { if (index >= 0) { - upsdebugx(5,"NOT looking for lib %s in unreachable directory #%d : %s", - base_libname, index, dirname); + upsdebugx(5, "%s: NOT looking for lib %s in " + "unreachable directory #%d : %s", + __func__, base_libname, index, dirname); } else { - upsdebugx(5,"NOT looking for lib %s in unreachable directory : %s", - base_libname, dirname); + upsdebugx(5, "%s: NOT looking for lib %s in " + "unreachable directory : %s", + __func__, base_libname, dirname); } return NULL; } if (index >= 0) { - upsdebugx(2,"Looking for lib %s in directory #%d : %s", base_libname, index, dirname); + upsdebugx(4, "%s: Looking for lib %s in directory #%d : %s", + __func__, base_libname, index, dirname); } else { - upsdebugx(2,"Looking for lib %s in directory : %s", base_libname, dirname); + upsdebugx(4, "%s: Looking for lib %s in directory : %s", + __func__, base_libname, dirname); } + + /* TODO: Try a quick stat() first? */ while ((dirp = readdir(dp)) != NULL) { #if !HAVE_DECL_REALPATH @@ -3218,7 +3227,8 @@ static char * get_libname_in_dir(const char* base_libname, size_t base_libname_l #endif int compres; - upsdebugx(5,"Comparing lib %s with dirpath entry %s", base_libname, dirp->d_name); + upsdebugx(5, "%s: Comparing lib %s with dirpath entry %s", + __func__, base_libname, dirp->d_name); compres = strncmp(dirp->d_name, base_libname, base_libname_length); if (compres == 0) { /* avoid "*.dll.a", ".so.1.2.3" etc. */ @@ -3247,7 +3257,8 @@ static char * get_libname_in_dir(const char* base_libname, size_t base_libname_l for (p = current_test_path; *p != '\0' && (p - current_test_path) < LARGEBUF; p++) { if (*p == '/') *p = '\\'; } - upsdebugx(3, "%s: WIN32: re-checking with %s", __func__, current_test_path); + upsdebugx(4, "%s: WIN32: re-checking with %s", + __func__, current_test_path); if (stat(current_test_path, &st) == 0) { if (st.st_size > 0) { libname_path = xstrdup(current_test_path); @@ -3260,7 +3271,8 @@ static char * get_libname_in_dir(const char* base_libname, size_t base_libname_l * original dir, and no threading at this moment, to be safe!) * https://stackoverflow.com/a/66096983/4715872 */ - upsdebugx(3, "%s: WIN32: re-checking with %s", __func__, current_test_path + 2); + upsdebugx(4, "%s: WIN32: re-checking with %s", + __func__, current_test_path + 2); if (stat(current_test_path + 2, &st) == 0) { if (st.st_size > 0) { libname_path = xstrdup(current_test_path + 2); @@ -3270,9 +3282,9 @@ static char * get_libname_in_dir(const char* base_libname, size_t base_libname_l # endif /* WIN32 */ #endif /* HAVE_DECL_REALPATH */ - upsdebugx(2,"Candidate path for lib %s is %s (realpath %s)", + upsdebugx(2, "Candidate path for lib %s is %s (realpath %s)", base_libname, current_test_path, - (libname_path!=NULL)?libname_path:"NULL"); + NUT_STRARG(libname_path)); if (libname_path != NULL) break; } @@ -3304,11 +3316,18 @@ static char * get_libname_in_pathset(const char* base_libname, size_t base_libna char *onedir = NULL; char* pathset_tmp; + upsdebugx(3, "%s('%s', %" PRIuSIZE ", '%s', %i): Entering method...", + __func__, base_libname, base_libname_length, + NUT_STRARG(pathset), + counter ? *counter : -1); + if (!pathset || *pathset == '\0') return NULL; /* First call to tokenization passes the string, others pass NULL */ pathset_tmp = xstrdup(pathset); + upsdebugx(4, "%s: Looking for lib %s in a colon-separated path set", + __func__, base_libname); while (NULL != (onedir = strtok( (onedir ? NULL : pathset_tmp), ":" ))) { libname_path = get_libname_in_dir(base_libname, base_libname_length, onedir, (*counter)++); if (libname_path != NULL) @@ -3321,6 +3340,8 @@ static char * get_libname_in_pathset(const char* base_libname, size_t base_libna pathset_tmp = xstrdup(pathset); if (!libname_path) { onedir = NULL; /* probably is NULL already, but better ensure this */ + upsdebugx(4, "%s: WIN32: Looking for lib %s in a semicolon-separated path set", + __func__, base_libname); while (NULL != (onedir = strtok( (onedir ? NULL : pathset_tmp), ";" ))) { libname_path = get_libname_in_dir(base_libname, base_libname_length, onedir, (*counter)++); if (libname_path != NULL) @@ -3342,16 +3363,21 @@ char * get_libname(const char* base_libname) size_t base_libname_length = strlen(base_libname); struct stat st; + upsdebugx(3, "%s('%s'): Entering method...", __func__, base_libname); + /* First, check for an exact hit by absolute/relative path * if `base_libname` includes path separator character(s) */ if (xbasename(base_libname) != base_libname) { + upsdebugx(4, "%s: Looking for lib %s by exact hit...", + __func__, base_libname); #if HAVE_DECL_REALPATH /* allocates the buffer we free() later */ libname_path = realpath(base_libname, NULL); if (libname_path != NULL) { if (stat(libname_path, &st) == 0) { if (st.st_size > 0) { - upsdebugx(2, "Looking for lib %s, found by exact hit", base_libname); + upsdebugx(2, "Looking for lib %s, found by exact hit", + base_libname); goto found; } } @@ -3366,7 +3392,8 @@ char * get_libname(const char* base_libname) if (stat(base_libname, &st) == 0) { if (st.st_size > 0) { libname_path = xstrdup(base_libname); - upsdebugx(2, "Looking for lib %s, found by exact hit", base_libname); + upsdebugx(2, "Looking for lib %s, found by exact hit", + base_libname); goto found; } } @@ -3375,25 +3402,36 @@ char * get_libname(const char* base_libname) /* Normally these envvars should not be set, but if the user insists, * we should prefer the override... */ #ifdef BUILD_64 + upsdebugx(4, "%s: Looking for lib %s by path-set LD_LIBRARY_PATH_64...", + __func__, base_libname); libname_path = get_libname_in_pathset(base_libname, base_libname_length, getenv("LD_LIBRARY_PATH_64"), &counter); if (libname_path != NULL) { - upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH_64", base_libname); + upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH_64", + base_libname); goto found; } #else + upsdebugx(4, "%s: Looking for lib %s by path-set LD_LIBRARY_PATH_32...", + __func__, base_libname); libname_path = get_libname_in_pathset(base_libname, base_libname_length, getenv("LD_LIBRARY_PATH_32"), &counter); if (libname_path != NULL) { - upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH_32", base_libname); + upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH_32", + base_libname); goto found; } #endif + upsdebugx(4, "%s: Looking for lib %s by path-set LD_LIBRARY_PATH...", + __func__, base_libname); libname_path = get_libname_in_pathset(base_libname, base_libname_length, getenv("LD_LIBRARY_PATH"), &counter); if (libname_path != NULL) { - upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH", base_libname); + upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH", + base_libname); goto found; } + upsdebugx(4, "%s: Looking for lib %s by search_paths[]...", + __func__, base_libname); for (index = 0 ; (search_paths[index] != NULL) && (libname_path == NULL) ; index++) { libname_path = get_libname_in_dir(base_libname, base_libname_length, search_paths[index], counter++); @@ -3408,17 +3446,23 @@ char * get_libname(const char* base_libname) if (!libname_path) { /* First check near the EXE (if executing it from another * working directory) */ + upsdebugx(4, "%s: WIN32: Looking for lib %s near EXE...", + __func__, base_libname); libname_path = get_libname_in_dir(base_libname, base_libname_length, getfullpath(NULL), counter++); } # ifdef PATH_LIB if (!libname_path) { + upsdebugx(4, "%s: WIN32: Looking for lib %s via PATH_LIB...", + __func__, base_libname); libname_path = get_libname_in_dir(base_libname, base_libname_length, getfullpath(PATH_LIB), counter++); } # endif if (!libname_path) { /* Resolve "lib" dir near the one with current executable ("bin" or "sbin") */ + upsdebugx(4, "%s: WIN32: Looking for lib %s in a 'lib' dir near EXE...", + __func__, base_libname); libname_path = get_libname_in_dir(base_libname, base_libname_length, getfullpath("/../lib"), counter++); } #endif /* WIN32 so far */ @@ -3427,13 +3471,15 @@ char * get_libname(const char* base_libname) /* Windows-specific: DLLs can be provided by common "PATH" envvar, * at lowest search priority though (after EXE dir, system, etc.) */ if (!libname_path) { - upsdebugx(2, "Looking for lib %s in PATH", base_libname); + upsdebugx(4, "%s: WIN32: Looking for lib %s in PATH", + __func__, base_libname); libname_path = get_libname_in_pathset(base_libname, base_libname_length, getenv("PATH"), &counter); } #endif /* WIN32 */ found: - upsdebugx(1,"Looking for lib %s, found %s", base_libname, (libname_path!=NULL)?libname_path:"NULL"); + upsdebugx(1, "Looking for lib %s, found %s", + base_libname, NUT_STRARG(libname_path)); return libname_path; } From eb84debcdf4884431f0bbaaed19b071bb79a3f9d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 20:46:36 +0200 Subject: [PATCH 07/10] tools/nut-scanner/*: move WIN32 WSAStartup() rituals to nutscan_init() so they only run once and for all [#2516] Signed-off-by: Jim Klimov --- tools/nut-scanner/nutscan-init.c | 11 ++++++++++- tools/nut-scanner/nutscan-ip.c | 8 -------- tools/nut-scanner/scan_nut.c | 5 ----- tools/nut-scanner/scan_snmp.c | 6 ------ tools/nut-scanner/scan_xml_http.c | 8 -------- 5 files changed, 10 insertions(+), 28 deletions(-) diff --git a/tools/nut-scanner/nutscan-init.c b/tools/nut-scanner/nutscan-init.c index 6b891a61de..940e04186d 100644 --- a/tools/nut-scanner/nutscan-init.c +++ b/tools/nut-scanner/nutscan-init.c @@ -37,6 +37,9 @@ #include "nut_platform.h" #ifdef WIN32 +# if defined HAVE_WINSOCK2_H && HAVE_WINSOCK2_H +# include +# endif # define SOEXT ".dll" #else # ifdef NUT_PLATFORM_APPLE_OSX @@ -120,6 +123,13 @@ void nutscan_init(void) { char *libname = NULL; +#ifdef WIN32 + /* Required ritual before calling any socket functions */ + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + /* Optional filter to not walk things twice */ nut_prepare_search_paths(); @@ -567,7 +577,6 @@ void nutscan_init(void) /* start of "NUT Simulation" - unconditional */ /* no need for additional library */ nutscan_avail_nut_simulation = 1; - } void nutscan_free(void) diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 0d73b7f039..0df42ba33b 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -504,9 +504,6 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) *start_ip = NULL; *stop_ip = NULL; -#ifdef WIN32 - WSADATA WSAdata; -#endif cidr_tok = strdup(cidr); first_ip = strdup(strtok_r(cidr_tok, "/", &saveptr)); @@ -550,11 +547,6 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) ip.type = IPv4; -#ifdef WIN32 - WSAStartup(2,&WSAdata); - atexit((void(*)(void))WSACleanup); -#endif - if ((ret = getaddrinfo(first_ip, NULL, &hints, &res)) != 0) { /* EAI_ADDRFAMILY? */ upsdebugx(5, "%s: getaddrinfo() failed for AF_INET (IPv4, will retry with IPv6): %d: %s", diff --git a/tools/nut-scanner/scan_nut.c b/tools/nut-scanner/scan_nut.c index 5ac4dc1995..4efc162ccf 100644 --- a/tools/nut-scanner/scan_nut.c +++ b/tools/nut-scanner/scan_nut.c @@ -265,11 +265,6 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons int change_action_handler = 0; #endif struct scan_nut_arg *nut_arg; -#ifdef WIN32 - WSADATA WSAdata; - WSAStartup(2,&WSAdata); - atexit((void(*)(void))WSACleanup); -#endif #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE diff --git a/tools/nut-scanner/scan_snmp.c b/tools/nut-scanner/scan_snmp.c index 210d138d76..4c1673639c 100644 --- a/tools/nut-scanner/scan_snmp.c +++ b/tools/nut-scanner/scan_snmp.c @@ -1054,12 +1054,6 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, sem_t * semaphore_scantype = &semaphore_scantype_inst; # endif /* HAVE_SEMAPHORE */ -# ifdef WIN32 - WSADATA WSAdata; - WSAStartup(2,&WSAdata); - atexit((void(*)(void))WSACleanup); -# endif - pthread_t thread; nutscan_thread_t * thread_array = NULL; size_t thread_count = 0, i; diff --git a/tools/nut-scanner/scan_xml_http.c b/tools/nut-scanner/scan_xml_http.c index 5032bd98f3..3212c3b590 100644 --- a/tools/nut-scanner/scan_xml_http.c +++ b/tools/nut-scanner/scan_xml_http.c @@ -200,17 +200,9 @@ static void * nutscan_scan_xml_http_generic(void * arg) ssize_t recv_size; int i; nutscan_device_t * nut_dev = NULL; -#ifdef WIN32 - WSADATA WSAdata; -#endif memset(&sockAddress_udp, 0, sizeof(sockAddress_udp)); -#ifdef WIN32 - WSAStartup(2,&WSAdata); - atexit((void(*)(void))WSACleanup); -#endif - if (sec != NULL) { /* if (sec->port_http > 0 && sec->port_http <= 65534) * port_http = sec->port_http; */ From 14bb3c9234fc3c4cff67b4d48d354b7a3d35a68d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 21:42:35 +0200 Subject: [PATCH 08/10] NEWS.adoc: update `nut-scanner -m auto` note - WIN32 is now supported too [#2516] Signed-off-by: Jim Klimov --- NEWS.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.adoc b/NEWS.adoc index 874373ca8a..93f2909509 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -143,7 +143,7 @@ installed. * newly added support to scan several IP addresses (single or ranges) with the same call, by repeating command-line options; also `-m auto{,4,6}` can be specified (once) to select IP (all, IPv4, IPv6) address ranges of - configured local network interfaces (currently not implemented for WIN32). + configured local network interfaces. An `/ADDRLEN` suffix can be added to the option, to filter out discovered subnets with too many bits available for the host address part (avoiding millions of scans in the extreme cases). From a4734f00e8bb366224be170e0e2d7f841ca70e2e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 20:50:25 +0200 Subject: [PATCH 09/10] tools/nut-scanner/nut-scanner.c: implement "-m auto" for two WIN32 APIs [#2516] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 480 +++++++++++++++++++++++++++++--- 1 file changed, 443 insertions(+), 37 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index eb11d35f5b..f2d6aaacd2 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -49,16 +49,18 @@ # include # include #else -/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo - on Windows 2000 and older versions */ -/* // TODO: complete "-m auto" support +# if defined HAVE_WINSOCK2_H && HAVE_WINSOCK2_H +# include +# endif +# if defined HAVE_IPHLPAPI_H && HAVE_IPHLPAPI_H +# include +# endif # include # include # ifndef AI_NUMERICSERV # define AI_NUMERICSERV NI_NUMERICSERV # endif # include "wincompat.h" -*/ #endif #ifdef HAVE_PTHREAD @@ -291,12 +293,55 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) char *s = NULL; int errno_saved; -#ifndef WIN32 - /* NOTE: Would need WIN32-specific implementation */ +#ifdef HAVE_GETIFADDRS + /* NOTE: this ifdef is more precise than ifdef WIN32; assuming + * its implementation of getifaddrs() would actually be functional + * if it appears in the OS or mingw/cygwin/... shims eventually. + * If it would be *not* functional, may have to revert to checking + * (also?) for WIN32 here and below. */ /* Inspired by https://stackoverflow.com/a/63789267/4715872 */ - struct ifaddrs *ifap; + struct ifaddrs *ifap = NULL, *ifa = NULL; +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + /* TODO: The two WIN32 approaches overlap quite a bit, deduplicate! + * First shot comes straight from examples as a starting point, but... */ + /* For Windows newer than Vista. Here and below, inspired by + * https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses + * https://stackoverflow.com/questions/122208/how-can-i-get-the-ip-address-of-a-local-computer + * https://stackoverflow.com/questions/41139561/find-ip-address-of-the-machine-in-c/41151132#41151132 + */ + + #define WIN32_GAA_WORKING_BUFFER_SIZE 15000 + #define WIN32_GAA_MAX_TRIES 3 + + DWORD dwRetVal = 0; + + /* Set the flags to pass to GetAdaptersAddresses */ + ULONG flags = GAA_FLAG_INCLUDE_PREFIX; + + /* default to unspecified address family (both IPv4 and IPv6) */ + ULONG family = AF_UNSPEC; + + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + ULONG outBufLen = 0; + ULONG Iterations = 0; + + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO + /* For Windows older than XP (present but not recommended + * in later releases). Here and below, inspired by + * https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersinfo + */ + + PIP_ADAPTER_INFO pAdapterInfo; + PIP_ADAPTER_INFO pAdapter = NULL; + DWORD dwRetVal = 0; + + ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); #endif + upsdebugx(3, "Entering %s('%s')", __func__, arg_addr); + /* Is this a `-m auto` mode? */ if (!strncmp(arg_addr, "auto", 4)) { /* TODO: Maybe split later, to allow separate @@ -386,13 +431,144 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) } /* Handle `-m auto*` modes below */ -#ifndef WIN32 - if (getifaddrs(&ifap) < 0) { +#ifdef HAVE_GETIFADDRS + upsdebugx(4, "%s: using getifaddrs()", __func__); + + if (getifaddrs(&ifap) < 0 || !ifap) { + if (ifap) + freeifaddrs(ifap); fatalx(EXIT_FAILURE, "Failed to getifaddrs() for connected subnet scan: %s\n", strerror(errno)); - } else { - struct ifaddrs *ifa; + /* TOTHINK: Non-fatal, just return / goto finish? + * Either way, do not proceed with code below! */ + } +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + upsdebugx(4, "%s: using GetAdaptersAddresses()", __func__); + + switch (auto_nets) { + case 4: + family = AF_INET; + break; + + case 6: + family = AF_INET6; + break; + + case 46: + default: + family = AF_UNSPEC; + break; + } + + /* Allocate a 15 KB buffer to start with; we will be told + * if more is needed (hence the loop below) */ + outBufLen = WIN32_GAA_WORKING_BUFFER_SIZE; + + do { + pAddresses = (IP_ADAPTER_ADDRESSES *) xcalloc(1, outBufLen); + + dwRetVal = + GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); + + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + free(pAddresses); + pAddresses = NULL; + } else { + break; + } + + Iterations++; + } while ( + (dwRetVal == ERROR_BUFFER_OVERFLOW) + && (Iterations < WIN32_GAA_MAX_TRIES) + ); + + if (dwRetVal != NO_ERROR) { + char msgPrefix[LARGEBUF]; + + snprintf(msgPrefix, sizeof(msgPrefix), + "Failed to GetAdaptersAddresses() for " + "connected subnet scan (%" PRIiMAX ")", + (intmax_t)dwRetVal); + + if (pAddresses) { + free(pAddresses); + pAddresses = NULL; + } + + if (dwRetVal == ERROR_NO_DATA) { + fatalx(EXIT_FAILURE, "%s: %s", + msgPrefix, + "No addresses were found for the requested parameters"); + } else { + LPVOID lpMsgBuf = NULL; + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + // Default language + (LPTSTR) & lpMsgBuf, 0, NULL) && lpMsgBuf + ) { + fatalx(EXIT_FAILURE, "%s: %s", + msgPrefix, + (LPTSTR)lpMsgBuf); + } + + if (lpMsgBuf) + LocalFree(lpMsgBuf); + fatalx(EXIT_FAILURE, "%s", msgPrefix); + } + + /* TOTHINK: Non-fatal, just return / goto finish? + * Either way, do not proceed with code below! */ + } +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO + upsdebugx(4, "%s: using GetAdaptersInfo()", __func__); + + /* NOTE: IPv4 only! */ + if (auto_nets == 6) { + fatalx(EXIT_FAILURE, + "Requested explicitly to query only IPv6 addresses, " + "but current system libraries support only IPv4." + ); + + /* TOTHINK: Non-fatal, just return / goto finish? + * Either way, do not proceed with code below! */ + } + + pAdapterInfo = (IP_ADAPTER_INFO *) xcalloc(1, sizeof (IP_ADAPTER_INFO)); + + /* Make an initial call to GetAdaptersInfo to get + * the necessary size into the ulOutBufLen variable */ + if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { + free(pAdapterInfo); + pAdapterInfo = (IP_ADAPTER_INFO *) xcalloc(1, ulOutBufLen); + } + + if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) != NO_ERROR) { + if (pAdapterInfo) { + free(pAdapterInfo); + pAdapterInfo = NULL; + } + + fatalx(EXIT_FAILURE, + "Failed to GetAdaptersAddresses() for " + "connected subnet scan (%" PRIiMAX ")", + (intmax_t)dwRetVal); + + /* TOTHINK: Non-fatal, just return / goto finish? + * Either way, do not proceed with code below! */ + } +#else + fatalx(EXIT_FAILURE, + "Have no way to query local interface addresses on this " + "platform, please run without the '-m auto*' options!"); +#endif + + /* Initial query did not fail, start a new scope + * for more variables, to be allocated just now */ + { char msg[LARGEBUF]; char cidr[LARGEBUF]; /* Note: INET6_ADDRSTRLEN is large enough for IPv4 too, @@ -402,19 +578,97 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) char mask[INET6_ADDRSTRLEN]; int masklen_subnet = 0; int masklen_hosts = 0; + uintmax_t ifflags = 0, iftype = 0; +#ifdef HAVE_GETIFADDRS + /* Every IP address (even if aliases on same interfaces) + * has its own "ifa" value. + */ for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr) { + const char *ifname = ifa->ifa_name; + + ifflags = (uintmax_t)ifa->ifa_flags; + iftype = ifflags; + + if (ifa->ifa_addr) +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + /* Nested structure can hold many addresses of different + * families assigned to the same physical interface. + * https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh + * https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_unicast_address_lh + */ + for (pCurrAddresses = pAddresses; pCurrAddresses; pCurrAddresses = pCurrAddresses->Next) { + char ifname[LARGEBUF]; + size_t ifnamelen = 0; + + ifflags = (uintmax_t)pCurrAddresses->Flags; + iftype = (uintmax_t)pCurrAddresses->IfType; + + /* Convert some fields from wide chars */ + ifnamelen += snprintf( + ifname, + sizeof(ifname), + "%s (", + pCurrAddresses->AdapterName); + ifnamelen += wcstombs( + ifname + ifnamelen, + pCurrAddresses->FriendlyName, + sizeof(ifname) - ifnamelen); + ifnamelen += snprintf( + ifname + ifnamelen, + sizeof(ifname) - ifnamelen, + "): "); + ifnamelen += wcstombs( + ifname + ifnamelen, + pCurrAddresses->Description, + sizeof(ifname) - ifnamelen); + + if ( (ifflags & IP_ADAPTER_IPV6_ENABLED) + || (ifflags & IP_ADAPTER_IPV4_ENABLED) + ) for (pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast; pUnicast = pUnicast->Next) +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO + /* NOTE: IPv4 only! + * https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_info + * https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_addr_string + * https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_address_string + */ + for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { + IP_ADDR_STRING *pUnicast; + char ifname[LARGEBUF]; + size_t ifnamelen = 0; + + iftype = (uintmax_t)pAdapter->Type; + ifflags = iftype; /* The nearest they have to flags */ + ifnamelen += snprintf( + ifname, + sizeof(ifname), + "%s: %s", + pAdapter->AdapterName, + pAdapter->Description); + + for (pUnicast = &(pAdapter->IpAddressList); pUnicast; pUnicast = pUnicast->Next) +#endif + { /* Have some address to handle */ memset(msg, 0, sizeof(msg)); memset(addr, 0, sizeof(addr)); memset(mask, 0, sizeof(mask)); masklen_subnet = -1; - if (ifa->ifa_addr->sa_family == AF_INET6) { +#if defined HAVE_GETIFADDRS || (defined WIN32 && defined HAVE_GETADAPTERSADDRESSES) +# ifdef HAVE_GETIFADDRS + if (ifa->ifa_addr->sa_family == AF_INET6) +# elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6) +# elif defined WIN32 && defined HAVE_GETADAPTERSINFO + if (0) /* No IPv6 for this library call */ +# endif + { /* IPv6 */ +# ifdef HAVE_GETIFADDRS uint8_t i, j; /* Ensure proper alignment */ - struct sockaddr_in6 sm; + struct sockaddr_in6 sa, sm; + memcpy (&sa, ifa->ifa_addr, sizeof(struct sockaddr_in6)); memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in6)); masklen_subnet = 0; @@ -425,22 +679,55 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) i >>= 1; } } +# elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + /* This structure member is only available on Windows Vista and later. + * If we need earlier versions built for, need to use common struct. + * https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_unicast_address_lh + */ + masklen_subnet = pUnicast->OnLinkPrefixLength; +# elif defined WIN32 && defined HAVE_GETADAPTERSINFO + masklen_subnet = 128; /* no-op anyway */ +# endif masklen_hosts = 128 - masklen_subnet; - getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); - getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in6), mask, sizeof(mask), NULL, 0, NI_NUMERICHOST); +# ifdef HAVE_GETIFADDRS + getnameinfo((struct sockaddr *)&sa, sizeof(struct sockaddr_in6), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); + getnameinfo((struct sockaddr *)&sm, sizeof(struct sockaddr_in6), mask, sizeof(mask), NULL, 0, NI_NUMERICHOST); +# elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + getnameinfo(pUnicast->Address.lpSockaddr, sizeof(struct sockaddr_in6), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); + /* We have no real need for mask as an IP + * string except debug logs, so let it be */ + snprintf(mask, sizeof(mask), "/%i", masklen_subnet); +# elif defined WIN32 && defined HAVE_GETADAPTERSINFO +# endif + snprintf(msg, sizeof(msg), - "Interface: %s\tAddress: %s\tMask: %s (subnet: %i, hosts: %i)\tFlags: %08" PRIxMAX, - ifa->ifa_name, addr, mask, + "Interface: %s\tAddress: %s\tMask: %s (subnet: %i, hosts: %i)\tFlags: %08" PRIxMAX "\tType: %08" PRIxMAX, + ifname, addr, mask, masklen_subnet, masklen_hosts, - (uintmax_t)ifa->ifa_flags); - } else if (ifa->ifa_addr->sa_family == AF_INET) { + ifflags, iftype); + } /* IPv6 */ +#endif /* HAVE_GETIFADDRS || HAVE_GETADAPTERSADDRESSES */ + +#ifdef HAVE_GETIFADDRS + else + if (ifa->ifa_addr->sa_family == AF_INET) +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + else + if (pUnicast->Address.lpSockaddr->sa_family == AF_INET) +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO + /* NOTE: No "else", no "if": IPv6 was skipped + * on this platform and IPv4 was the only option */ +#endif + { /* IPv4 */ +#ifdef HAVE_GETIFADDRS in_addr_t i; /* Ensure proper alignment */ struct sockaddr_in sa, sm; memcpy (&sa, ifa->ifa_addr, sizeof(struct sockaddr_in)); memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in)); + snprintf(addr, sizeof(addr), "%s", inet_ntoa(sa.sin_addr)); snprintf(mask, sizeof(mask), "%s", inet_ntoa(sm.sin_addr)); @@ -450,31 +737,101 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) masklen_subnet += i & 1; i >>= 1; } +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + masklen_subnet = pUnicast->OnLinkPrefixLength; + + getnameinfo(pUnicast->Address.lpSockaddr, sizeof(struct sockaddr_in), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); + /* We have no real need for mask as an IP + * string except debug logs, so let it be */ + snprintf(mask, sizeof(mask), "/%i", masklen_subnet); +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO + struct sockaddr_in sm; + + snprintf(addr, sizeof(addr) > 16 ? 16 : sizeof(addr), "%s", pUnicast->IpAddress.String); + snprintf(mask, sizeof(mask) > 16 ? 16 : sizeof(mask), "%s", pUnicast->IpMask.String); + + masklen_subnet = 0; + if (inet_pton(AF_INET, mask, &sm.sin_addr)) { + uint32_t i = sm.sin_addr.s_addr; + while (i) { + masklen_subnet += i & 1; + i >>= 1; + } + } +#endif masklen_hosts = 32 - masklen_subnet; snprintf(msg, sizeof(msg), - "Interface: %s\tAddress: %s\tMask: %s (subnet: %i, hosts: %i)\tFlags: %08" PRIxMAX, - ifa->ifa_name, addr, mask, + "Interface: %s\tAddress: %s\tMask: %s (subnet: %i, hosts: %i)\tFlags: %08" PRIxMAX "\tType: %08" PRIxMAX, + ifname, addr, mask, masklen_subnet, masklen_hosts, - (uintmax_t)ifa->ifa_flags); + ifflags, iftype); + } /* IPv4 */ + +#ifdef HAVE_GETIFADDRS /* - } else { + else { snprintf(msg, sizeof(msg), "Addr family: %" PRIuMAX, (intmax_t)ifa->ifa_addr->sa_family); -*/ } +*/ +#endif +#ifdef HAVE_GETIFADDRS if (ifa->ifa_addr->sa_family == AF_INET6 || ifa->ifa_addr->sa_family == AF_INET) { - if (ifa->ifa_flags & IFF_LOOPBACK) + if (iftype & IFF_LOOPBACK) snprintfcat(msg, sizeof(msg), " IFF_LOOPBACK"); - if (ifa->ifa_flags & IFF_UP) + if (iftype & IFF_UP) snprintfcat(msg, sizeof(msg), " IFF_UP"); - if (ifa->ifa_flags & IFF_RUNNING) + if (iftype & IFF_RUNNING) snprintfcat(msg, sizeof(msg), " IFF_RUNNING"); - if (ifa->ifa_flags & IFF_BROADCAST) + if (iftype & IFF_BROADCAST) snprintfcat(msg, sizeof(msg), " IFF_BROADCAST(is assigned)"); +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + /* https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin + * https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_suffix_origin + */ + if (pUnicast->Address.lpSockaddr->sa_family == AF_INET + || pUnicast->Address.lpSockaddr->sa_family == AF_INET6 + ) { + if (iftype == IF_TYPE_OTHER) + snprintfcat(msg, sizeof(msg), " IF_TYPE_OTHER"); + if (iftype == IF_TYPE_ETHERNET_CSMACD) + snprintfcat(msg, sizeof(msg), " IF_TYPE_ETHERNET_CSMACD"); + if (iftype == IF_TYPE_ISO88025_TOKENRING) + snprintfcat(msg, sizeof(msg), " IF_TYPE_ISO88025_TOKENRING"); + if (iftype == IF_TYPE_PPP) + snprintfcat(msg, sizeof(msg), " IF_TYPE_PPP"); + if (iftype == IF_TYPE_SOFTWARE_LOOPBACK) + snprintfcat(msg, sizeof(msg), " IF_TYPE_SOFTWARE_LOOPBACK"); + if (iftype == IF_TYPE_ATM) + snprintfcat(msg, sizeof(msg), " IF_TYPE_ATM"); + if (iftype == IF_TYPE_IEEE80211) + snprintfcat(msg, sizeof(msg), " IF_TYPE_IEEE80211"); + if (iftype == IF_TYPE_TUNNEL) + snprintfcat(msg, sizeof(msg), " IF_TYPE_TUNNEL"); + if (iftype == IF_TYPE_IEEE1394) + snprintfcat(msg, sizeof(msg), " IF_TYPE_IEEE1394"); +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO + { /* Just scoping for this platform */ + if (iftype == MIB_IF_TYPE_OTHER) + snprintfcat(msg, sizeof(msg), " MIB_IF_TYPE_OTHER"); + if (iftype == MIB_IF_TYPE_ETHERNET) + snprintfcat(msg, sizeof(msg), " MIB_IF_TYPE_ETHERNET"); + if (iftype == IF_TYPE_ISO88025_TOKENRING) + snprintfcat(msg, sizeof(msg), " IF_TYPE_ISO88025_TOKENRING"); + if (iftype == MIB_IF_TYPE_PPP) + snprintfcat(msg, sizeof(msg), " MIB_IF_TYPE_PPP"); + if (iftype == MIB_IF_TYPE_LOOPBACK) + snprintfcat(msg, sizeof(msg), " MIB_IF_TYPE_LOOPBACK"); + if (iftype == MIB_IF_TYPE_SLIP) + snprintfcat(msg, sizeof(msg), " MIB_IF_TYPE_SLIP"); + if (iftype == IF_TYPE_IEEE80211) + snprintfcat(msg, sizeof(msg), " IF_TYPE_IEEE80211"); +#endif upsdebugx(5, "Discovering getifaddrs(): %s", msg); +#ifdef HAVE_GETIFADDRS if (!( (auto_nets == 46 || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) @@ -483,6 +840,28 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) upsdebugx(6, "Subnet ignored: not of the requested address family"); continue; } +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + if (!( + (auto_nets == 46 + || (auto_nets == 4 && pUnicast->Address.lpSockaddr->sa_family == AF_INET) + || (auto_nets == 6 && pUnicast->Address.lpSockaddr->sa_family == AF_INET6) ) + )) { + upsdebugx(6, "Subnet ignored: not of the requested address family"); + continue; + } +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO +#endif + + if ( + !strcmp(addr, "0.0.0.0") + || !strcmp(addr, "*") + || !strcmp(addr, "::") + || !strcmp(addr, "0::") + ) { + /* FIXME: IPv6 spellings? Search for hex/digits other than 0? */ + upsdebugx(6, "Subnet ignored: host address not assigned or mis-detected"); + continue; + } if (masklen_hosts_limit < masklen_hosts) { /* NOTE: masklen_hosts==0 means @@ -494,19 +873,35 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) continue; } - if (ifa->ifa_flags & IFF_LOOPBACK) { +#ifdef HAVE_GETIFADDRS + if (iftype & IFF_LOOPBACK) +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + if (iftype == IF_TYPE_SOFTWARE_LOOPBACK) +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO + if (iftype == MIB_IF_TYPE_LOOPBACK) +#endif + { upsdebugx(6, "Subnet ignored: loopback"); continue; } + /* TODO? Filter other interface types? */ +#ifdef HAVE_GETIFADDRS if (!( - (ifa->ifa_flags & IFF_UP) - && (ifa->ifa_flags & IFF_RUNNING) - && (ifa->ifa_flags & IFF_BROADCAST) + (ifflags & IFF_UP) + && (ifflags & IFF_RUNNING) + && (ifflags & IFF_BROADCAST) )) { upsdebugx(6, "Subnet ignored: not up and running, with a proper broadcast-able address"); continue; } +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + if (pCurrAddresses->OperStatus != IfOperStatusUp) { + upsdebugx(6, "Subnet ignored: not up and running"); + continue; + } +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO +#endif /* TODO: also rule out "link-local" address ranges * so we do not issue billions of worthless scans. @@ -527,13 +922,24 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) } /* else AF_UNIX or a dozen other types we do not care about here */ } } + } + +/*finish:*/ +#ifdef HAVE_GETIFADDRS + if (ifap) { freeifaddrs(ifap); } -#else /* WIN32 */ - /* https://stackoverflow.com/questions/122208/how-can-i-get-the-ip-address-of-a-local-computer */ - /* https://github.com/networkupstools/nut/issues/2516 */ - upsdebugx(0, "Local address detection feature is not completed on Windows, please call back later"); +#elif defined WIN32 && defined HAVE_GETADAPTERSADDRESSES + if (pAddresses) { + free(pAddresses); + } +#elif defined WIN32 && defined HAVE_GETADAPTERSINFO + if (pAdapterInfo) { + free(pAdapterInfo); + } #endif + + upsdebugx(3, "Finished %s('%s')", __func__, arg_addr); } static void show_usage(void) From fd4106aca3fd7373f45265b2bb65e37b6b44a68a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 22:03:14 +0200 Subject: [PATCH 10/10] tools/nut-scanner/nut-scanner.c: add a warning comment about bit-counting for `-m auto` [#2244, #2516] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index f2d6aaacd2..9628171ea8 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -671,6 +671,13 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) memcpy (&sa, ifa->ifa_addr, sizeof(struct sockaddr_in6)); memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in6)); + /* FIXME: Here and below, this code + * technically just counts set bits + * and we assume they are a single + * contiguous range in the address + * portion of the IP address for a + * netmask. + */ masklen_subnet = 0; for (j = 0; j < 16; j++) { i = sm.sin6_addr.s6_addr[j];