From d20f9ab3a9cabffe16ad603f2724d832b4052e2a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 10:48:44 +0200 Subject: [PATCH 01/27] tools/nut-scanner/nut-scanner.c: refactor handle_arg_cidr() into a separate method [#2244] Allow for some shorter indentation Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 292 +++++++++++++++++--------------- 1 file changed, 152 insertions(+), 140 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 0c3071d146..515bd7486a 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -412,6 +412,156 @@ static void * run_eaton_serial(void *arg) return NULL; } +static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) +{ + char *start_ip = NULL, *end_ip = NULL; + int auto_nets = -1; + + if (!strcmp(optarg, "auto") || !strcmp(optarg, "auto4") || !strcmp(optarg, "auto6")) { + if (auto_nets_ptr && *auto_nets_ptr) { + fprintf(stderr, "Duplicate request for connected subnet scan ignored\n"); + } else { +#ifndef WIN32 + /* Inspired by https://stackoverflow.com/a/63789267/4715872 */ + struct ifaddrs *ifap; +#endif + + if (!strcmp(optarg, "auto")) { + auto_nets = 46; + } else if (!strcmp(optarg, "auto4")) { + auto_nets = 4; + } else if (!strcmp(optarg, "auto6")) { + auto_nets = 6; + } + if (auto_nets_ptr) { + *auto_nets_ptr = auto_nets; + } + +#ifndef WIN32 + if (getifaddrs(&ifap) < 0) { + fatalx(EXIT_FAILURE, + "Failed to getifaddrs() for connected subnet scan: %s\n", + strerror(errno)); + } else { + struct ifaddrs *ifa; + char msg[LARGEBUF]; + /* Note: INET6_ADDRSTRLEN is large enough for IPv4 too, + * and is smaller than LARGEBUF to avoid snprintf() + * warnings that the result might not fit. */ + char addr[INET6_ADDRSTRLEN]; + char mask[INET6_ADDRSTRLEN]; + int masklen = 0; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr) { + memset(msg, 0, sizeof(msg)); + memset(addr, 0, sizeof(addr)); + memset(mask, 0, sizeof(mask)); + masklen = -1; + + if (ifa->ifa_addr->sa_family == AF_INET6) { + uint8_t i, j; + + /* Ensure proper alignment */ + struct sockaddr_in6 sm; + memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in6)); + + masklen = 0; + for (j = 0; j < 16; j++) { + i = sm.sin6_addr.s6_addr[j]; + while (i) { + masklen += i & 1; + i >>= 1; + } + } + + 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); + snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); + } else if (ifa->ifa_addr->sa_family == AF_INET) { + 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)); + + i = sm.sin_addr.s_addr; + masklen = 0; + while (i) { + masklen += i & 1; + i >>= 1; + } + snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); +/* + } else { + snprintf(msg, sizeof(msg), "Addr family: %" PRIuMAX, (intmax_t)ifa->ifa_addr->sa_family); +*/ + } + + if (ifa->ifa_addr->sa_family == AF_INET6 || ifa->ifa_addr->sa_family == AF_INET) { + if (ifa->ifa_flags & IFF_LOOPBACK) + snprintfcat(msg, sizeof(msg), " IFF_LOOPBACK"); + if (ifa->ifa_flags & IFF_UP) + snprintfcat(msg, sizeof(msg), " IFF_UP"); + if (ifa->ifa_flags & IFF_RUNNING) + snprintfcat(msg, sizeof(msg), " IFF_RUNNING"); + if (ifa->ifa_flags & IFF_BROADCAST) + snprintfcat(msg, sizeof(msg), " IFF_BROADCAST(is assigned)"); + + upsdebugx(5, "Discovering getifaddrs(): %s", msg); + + /* TODO: also rule out "link-local" address ranges + * so we do not issue billions of worthless scans. + * FIXME: IPv6 may also be a problem, see + * https://github.com/networkupstools/nut/issues/2512 + */ + if (!(ifa->ifa_flags & IFF_LOOPBACK) + && (ifa->ifa_flags & IFF_UP) + && (ifa->ifa_flags & IFF_RUNNING) + && (ifa->ifa_flags & IFF_BROADCAST) + && (auto_nets == 46 + || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) + || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) + ) { + char cidr[LARGEBUF]; + + if (snprintf(cidr, sizeof(cidr), "%s/%i", addr, masklen) < 0) { + fatalx(EXIT_FAILURE, "Could not construct a CIDR string from discovered address/mask"); + } + + upsdebugx(5, "Processing CIDR net/mask: %s", cidr); + nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); + upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); + + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } + } /* else AF_UNIX or a dozen other types we do not care about here */ + } + } + freeifaddrs(ifap); + } +#else /* WIN32 */ + /* https://stackoverflow.com/questions/122208/how-can-i-get-the-ip-address-of-a-local-computer */ + upsdebugx(0, "Local address detection feature is not completed on Windows, please call back later"); +#endif + } + } else { + /* not `-m auto` => is `-m cidr` */ + upsdebugx(5, "Processing CIDR net/mask: %s", optarg); + nutscan_cidr_to_ip(optarg, &start_ip, &end_ip); + upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); + + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } +} + static void show_usage(void) { /* NOTE: This code uses `nutscan_avail_*` global vars from nutscan-init.c */ @@ -753,147 +903,9 @@ int main(int argc, char *argv[]) end_ip = NULL; } - if (!strcmp(optarg, "auto") || !strcmp(optarg, "auto4") || !strcmp(optarg, "auto6")) { - if (auto_nets) { - fprintf(stderr, "Duplicate request for connected subnet scan ignored\n"); - } else { -#ifndef WIN32 - /* TODO: Refactor into a method, reduce indentation? */ - /* Inspired by https://stackoverflow.com/a/63789267/4715872 */ - struct ifaddrs *ifap; -#endif - - if (!strcmp(optarg, "auto")) { - auto_nets = 46; - } else if (!strcmp(optarg, "auto4")) { - auto_nets = 4; - } else if (!strcmp(optarg, "auto6")) { - auto_nets = 6; - } - -#ifndef WIN32 - if (getifaddrs(&ifap) < 0) { - fatalx(EXIT_FAILURE, - "Failed to getifaddrs() for connected subnet scan: %s\n", - strerror(errno)); - } else { - struct ifaddrs *ifa; - char msg[LARGEBUF]; - /* Note: INET6_ADDRSTRLEN is large enough for IPv4 too, - * and is smaller than LARGEBUF to avoid snprintf() - * warnings that the result might not fit. */ - char addr[INET6_ADDRSTRLEN]; - char mask[INET6_ADDRSTRLEN]; - int masklen = 0; - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr) { - memset(msg, 0, sizeof(msg)); - memset(addr, 0, sizeof(addr)); - memset(mask, 0, sizeof(mask)); - masklen = -1; - - if (ifa->ifa_addr->sa_family == AF_INET6) { - uint8_t i, j; - - /* Ensure proper alignment */ - struct sockaddr_in6 sm; - memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in6)); - - masklen = 0; - for (j = 0; j < 16; j++) { - i = sm.sin6_addr.s6_addr[j]; - while (i) { - masklen += i & 1; - i >>= 1; - } - } - - 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); - snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); - } else if (ifa->ifa_addr->sa_family == AF_INET) { - 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)); - - i = sm.sin_addr.s_addr; - masklen = 0; - while (i) { - masklen += i & 1; - i >>= 1; - } - snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); -/* - } else { - snprintf(msg, sizeof(msg), "Addr family: %" PRIuMAX, (intmax_t)ifa->ifa_addr->sa_family); -*/ - } - - if (ifa->ifa_addr->sa_family == AF_INET6 || ifa->ifa_addr->sa_family == AF_INET) { - if (ifa->ifa_flags & IFF_LOOPBACK) - snprintfcat(msg, sizeof(msg), " IFF_LOOPBACK"); - if (ifa->ifa_flags & IFF_UP) - snprintfcat(msg, sizeof(msg), " IFF_UP"); - if (ifa->ifa_flags & IFF_RUNNING) - snprintfcat(msg, sizeof(msg), " IFF_RUNNING"); - if (ifa->ifa_flags & IFF_BROADCAST) - snprintfcat(msg, sizeof(msg), " IFF_BROADCAST(is assigned)"); - - upsdebugx(5, "Discovering getifaddrs(): %s", msg); - - /* TODO: also rule out "link-local" address ranges - * so we do not issue billions of worthless scans. - * FIXME: IPv6 may also be a problem, see - * https://github.com/networkupstools/nut/issues/2512 - */ - if (!(ifa->ifa_flags & IFF_LOOPBACK) - && (ifa->ifa_flags & IFF_UP) - && (ifa->ifa_flags & IFF_RUNNING) - && (ifa->ifa_flags & IFF_BROADCAST) - && (auto_nets == 46 - || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) - || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) - ) { - char cidr[LARGEBUF]; - - if (snprintf(cidr, sizeof(cidr), "%s/%i", addr, masklen) < 0) { - fatalx(EXIT_FAILURE, "Could not construct a CIDR string from discovered address/mask"); - } - - upsdebugx(5, "Processing CIDR net/mask: %s", cidr); - nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); - upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); - - add_ip_range(start_ip, end_ip); - start_ip = NULL; - end_ip = NULL; - } - } /* else AF_UNIX or a dozen other types we do not care about here */ - } - } - freeifaddrs(ifap); - } -#else /* WIN32 */ - /* https://stackoverflow.com/questions/122208/how-can-i-get-the-ip-address-of-a-local-computer */ - upsdebugx(0, "Local address detection feature is not completed on Windows, please call back later"); -#endif - } - } else { - /* not `-m auto` => is `-m cidr` */ - upsdebugx(5, "Processing CIDR net/mask: %s", optarg); - nutscan_cidr_to_ip(optarg, &start_ip, &end_ip); - upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); + /* Large code block offloaded into a method */ + handle_arg_cidr(optarg, &auto_nets); - add_ip_range(start_ip, end_ip); - start_ip = NULL; - end_ip = NULL; - } break; case 'D': /* nothing to do, here */ From 54c2365798f8ff0aaf2d14e95139037ce8d85d99 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 11:09:10 +0200 Subject: [PATCH 02/27] tools/nut-scanner/nut-scanner.c: refactor handle_arg_cidr() logic to un-indent it some more [#2244] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 279 +++++++++++++++++--------------- 1 file changed, 150 insertions(+), 129 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 515bd7486a..2ecb67fb8e 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -415,143 +415,48 @@ static void * run_eaton_serial(void *arg) static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) { char *start_ip = NULL, *end_ip = NULL; + /* Scanning mode: IPv4, IPv6 or both */ int auto_nets = -1; - if (!strcmp(optarg, "auto") || !strcmp(optarg, "auto4") || !strcmp(optarg, "auto6")) { - if (auto_nets_ptr && *auto_nets_ptr) { - fprintf(stderr, "Duplicate request for connected subnet scan ignored\n"); - } else { #ifndef WIN32 - /* Inspired by https://stackoverflow.com/a/63789267/4715872 */ - struct ifaddrs *ifap; + /* NOTE: Would need WIN32-specific implementation */ + /* Inspired by https://stackoverflow.com/a/63789267/4715872 */ + struct ifaddrs *ifap; #endif - if (!strcmp(optarg, "auto")) { - auto_nets = 46; - } else if (!strcmp(optarg, "auto4")) { - auto_nets = 4; - } else if (!strcmp(optarg, "auto6")) { - auto_nets = 6; - } - if (auto_nets_ptr) { - *auto_nets_ptr = auto_nets; - } + /* Is this a `-m auto` mode? */ + if (!strncmp(optarg, "auto", 4)) { + if (auto_nets_ptr && *auto_nets_ptr) { + fprintf(stderr, "Duplicate request for connected subnet scan ignored\n"); + return; + } -#ifndef WIN32 - if (getifaddrs(&ifap) < 0) { - fatalx(EXIT_FAILURE, - "Failed to getifaddrs() for connected subnet scan: %s\n", - strerror(errno)); - } else { - struct ifaddrs *ifa; - char msg[LARGEBUF]; - /* Note: INET6_ADDRSTRLEN is large enough for IPv4 too, - * and is smaller than LARGEBUF to avoid snprintf() - * warnings that the result might not fit. */ - char addr[INET6_ADDRSTRLEN]; - char mask[INET6_ADDRSTRLEN]; - int masklen = 0; - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr) { - memset(msg, 0, sizeof(msg)); - memset(addr, 0, sizeof(addr)); - memset(mask, 0, sizeof(mask)); - masklen = -1; - - if (ifa->ifa_addr->sa_family == AF_INET6) { - uint8_t i, j; - - /* Ensure proper alignment */ - struct sockaddr_in6 sm; - memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in6)); - - masklen = 0; - for (j = 0; j < 16; j++) { - i = sm.sin6_addr.s6_addr[j]; - while (i) { - masklen += i & 1; - i >>= 1; - } - } - - 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); - snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); - } else if (ifa->ifa_addr->sa_family == AF_INET) { - 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)); - - i = sm.sin_addr.s_addr; - masklen = 0; - while (i) { - masklen += i & 1; - i >>= 1; - } - snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); -/* - } else { - snprintf(msg, sizeof(msg), "Addr family: %" PRIuMAX, (intmax_t)ifa->ifa_addr->sa_family); -*/ - } + /* Not very efficient to stack strcmp's, but + * also not a hot codepath to care much, either. + */ + if (!strcmp(optarg, "auto")) { + auto_nets = 46; + } else if (!strcmp(optarg, "auto4")) { + auto_nets = 4; + } else if (!strcmp(optarg, "auto6")) { + auto_nets = 6; + } else { + /* TODO: maybe fail right away? + * Or claim a simple auto46 mode? */ + upsdebugx(0, + "Got a '-m auto*' CLI option with unsupported " + "keyword pattern; assuming a CIDR, " + "likely to fail: %s", optarg); + } - if (ifa->ifa_addr->sa_family == AF_INET6 || ifa->ifa_addr->sa_family == AF_INET) { - if (ifa->ifa_flags & IFF_LOOPBACK) - snprintfcat(msg, sizeof(msg), " IFF_LOOPBACK"); - if (ifa->ifa_flags & IFF_UP) - snprintfcat(msg, sizeof(msg), " IFF_UP"); - if (ifa->ifa_flags & IFF_RUNNING) - snprintfcat(msg, sizeof(msg), " IFF_RUNNING"); - if (ifa->ifa_flags & IFF_BROADCAST) - snprintfcat(msg, sizeof(msg), " IFF_BROADCAST(is assigned)"); - - upsdebugx(5, "Discovering getifaddrs(): %s", msg); - - /* TODO: also rule out "link-local" address ranges - * so we do not issue billions of worthless scans. - * FIXME: IPv6 may also be a problem, see - * https://github.com/networkupstools/nut/issues/2512 - */ - if (!(ifa->ifa_flags & IFF_LOOPBACK) - && (ifa->ifa_flags & IFF_UP) - && (ifa->ifa_flags & IFF_RUNNING) - && (ifa->ifa_flags & IFF_BROADCAST) - && (auto_nets == 46 - || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) - || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) - ) { - char cidr[LARGEBUF]; - - if (snprintf(cidr, sizeof(cidr), "%s/%i", addr, masklen) < 0) { - fatalx(EXIT_FAILURE, "Could not construct a CIDR string from discovered address/mask"); - } - - upsdebugx(5, "Processing CIDR net/mask: %s", cidr); - nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); - upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); - - add_ip_range(start_ip, end_ip); - start_ip = NULL; - end_ip = NULL; - } - } /* else AF_UNIX or a dozen other types we do not care about here */ - } - } - freeifaddrs(ifap); - } -#else /* WIN32 */ - /* https://stackoverflow.com/questions/122208/how-can-i-get-the-ip-address-of-a-local-computer */ - upsdebugx(0, "Local address detection feature is not completed on Windows, please call back later"); -#endif + /* Let the caller know, to allow for run-once support */ + if (auto_nets_ptr) { + *auto_nets_ptr = auto_nets; } - } else { - /* not `-m auto` => is `-m cidr` */ + } + + if (auto_nets < 0) { + /* not a supported `-m auto*` pattern => is `-m cidr` */ upsdebugx(5, "Processing CIDR net/mask: %s", optarg); nutscan_cidr_to_ip(optarg, &start_ip, &end_ip); upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); @@ -559,7 +464,123 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) add_ip_range(start_ip, end_ip); start_ip = NULL; end_ip = NULL; + return; } + + /* Handle `-m auto*` modes below */ +#ifndef WIN32 + if (getifaddrs(&ifap) < 0) { + fatalx(EXIT_FAILURE, + "Failed to getifaddrs() for connected subnet scan: %s\n", + strerror(errno)); + } else { + struct ifaddrs *ifa; + char msg[LARGEBUF]; + /* Note: INET6_ADDRSTRLEN is large enough for IPv4 too, + * and is smaller than LARGEBUF to avoid snprintf() + * warnings that the result might not fit. */ + char addr[INET6_ADDRSTRLEN]; + char mask[INET6_ADDRSTRLEN]; + int masklen = 0; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr) { + memset(msg, 0, sizeof(msg)); + memset(addr, 0, sizeof(addr)); + memset(mask, 0, sizeof(mask)); + masklen = -1; + + if (ifa->ifa_addr->sa_family == AF_INET6) { + uint8_t i, j; + + /* Ensure proper alignment */ + struct sockaddr_in6 sm; + memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in6)); + + masklen = 0; + for (j = 0; j < 16; j++) { + i = sm.sin6_addr.s6_addr[j]; + while (i) { + masklen += i & 1; + i >>= 1; + } + } + + 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); + snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); + } else if (ifa->ifa_addr->sa_family == AF_INET) { + 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)); + + i = sm.sin_addr.s_addr; + masklen = 0; + while (i) { + masklen += i & 1; + i >>= 1; + } + snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); +/* + } else { + snprintf(msg, sizeof(msg), "Addr family: %" PRIuMAX, (intmax_t)ifa->ifa_addr->sa_family); +*/ + } + + if (ifa->ifa_addr->sa_family == AF_INET6 || ifa->ifa_addr->sa_family == AF_INET) { + if (ifa->ifa_flags & IFF_LOOPBACK) + snprintfcat(msg, sizeof(msg), " IFF_LOOPBACK"); + if (ifa->ifa_flags & IFF_UP) + snprintfcat(msg, sizeof(msg), " IFF_UP"); + if (ifa->ifa_flags & IFF_RUNNING) + snprintfcat(msg, sizeof(msg), " IFF_RUNNING"); + if (ifa->ifa_flags & IFF_BROADCAST) + snprintfcat(msg, sizeof(msg), " IFF_BROADCAST(is assigned)"); + + upsdebugx(5, "Discovering getifaddrs(): %s", msg); + + /* TODO: also rule out "link-local" address ranges + * so we do not issue billions of worthless scans. + * FIXME: IPv6 may also be a problem, see + * https://github.com/networkupstools/nut/issues/2512 + */ + if (!(ifa->ifa_flags & IFF_LOOPBACK) + && (ifa->ifa_flags & IFF_UP) + && (ifa->ifa_flags & IFF_RUNNING) + && (ifa->ifa_flags & IFF_BROADCAST) + && (auto_nets == 46 + || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) + || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) + ) { + char cidr[LARGEBUF]; + + if (snprintf(cidr, sizeof(cidr), "%s/%i", addr, masklen) < 0) { + fatalx(EXIT_FAILURE, "Could not construct a CIDR string from discovered address/mask"); + } + + upsdebugx(5, "Processing CIDR net/mask: %s", cidr); + nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); + upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); + + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } + } /* else AF_UNIX or a dozen other types we do not care about here */ + } + } + 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"); +#endif } static void show_usage(void) From 45d873087d4ffdd5967d5d1e2541dafa715a60d7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 11:35:47 +0200 Subject: [PATCH 03/27] tools/nut-scanner/nut-scanner.c: handle_arg_cidr(): tabs (style) [#2244] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 2ecb67fb8e..0cdf9b13d7 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -421,7 +421,7 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) #ifndef WIN32 /* NOTE: Would need WIN32-specific implementation */ /* Inspired by https://stackoverflow.com/a/63789267/4715872 */ - struct ifaddrs *ifap; + struct ifaddrs *ifap; #endif /* Is this a `-m auto` mode? */ @@ -474,14 +474,14 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) "Failed to getifaddrs() for connected subnet scan: %s\n", strerror(errno)); } else { - struct ifaddrs *ifa; - char msg[LARGEBUF]; + struct ifaddrs *ifa; + char msg[LARGEBUF]; /* Note: INET6_ADDRSTRLEN is large enough for IPv4 too, * and is smaller than LARGEBUF to avoid snprintf() * warnings that the result might not fit. */ - char addr[INET6_ADDRSTRLEN]; - char mask[INET6_ADDRSTRLEN]; - int masklen = 0; + char addr[INET6_ADDRSTRLEN]; + char mask[INET6_ADDRSTRLEN]; + int masklen = 0; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr) { @@ -491,7 +491,7 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) masklen = -1; if (ifa->ifa_addr->sa_family == AF_INET6) { - uint8_t i, j; + uint8_t i, j; /* Ensure proper alignment */ struct sockaddr_in6 sm; @@ -510,7 +510,7 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in6), mask, sizeof(mask), NULL, 0, NI_NUMERICHOST); snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); } else if (ifa->ifa_addr->sa_family == AF_INET) { - in_addr_t i; + in_addr_t i; /* Ensure proper alignment */ struct sockaddr_in sa, sm; From 5c1d4b0fa12b57974d848df429d286c32d7b7d24 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 11:37:25 +0200 Subject: [PATCH 04/27] tools/nut-scanner/nut-scanner.c: handle_arg_cidr(): line-wrap long printouts [#2244] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 0cdf9b13d7..e8eaac511f 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -508,7 +508,11 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) 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); - snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); + snprintf(msg, sizeof(msg), + "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, + ifa->ifa_name, addr, mask, + masklen, + (uintmax_t)ifa->ifa_flags); } else if (ifa->ifa_addr->sa_family == AF_INET) { in_addr_t i; @@ -525,7 +529,11 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) masklen += i & 1; i >>= 1; } - snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); + snprintf(msg, sizeof(msg), + "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, + ifa->ifa_name, addr, mask, + masklen, + (uintmax_t)ifa->ifa_flags); /* } else { snprintf(msg, sizeof(msg), "Addr family: %" PRIuMAX, (intmax_t)ifa->ifa_addr->sa_family); From 065002c6ef97d24c5eec81c29cdae34b8b88c3e0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 11:38:53 +0200 Subject: [PATCH 05/27] tools/nut-scanner/nut-scanner.c: handle_arg_cidr(): log why some discovered subnets are filtered out [#2244] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 53 ++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index e8eaac511f..a5d5c84b89 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -476,6 +476,7 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) } else { struct ifaddrs *ifa; char msg[LARGEBUF]; + char cidr[LARGEBUF]; /* Note: INET6_ADDRSTRLEN is large enough for IPv4 too, * and is smaller than LARGEBUF to avoid snprintf() * warnings that the result might not fit. */ @@ -552,33 +553,45 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) upsdebugx(5, "Discovering getifaddrs(): %s", msg); + if (ifa->ifa_flags & IFF_LOOPBACK) { + upsdebugx(6, "Subnet ignored: loopback"); + continue; + } + + if (!( + (ifa->ifa_flags & IFF_UP) + && (ifa->ifa_flags & IFF_RUNNING) + && (ifa->ifa_flags & IFF_BROADCAST) + )) { + upsdebugx(6, "Subnet ignored: not up and running, with a proper broadcast-able address"); + continue; + } + + if (!( + (auto_nets == 46 + || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) + || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) + )) { + upsdebugx(6, "Subnet ignored: not of the requested address family"); + continue; + } + /* TODO: also rule out "link-local" address ranges * so we do not issue billions of worthless scans. * FIXME: IPv6 may also be a problem, see * https://github.com/networkupstools/nut/issues/2512 */ - if (!(ifa->ifa_flags & IFF_LOOPBACK) - && (ifa->ifa_flags & IFF_UP) - && (ifa->ifa_flags & IFF_RUNNING) - && (ifa->ifa_flags & IFF_BROADCAST) - && (auto_nets == 46 - || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) - || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) - ) { - char cidr[LARGEBUF]; - - if (snprintf(cidr, sizeof(cidr), "%s/%i", addr, masklen) < 0) { - fatalx(EXIT_FAILURE, "Could not construct a CIDR string from discovered address/mask"); - } + if (snprintf(cidr, sizeof(cidr), "%s/%i", addr, masklen_subnet) < 0) { + fatalx(EXIT_FAILURE, "Could not construct a CIDR string from discovered address/mask"); + } - upsdebugx(5, "Processing CIDR net/mask: %s", cidr); - nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); - upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); + upsdebugx(5, "Processing CIDR net/mask: %s", cidr); + nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); + upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); - add_ip_range(start_ip, end_ip); - start_ip = NULL; - end_ip = NULL; - } + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; } /* else AF_UNIX or a dozen other types we do not care about here */ } } From e91412e7ef008ce2d140be11a6fea3ca1a7dbb31 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 11:39:33 +0200 Subject: [PATCH 06/27] tools/nut-scanner/nut-scanner.c, docs/man/nut-scanner.txt, docs/nut.dict, NEWS.adoc: handle_arg_cidr(): add `-m auto*/ADDRLEN` mode [#2244] Signed-off-by: Jim Klimov --- NEWS.adoc | 5 ++- docs/man/nut-scanner.txt | 8 ++++ docs/nut.dict | 4 +- tools/nut-scanner/nut-scanner.c | 70 ++++++++++++++++++++++++++++----- 4 files changed, 75 insertions(+), 12 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 231fabb543..83c12ddfa4 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -144,7 +144,10 @@ installed. 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). - [issue #2244, PR #2509, PR #2513] + 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). + [issue #2244, PR #2509, PR #2513, PR #2517] * bumped version of `libnutscan` to 2.5.2, it now includes a few more methods and symbols from `libcommon`. [issue #2244, PR #2509] diff --git a/docs/man/nut-scanner.txt b/docs/man/nut-scanner.txt index fe66ff113e..f97ce2070f 100644 --- a/docs/man/nut-scanner.txt +++ b/docs/man/nut-scanner.txt @@ -151,6 +151,14 @@ A special form `-m auto` allows `nut-scanner` to detect local IP address(es) and scan corresponding subnet(s) on supported platforms, and `-m auto4` or `-m auto6` limits the selected addresses to IPv4 and IPv6 respectively. Only the first "auto*" request would be honoured, others ignored with a warning. ++ +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). For example, if your IPv4 LAN's +network range is `10.2.3.0/24`, its address part is `(32-24)=8`. Note that +while this is applied to IPv6 networks also, their typical `/64` subnets +are not likely to have a NUT/SNMP/NetXML/... server *that* close nearby +(in addressing terms), for a tight filter to find them. Default is `8`. NUT DEVICE OPTION ----------------- diff --git a/docs/nut.dict b/docs/nut.dict index 559bbc115d..5f09065786 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3178 utf-8 +personal_ws-1.1 en 3180 utf-8 AAC AAS ABI @@ -11,6 +11,7 @@ ACPresent ADDR ADDRCONFIG ADDRINFO +ADDRLEN ADELSYSTEM ADK ADKK @@ -2848,6 +2849,7 @@ sublicenses submodule submodules subnet +subnets subtree sudo suid diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index a5d5c84b89..8d3cac70e7 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -417,6 +417,15 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) char *start_ip = NULL, *end_ip = NULL; /* Scanning mode: IPv4, IPv6 or both */ int auto_nets = -1; + /* Bit-length limit for *address part* of subnets to consider; + * e.g. if your LAN's network range is 10.2.3.0/24 the address + * part is (32-24)=8. Larger subnets e.g. 10.0.0.0/8 would be + * ignored to avoid billions of scan requests. Note that while + * this is applied to IPv6 also, their typical /64 subnets are + * not likely to have a NUT/SNMP/NetXML/... server *that* close + * nearby in addressing terms, for a tight filter to find them. + */ + int masklen_hosts_limit = 8; #ifndef WIN32 /* NOTE: Would need WIN32-specific implementation */ @@ -426,6 +435,9 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) /* Is this a `-m auto` mode? */ if (!strncmp(optarg, "auto", 4)) { + /* TODO: Maybe split later, to allow separate + * `-m auto4/X` and `-m auto6/Y` requests? + */ if (auto_nets_ptr && *auto_nets_ptr) { fprintf(stderr, "Duplicate request for connected subnet scan ignored\n"); return; @@ -440,6 +452,30 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) auto_nets = 4; } else if (!strcmp(optarg, "auto6")) { auto_nets = 6; + } else if (!strncmp(optarg, "auto/", 5)) { + auto_nets = 46; + masklen_hosts_limit = atoi(optarg + 5); + if (masklen_hosts_limit < 0 || masklen_hosts_limit > 128) { + fatalx(EXIT_FAILURE, + "Invalid auto-net limit value: %s", + optarg); + } + } else if (!strncmp(optarg, "auto4/", 6)) { + auto_nets = 4; + masklen_hosts_limit = atoi(optarg + 6); + if (masklen_hosts_limit < 0 || masklen_hosts_limit > 32) { + fatalx(EXIT_FAILURE, + "Invalid auto-net limit value: %s", + optarg); + } + } else if (!strncmp(optarg, "auto6/", 6)) { + auto_nets = 6; + masklen_hosts_limit = atoi(optarg + 6); + if (masklen_hosts_limit < 0 || masklen_hosts_limit > 128) { + fatalx(EXIT_FAILURE, + "Invalid auto-net limit value: %s", + optarg); + } } else { /* TODO: maybe fail right away? * Or claim a simple auto46 mode? */ @@ -482,14 +518,15 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) * warnings that the result might not fit. */ char addr[INET6_ADDRSTRLEN]; char mask[INET6_ADDRSTRLEN]; - int masklen = 0; + int masklen_subnet = 0; + int masklen_hosts = 0; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr) { memset(msg, 0, sizeof(msg)); memset(addr, 0, sizeof(addr)); memset(mask, 0, sizeof(mask)); - masklen = -1; + masklen_subnet = -1; if (ifa->ifa_addr->sa_family == AF_INET6) { uint8_t i, j; @@ -498,21 +535,22 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) struct sockaddr_in6 sm; memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in6)); - masklen = 0; + masklen_subnet = 0; for (j = 0; j < 16; j++) { i = sm.sin6_addr.s6_addr[j]; while (i) { - masklen += i & 1; + masklen_subnet += i & 1; i >>= 1; } } + 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); snprintf(msg, sizeof(msg), - "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, + "Interface: %s\tAddress: %s\tMask: %s (subnet: %i, hosts: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, - masklen, + masklen_subnet, masklen_hosts, (uintmax_t)ifa->ifa_flags); } else if (ifa->ifa_addr->sa_family == AF_INET) { in_addr_t i; @@ -525,15 +563,17 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) snprintf(mask, sizeof(mask), "%s", inet_ntoa(sm.sin_addr)); i = sm.sin_addr.s_addr; - masklen = 0; + masklen_subnet = 0; while (i) { - masklen += i & 1; + masklen_subnet += i & 1; i >>= 1; } + masklen_hosts = 32 - masklen_subnet; + snprintf(msg, sizeof(msg), - "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, + "Interface: %s\tAddress: %s\tMask: %s (subnet: %i, hosts: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, - masklen, + masklen_subnet, masklen_hosts, (uintmax_t)ifa->ifa_flags); /* } else { @@ -553,6 +593,16 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) upsdebugx(5, "Discovering getifaddrs(): %s", msg); + if (masklen_hosts_limit < masklen_hosts) { + /* NOTE: masklen_hosts==0 means + * an exact hit on one address, + * so an IPv4/32 or IPv6/128. + */ + upsdebugx(6, "Subnet ignored: address range too large: %d bits allowed vs. %d bits per netmask", + masklen_hosts_limit, masklen_hosts); + continue; + } + if (ifa->ifa_flags & IFF_LOOPBACK) { upsdebugx(6, "Subnet ignored: loopback"); continue; From c2f52212f75865be22f8fd692e263a359ce187f5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 12:00:42 +0200 Subject: [PATCH 07/27] tools/nut-scanner/nut-scanner.c: handle_arg_cidr() rename "optarg" to "arg_addr" [#2244] Avoid warning about clash with a global variable. Fallout of refactoring original code into a method. Also make it `const` as we do not change the original value anyway. Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 8d3cac70e7..67da2806e5 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -412,7 +412,7 @@ static void * run_eaton_serial(void *arg) return NULL; } -static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) +static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) { char *start_ip = NULL, *end_ip = NULL; /* Scanning mode: IPv4, IPv6 or both */ @@ -434,7 +434,7 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) #endif /* Is this a `-m auto` mode? */ - if (!strncmp(optarg, "auto", 4)) { + if (!strncmp(arg_addr, "auto", 4)) { /* TODO: Maybe split later, to allow separate * `-m auto4/X` and `-m auto6/Y` requests? */ @@ -446,35 +446,35 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) /* Not very efficient to stack strcmp's, but * also not a hot codepath to care much, either. */ - if (!strcmp(optarg, "auto")) { + if (!strcmp(arg_addr, "auto")) { auto_nets = 46; - } else if (!strcmp(optarg, "auto4")) { + } else if (!strcmp(arg_addr, "auto4")) { auto_nets = 4; - } else if (!strcmp(optarg, "auto6")) { + } else if (!strcmp(arg_addr, "auto6")) { auto_nets = 6; - } else if (!strncmp(optarg, "auto/", 5)) { + } else if (!strncmp(arg_addr, "auto/", 5)) { auto_nets = 46; - masklen_hosts_limit = atoi(optarg + 5); + masklen_hosts_limit = atoi(arg_addr + 5); if (masklen_hosts_limit < 0 || masklen_hosts_limit > 128) { fatalx(EXIT_FAILURE, "Invalid auto-net limit value: %s", - optarg); + arg_addr); } - } else if (!strncmp(optarg, "auto4/", 6)) { + } else if (!strncmp(arg_addr, "auto4/", 6)) { auto_nets = 4; - masklen_hosts_limit = atoi(optarg + 6); + masklen_hosts_limit = atoi(arg_addr + 6); if (masklen_hosts_limit < 0 || masklen_hosts_limit > 32) { fatalx(EXIT_FAILURE, "Invalid auto-net limit value: %s", - optarg); + arg_addr); } - } else if (!strncmp(optarg, "auto6/", 6)) { + } else if (!strncmp(arg_addr, "auto6/", 6)) { auto_nets = 6; - masklen_hosts_limit = atoi(optarg + 6); + masklen_hosts_limit = atoi(arg_addr + 6); if (masklen_hosts_limit < 0 || masklen_hosts_limit > 128) { fatalx(EXIT_FAILURE, "Invalid auto-net limit value: %s", - optarg); + arg_addr); } } else { /* TODO: maybe fail right away? @@ -482,7 +482,7 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) upsdebugx(0, "Got a '-m auto*' CLI option with unsupported " "keyword pattern; assuming a CIDR, " - "likely to fail: %s", optarg); + "likely to fail: %s", arg_addr); } /* Let the caller know, to allow for run-once support */ @@ -493,8 +493,8 @@ static void handle_arg_cidr(char *optarg, int *auto_nets_ptr) if (auto_nets < 0) { /* not a supported `-m auto*` pattern => is `-m cidr` */ - upsdebugx(5, "Processing CIDR net/mask: %s", optarg); - nutscan_cidr_to_ip(optarg, &start_ip, &end_ip); + upsdebugx(5, "Processing CIDR net/mask: %s", arg_addr); + nutscan_cidr_to_ip(arg_addr, &start_ip, &end_ip); upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); add_ip_range(start_ip, end_ip); From 58a4e23573c46976dc03c3ab37069d99ae269300 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 12:05:13 +0200 Subject: [PATCH 08/27] tools/nut-scanner/nut-scanner.c: handle_arg_cidr(): report ignoring a subnet by family before checking for mask length [#2244] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 67da2806e5..60579a17e4 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -593,6 +593,15 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) upsdebugx(5, "Discovering getifaddrs(): %s", msg); + if (!( + (auto_nets == 46 + || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) + || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) + )) { + upsdebugx(6, "Subnet ignored: not of the requested address family"); + continue; + } + if (masklen_hosts_limit < masklen_hosts) { /* NOTE: masklen_hosts==0 means * an exact hit on one address, @@ -617,15 +626,6 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) continue; } - if (!( - (auto_nets == 46 - || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) - || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) - )) { - upsdebugx(6, "Subnet ignored: not of the requested address family"); - continue; - } - /* TODO: also rule out "link-local" address ranges * so we do not issue billions of worthless scans. * FIXME: IPv6 may also be a problem, see From b116f75d3a98d5136d0dcc3c025d8d77e12cb614 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 12:48:22 +0200 Subject: [PATCH 09/27] tools/nut-scanner/nut-scanner.c: handle_arg_cidr(): fix to use strtol() not atoi() [#2244] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 42 ++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 60579a17e4..8aa504f168 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -425,7 +425,9 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) * not likely to have a NUT/SNMP/NetXML/... server *that* close * nearby in addressing terms, for a tight filter to find them. */ - int masklen_hosts_limit = 8; + long masklen_hosts_limit = 8; + char *s = NULL; + int errno_saved; #ifndef WIN32 /* NOTE: Would need WIN32-specific implementation */ @@ -454,26 +456,44 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) auto_nets = 6; } else if (!strncmp(arg_addr, "auto/", 5)) { auto_nets = 46; - masklen_hosts_limit = atoi(arg_addr + 5); - if (masklen_hosts_limit < 0 || masklen_hosts_limit > 128) { + errno = 0; + masklen_hosts_limit = strtol(arg_addr + 5, &s, 10); + errno_saved = errno; + upsdebugx(6, "errno=%d s='%s'(%p) input='%s'(%p) output=%ld", + errno_saved, NUT_STRARG(s), (void *)s, + arg_addr + 5, (void *)(arg_addr + 5), + masklen_hosts_limit); + if (errno_saved || (s && *s != '\0') || masklen_hosts_limit < 0 || masklen_hosts_limit > 128) { fatalx(EXIT_FAILURE, - "Invalid auto-net limit value: %s", + "Invalid auto-net limit value, should be an integer [0..128]: %s", arg_addr); } } else if (!strncmp(arg_addr, "auto4/", 6)) { auto_nets = 4; - masklen_hosts_limit = atoi(arg_addr + 6); - if (masklen_hosts_limit < 0 || masklen_hosts_limit > 32) { + errno = 0; + masklen_hosts_limit = strtol(arg_addr + 6, &s, 10); + errno_saved = errno; + upsdebugx(6, "errno=%d s='%s'(%p) input='%s'(%p) output=%ld", + errno_saved, NUT_STRARG(s), (void *)s, + arg_addr + 6, (void *)(arg_addr + 6), + masklen_hosts_limit); + if (errno_saved || (s && *s != '\0') || masklen_hosts_limit < 0 || masklen_hosts_limit > 32) { fatalx(EXIT_FAILURE, - "Invalid auto-net limit value: %s", + "Invalid auto-net limit value, should be an integer [0..32]: %s", arg_addr); } } else if (!strncmp(arg_addr, "auto6/", 6)) { auto_nets = 6; - masklen_hosts_limit = atoi(arg_addr + 6); - if (masklen_hosts_limit < 0 || masklen_hosts_limit > 128) { + errno = 0; + masklen_hosts_limit = strtol(arg_addr + 6, &s, 10); + errno_saved = errno; + upsdebugx(6, "errno=%d s='%s'(%p) input='%s'(%p) output=%ld", + errno_saved, NUT_STRARG(s), (void *)s, + arg_addr + 6, (void *)(arg_addr + 6), + masklen_hosts_limit); + if (errno_saved || (s && *s != '\0') || masklen_hosts_limit < 0 || masklen_hosts_limit > 128) { fatalx(EXIT_FAILURE, - "Invalid auto-net limit value: %s", + "Invalid auto-net limit value, should be an integer [0..128]: %s", arg_addr); } } else { @@ -607,7 +627,7 @@ static void handle_arg_cidr(const char *arg_addr, int *auto_nets_ptr) * an exact hit on one address, * so an IPv4/32 or IPv6/128. */ - upsdebugx(6, "Subnet ignored: address range too large: %d bits allowed vs. %d bits per netmask", + upsdebugx(6, "Subnet ignored: address range too large: %ld bits allowed vs. %d bits per netmask", masklen_hosts_limit, masklen_hosts); continue; } From 60c4d33caec09925097aff95f5b9b9766ee4226a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 12:49:11 +0200 Subject: [PATCH 10/27] tools/nut-scanner/nut-scanner.c: `-t timeout`: fix to use strtol() not atol() [#2244] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 8aa504f168..ce1ed9b7d2 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -957,12 +957,28 @@ int main(int argc, char *argv[]) switch(opt_ret) { case 't': - timeout = (useconds_t)atol(optarg) * 1000 * 1000; /*in usec*/ - if (timeout <= 0) { - fprintf(stderr, - "Illegal timeout value, using default %ds\n", - DEFAULT_NETWORK_TIMEOUT); - timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000; + { // scoping + long l; + char *s = NULL; + int errno_saved; + + errno = 0; + l = strtol(optarg, &s, 10); + errno_saved = errno; + upsdebugx(6, "errno=%d s='%s'(%p) input='%s'(%p) output=%ld", + errno_saved, NUT_STRARG(s), (void *)s, + optarg, (void *)(optarg), l); + + if (errno_saved || (s && *s != '\0') || l <= 0) { + /* TODO: Any max limit? At least, + * max(useconds_t)/1000000 ? */ + fprintf(stderr, + "Illegal timeout value, using default %ds\n", + DEFAULT_NETWORK_TIMEOUT); + timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000; + } else { + timeout = (useconds_t)l * 1000 * 1000; /*in usec*/ + } } break; case 's': From 27774238ab87732a947398b9411f9500a72566ce Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 18:33:08 +0200 Subject: [PATCH 11/27] tools/nut-scanner/nut-scan.h: move __cplusplus extern "C" guard up to before we declare vars and types Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scan.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tools/nut-scanner/nut-scan.h b/tools/nut-scanner/nut-scan.h index 3bba701369..4f8fcce0b5 100644 --- a/tools/nut-scanner/nut-scan.h +++ b/tools/nut-scanner/nut-scan.h @@ -73,7 +73,15 @@ # ifdef HAVE_SEMAPHORE # include # endif +#endif + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif +#ifdef HAVE_PTHREAD # if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) extern size_t max_threads, curr_threads, max_threads_netxml, max_threads_oldnut, max_threads_netsnmp; # endif @@ -88,12 +96,6 @@ typedef struct nutscan_thread { } nutscan_thread_t; #endif /* HAVE_PTHREAD */ -#ifdef __cplusplus -/* *INDENT-OFF* */ -extern "C" { -/* *INDENT-ON* */ -#endif - /* SNMP structure */ typedef struct nutscan_snmp { char * community; From 50471bb2cb4128dea6cd2b5d30af062b3529df0b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 18:34:30 +0200 Subject: [PATCH 12/27] tools/nut-scanner/nut-scanner.c, tools/nut-scanner/nutscan-ip.h: move ip_range_t definition into header [#2244] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 5 ----- tools/nut-scanner/nutscan-ip.h | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 0c3071d146..c871d81f98 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -138,11 +138,6 @@ static char * serial_ports = NULL; static int cli_link_detail_level = -1; /* Track requested IP ranges (from CLI or auto-discovery) */ -typedef struct ip_range_s { - char * start_ip; - char * end_ip; - struct ip_range_s * next; -} ip_range_t; static ip_range_t * ip_ranges = NULL; static ip_range_t * ip_ranges_last = NULL; static size_t ip_ranges_count = 0; diff --git a/tools/nut-scanner/nutscan-ip.h b/tools/nut-scanner/nutscan-ip.h index 8cdf6d8498..d67690a4aa 100644 --- a/tools/nut-scanner/nutscan-ip.h +++ b/tools/nut-scanner/nutscan-ip.h @@ -56,6 +56,14 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t *, const char * startIP, const cha char * nutscan_ip_iter_inc(nutscan_ip_iter_t *); int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip); +/* Track requested IP ranges (from CLI or auto-discovery) */ +/* One IP address range: */ +typedef struct ip_range_s { + char * start_ip; + char * end_ip; + struct ip_range_s * next; +} ip_range_t; + #ifdef __cplusplus /* *INDENT-OFF* */ } From 644bb3a4e5680756a1fd5d5f40bf259ab448af6f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jul 2024 19:06:54 +0200 Subject: [PATCH 13/27] nut-scanner code and docs: refactor with nutscan_ip_range_list_t type for ip_ranges[] list and helper metadata, and methods as part of libnutscan [#2244, #2511] Not bumping library version, because it was recently bumped as part of other PRs about this issue. Technically the scope of the library has been changed by new exported methods and header lines. Signed-off-by: Jim Klimov --- docs/man/Makefile.am | 9 +++ docs/man/nutscan.txt | 3 + docs/man/nutscan_add_ip_range.txt | 74 +++++++++++++++++++ docs/man/nutscan_free_ip_ranges.txt | 40 ++++++++++ docs/man/nutscan_init_ip_ranges.txt | 41 +++++++++++ docs/nut.dict | 3 +- tools/nut-scanner/nut-scanner.c | 110 ++++++---------------------- tools/nut-scanner/nutscan-ip.c | 95 ++++++++++++++++++++++++ tools/nut-scanner/nutscan-ip.h | 31 +++++++- 9 files changed, 313 insertions(+), 93 deletions(-) create mode 100644 docs/man/nutscan_add_ip_range.txt create mode 100644 docs/man/nutscan_free_ip_ranges.txt create mode 100644 docs/man/nutscan_init_ip_ranges.txt diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 52669c1cd4..dc6a747857 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -198,6 +198,9 @@ SRC_DEV_PAGES = \ nutscan_display_ups_conf_with_sanity_check.txt \ nutscan_display_ups_conf.txt \ nutscan_display_parsable.txt \ + nutscan_init_ip_ranges.txt \ + nutscan_free_ip_ranges.txt \ + nutscan_add_ip_range.txt \ nutscan_cidr_to_ip.txt \ nutscan_new_device.txt \ nutscan_free_device.txt \ @@ -316,6 +319,9 @@ MAN3_DEV_PAGES = \ nutscan_display_ups_conf_with_sanity_check.3 \ nutscan_display_ups_conf.3 \ nutscan_display_parsable.3 \ + nutscan_init_ip_ranges.3 \ + nutscan_free_ip_ranges.3 \ + nutscan_add_ip_range.3 \ nutscan_cidr_to_ip.3 \ nutscan_new_device.3 \ nutscan_free_device.3 \ @@ -391,6 +397,9 @@ HTML_DEV_MANS = \ nutscan_display_ups_conf_with_sanity_check.html \ nutscan_display_ups_conf.html \ nutscan_display_parsable.html \ + nutscan_init_ip_ranges.html \ + nutscan_free_ip_ranges.html \ + nutscan_add_ip_range.html \ nutscan_cidr_to_ip.html \ nutscan_new_device.html \ nutscan_free_device.html \ diff --git a/docs/man/nutscan.txt b/docs/man/nutscan.txt index bfef09f52b..7f3390d026 100644 --- a/docs/man/nutscan.txt +++ b/docs/man/nutscan.txt @@ -68,6 +68,9 @@ linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_add_option_to_device[3], +linkman:nutscan_init_ip_ranges[3], +linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_add_ip_range[3], linkman:nutscan_cidr_to_ip[3] Internet resources: diff --git a/docs/man/nutscan_add_ip_range.txt b/docs/man/nutscan_add_ip_range.txt new file mode 100644 index 0000000000..6d0a922202 --- /dev/null +++ b/docs/man/nutscan_add_ip_range.txt @@ -0,0 +1,74 @@ +NUTSCAN_FREE_IP_RANGES(3) +========================= + +NAME +---- + +nutscan_add_ip_range - Add an entry with IP address range (start and end +address) to a `nutscan_ip_range_list_t` structure. + +SYNOPSIS +-------- + + #include + + /* One IP address range: */ + typedef struct nutscan_ip_range_s { + char * start_ip; + char * end_ip; + struct nutscan_ip_range_s * next; + } nutscan_ip_range_t; + + /* List of IP address ranges and helper data: */ + typedef struct nutscan_ip_range_list_s { + nutscan_ip_range_t * ip_ranges; + nutscan_ip_range_t * ip_ranges_last; + size_t ip_ranges_count; + } nutscan_ip_range_list_t; + + + size_t nutscan_add_ip_range( + nutscan_ip_range_list_t *irl, + char * start_ip, + char * end_ip); + +DESCRIPTION +----------- + +The *nutscan_add_ip_range()* function can create and add a `nutscan_ip_range_t` +entry based on provided inputs to the specified `nutscan_ip_range_list_t` +structure. The resulting amount of entries in the structure is returned, +or 0 in case of non-fatal errors. + +This function skips work if: + +* the structure pointer is `NULL` (0 is returned); +* neither `start_ip` nor `end_ip` were provided, i.e. they have `NULL` values + (current list length from the structure is returned); +* failed to allocate the entry (fatal). + +If only one of `start_ip` or `end_ip` values was provided (not `NULL`), a +single-address range is created with both addresses set to the same pointer +value. + +The structure should be initialized before use by `nutscan_init_ip_ranges()`. + +The caller must free the contents of the structure after completing its use +by calling `nutscan_free_ip_ranges()` (after which the structure can be +re-used for a new list), and explicitly `free()` the structure object itself if +it was allocated dynamically (e.g. by calling `nutscan_init_ip_ranges(NULL)`). + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-ip.h' file. + +Currently there are no checks for duplicate or overlapping entries, so the +same IP addresses and whole IP address ranges can be added to the list (and +would eventually be scanned) many times. + +SEE ALSO +-------- + +linkman:nutscan_init_ip_ranges[3], linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_cidr_to_ip[3] diff --git a/docs/man/nutscan_free_ip_ranges.txt b/docs/man/nutscan_free_ip_ranges.txt new file mode 100644 index 0000000000..7b9ac15845 --- /dev/null +++ b/docs/man/nutscan_free_ip_ranges.txt @@ -0,0 +1,40 @@ +NUTSCAN_FREE_IP_RANGES(3) +========================= + +NAME +---- + +nutscan_free_ip_ranges - Free contents of a `nutscan_ip_range_list_t` +structure populated (and optionally created) by `nutscan_init_ip_ranges()` +and, more importantly, filled by a series of `nutscan_add_ip_range()` calls. + +SYNOPSIS +-------- + + #include + + void nutscan_free_ip_ranges(nutscan_ip_range_list_t *irl); + +DESCRIPTION +----------- + +The *nutscan_free_ip_ranges()* function can free a `nutscan_ip_range_list_t` +structure. Doing so, it frees the whole linked list of `nutscan_ip_range_t` +entries, and zeroes out helper properties. + +The structure itself is not freed (as it can be a statically allocated +variable on the stack), and can be re-used for a new list if needed. + +The caller must free the structure object if it was allocated dynamically +(e.g. by calling `nutscan_init_ip_ranges(NULL)`). + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-ip.h' file. + +SEE ALSO +-------- + +linkman:nutscan_init_ip_ranges[3], linkman:nutscan_add_ip_range[3], +linkman:nutscan_cidr_to_ip[3] diff --git a/docs/man/nutscan_init_ip_ranges.txt b/docs/man/nutscan_init_ip_ranges.txt new file mode 100644 index 0000000000..4151e86498 --- /dev/null +++ b/docs/man/nutscan_init_ip_ranges.txt @@ -0,0 +1,41 @@ +NUTSCAN_INIT_IP_RANGES(3) +========================= + +NAME +---- + +nutscan_init_ip_ranges - Initialize contents of a `nutscan_ip_range_list_t` +structure (and optionally create one in the first place). + +SYNOPSIS +-------- + + #include + + nutscan_ip_range_list_t * nutscan_init_ip_ranges(nutscan_ip_range_list_t *irl); + +DESCRIPTION +----------- + +The *nutscan_init_ip_ranges()* function can prepare a `nutscan_ip_range_list_t` +structure by zeroing out its fields. If the argument is `NULL`, the structure +is dynamically allocated. Either way, a pointer to it is returned. + +A structure passed by caller is not assumed to have any valid contents to free, +as it may have garbage from stack after allocation. + +The caller must free the contents of the structure after completing its use +by calling `nutscan_free_ip_ranges` (after which the structure can be re-used), +and explicitly `free()` the structure object itself if it was allocated +dynamically (e.g. by calling `nutscan_init_ip_ranges(NULL)`). + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-ip.h' file. + +SEE ALSO +-------- + +linkman:nutscan_free_ip_ranges[3], linkman:nutscan_add_ip_range[3], +linkman:nutscan_cidr_to_ip[3] diff --git a/docs/nut.dict b/docs/nut.dict index 559bbc115d..e897d63eb9 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3178 utf-8 +personal_ws-1.1 en 3179 utf-8 AAC AAS ABI @@ -2090,6 +2090,7 @@ ipp ippon ipv ipxe +irl isDefault isbmex ish diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index c871d81f98..a5556ab1a2 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -138,76 +138,7 @@ static char * serial_ports = NULL; static int cli_link_detail_level = -1; /* Track requested IP ranges (from CLI or auto-discovery) */ -static ip_range_t * ip_ranges = NULL; -static ip_range_t * ip_ranges_last = NULL; -static size_t ip_ranges_count = 0; - -static size_t add_ip_range(char * start_ip, char * end_ip) -{ - ip_range_t *p; - - if (!start_ip && !end_ip) { - upsdebugx(5, "%s: skip, no addresses were provided", __func__); - return ip_ranges_count; - } - - if (start_ip == NULL) { - upsdebugx(5, "%s: only end address was provided, setting start to same: %s", - __func__, end_ip); - start_ip = end_ip; - } - if (end_ip == NULL) { - upsdebugx(5, "%s: only start address was provided, setting end to same: %s", - __func__, start_ip); - end_ip = start_ip; - } - - p = xcalloc(1, sizeof(ip_range_t)); - - p->start_ip = start_ip; - p->end_ip = end_ip; - p->next = NULL; - - if (!ip_ranges) { - ip_ranges = p; - } - - if (ip_ranges_last) { - ip_ranges_last->next = p; - } - ip_ranges_last = p; - ip_ranges_count++; - - upsdebugx(1, "Recorded IP address range #%" PRIuSIZE ": [%s .. %s]", - ip_ranges_count, start_ip, end_ip); - - return ip_ranges_count; -} - -static void free_ip_ranges(void) -{ - ip_range_t *p = ip_ranges; - - while (p) { - ip_ranges = p->next; - - /* Only free the strings once, if they pointed to same */ - if (p->start_ip == p->end_ip && p->start_ip) { - free(p->start_ip); - } else { - if (p->start_ip) - free(p->start_ip); - if (p->end_ip) - free(p->end_ip); - } - - free(p); - p = ip_ranges; - } - - ip_ranges_last = NULL; - ip_ranges_count = 0; -} +static nutscan_ip_range_list_t ip_ranges_list; #ifdef HAVE_PTHREAD static pthread_t thread[TYPE_END]; @@ -271,10 +202,10 @@ static void * run_snmp(void * arg) { nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; nutscan_device_t * dev_ret; - ip_range_t *p = ip_ranges; + nutscan_ip_range_t *p = ip_ranges_list.ip_ranges; upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", - __func__, ip_ranges_count); + __func__, ip_ranges_list.ip_ranges_count); dev[TYPE_SNMP] = NULL; while (p) { @@ -296,10 +227,10 @@ static void * run_xml(void * arg) { nutscan_xml_t * sec = (nutscan_xml_t *)arg; nutscan_device_t * dev_ret; - ip_range_t *p = ip_ranges; + nutscan_ip_range_t *p = ip_ranges_list.ip_ranges; upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", - __func__, ip_ranges_count); + __func__, ip_ranges_list.ip_ranges_count); if (!p) { /* Probe broadcast */ @@ -328,11 +259,11 @@ static void * run_xml(void * arg) static void * run_nut_old(void *arg) { nutscan_device_t * dev_ret; - ip_range_t *p = ip_ranges; + nutscan_ip_range_t *p = ip_ranges_list.ip_ranges; NUT_UNUSED_VARIABLE(arg); upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", - __func__, ip_ranges_count); + __func__, ip_ranges_list.ip_ranges_count); dev[TYPE_NUT] = NULL; while (p) { @@ -370,10 +301,10 @@ static void * run_ipmi(void * arg) { nutscan_ipmi_t * sec = (nutscan_ipmi_t *)arg; nutscan_device_t * dev_ret; - ip_range_t *p = ip_ranges; + nutscan_ip_range_t *p = ip_ranges_list.ip_ranges; upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", - __func__, ip_ranges_count); + __func__, ip_ranges_list.ip_ranges_count); if (!p) { /* Probe local device */ @@ -674,6 +605,7 @@ int main(int argc, char *argv[]) nut_debug_level++; } + nutscan_init_ip_ranges(&ip_ranges_list); nutscan_init(); /* Default, see -Q/-N/-P below */ @@ -703,7 +635,7 @@ int main(int argc, char *argv[]) /* Save whatever we have, either * this one address or an earlier * known range with its end */ - add_ip_range(start_ip, end_ip); + nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); start_ip = NULL; end_ip = NULL; } @@ -711,7 +643,7 @@ int main(int argc, char *argv[]) start_ip = strdup(optarg); if (end_ip != NULL) { /* Already we know two addresses, save them */ - add_ip_range(start_ip, end_ip); + nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); start_ip = NULL; end_ip = NULL; } @@ -721,7 +653,7 @@ int main(int argc, char *argv[]) /* Save whatever we have, either * this one address or an earlier * known range with its start */ - add_ip_range(start_ip, end_ip); + nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); start_ip = NULL; end_ip = NULL; } @@ -729,7 +661,7 @@ int main(int argc, char *argv[]) end_ip = strdup(optarg); if (start_ip != NULL) { /* Already we know two addresses, save them */ - add_ip_range(start_ip, end_ip); + nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); start_ip = NULL; end_ip = NULL; } @@ -743,7 +675,7 @@ int main(int argc, char *argv[]) /* Save whatever we have, either * this one address or an earlier * known range with its start or end */ - add_ip_range(start_ip, end_ip); + nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); start_ip = NULL; end_ip = NULL; } @@ -865,7 +797,7 @@ int main(int argc, char *argv[]) nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); - add_ip_range(start_ip, end_ip); + nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); start_ip = NULL; end_ip = NULL; } @@ -885,7 +817,7 @@ int main(int argc, char *argv[]) nutscan_cidr_to_ip(optarg, &start_ip, &end_ip); upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); - add_ip_range(start_ip, end_ip); + nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); start_ip = NULL; end_ip = NULL; } @@ -1162,7 +1094,7 @@ int main(int argc, char *argv[]) if (start_ip != NULL || end_ip != NULL) { /* Something did not cancel out above */ - add_ip_range(start_ip, end_ip); + nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); start_ip = NULL; end_ip = NULL; } @@ -1209,7 +1141,7 @@ int main(int argc, char *argv[]) } if (allow_snmp && nutscan_avail_snmp) { - if (!ip_ranges_count) { + if (!ip_ranges_list.ip_ranges_count) { upsdebugx(quiet, "No IP range(s) requested, skipping SNMP"); nutscan_avail_snmp = 0; } @@ -1256,7 +1188,7 @@ int main(int argc, char *argv[]) } if (allow_oldnut && nutscan_avail_nut) { - if (!ip_ranges_count) { + if (!ip_ranges_list.ip_ranges_count) { upsdebugx(quiet, "No IP range(s) requested, skipping NUT bus (old libupsclient connect method)"); nutscan_avail_nut = 0; } @@ -1437,7 +1369,7 @@ int main(int argc, char *argv[]) upsdebugx(1, "SCANS DONE: free common scanner resources"); nutscan_free(); - free_ip_ranges(); + nutscan_free_ip_ranges(&ip_ranges_list); upsdebugx(1, "SCANS DONE: EXIT_SUCCESS"); return EXIT_SUCCESS; diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 2bb6976dd4..777661651a 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -88,6 +88,101 @@ static int ntop6(struct in6_addr * ip, char * host, GETNAMEINFO_TYPE_ARG46 host_ host, host_size, NULL, 0, NI_NUMERICHOST); } +/* Track requested IP ranges (from CLI or auto-discovery) */ +nutscan_ip_range_list_t *nutscan_init_ip_ranges(nutscan_ip_range_list_t *irl) +{ + if (!irl) { + irl = (nutscan_ip_range_list_t *)xcalloc(1, sizeof(nutscan_ip_range_list_t)); + } + + irl->ip_ranges = NULL; + irl->ip_ranges_last = NULL; + irl->ip_ranges_count = 0; + + return irl; +} + +void nutscan_free_ip_ranges(nutscan_ip_range_list_t *irl) +{ + nutscan_ip_range_t *p; + + if (!irl) { + upsdebugx(5, "%s: skip, no nutscan_ip_range_list_t was specified", __func__); + return; + } + + p = irl->ip_ranges; + while (p) { + irl->ip_ranges = p->next; + + /* Only free the strings once, if they pointed to same */ + if (p->start_ip == p->end_ip && p->start_ip) { + free(p->start_ip); + } else { + if (p->start_ip) + free(p->start_ip); + if (p->end_ip) + free(p->end_ip); + } + + free(p); + p = irl->ip_ranges; + } + + irl->ip_ranges_last = NULL; + irl->ip_ranges_count = 0; +} + +size_t nutscan_add_ip_range(nutscan_ip_range_list_t *irl, char * start_ip, char * end_ip) +{ + nutscan_ip_range_t *p; + + if (!irl) { + upsdebugx(5, "%s: skip, no nutscan_ip_range_list_t was specified", __func__); + return 0; + } + + if (!start_ip && !end_ip) { + upsdebugx(5, "%s: skip, no addresses were provided", __func__); + return irl->ip_ranges_count; + } + + if (start_ip == NULL) { + upsdebugx(5, "%s: only end address was provided, setting start to same: %s", + __func__, end_ip); + start_ip = end_ip; + } + if (end_ip == NULL) { + upsdebugx(5, "%s: only start address was provided, setting end to same: %s", + __func__, start_ip); + end_ip = start_ip; + } + + p = xcalloc(1, sizeof(nutscan_ip_range_t)); + + p->start_ip = start_ip; + p->end_ip = end_ip; + p->next = NULL; + + if (!irl->ip_ranges) { + /* First entry */ + irl->ip_ranges = p; + } + + if (irl->ip_ranges_last) { + /* Got earlier entries, promote the tail */ + irl->ip_ranges_last->next = p; + } + + irl->ip_ranges_last = p; + irl->ip_ranges_count++; + + upsdebugx(1, "Recorded IP address range #%" PRIuSIZE ": [%s .. %s]", + irl->ip_ranges_count, start_ip, end_ip); + + return irl->ip_ranges_count; +} + /* Return the first ip or NULL if error */ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const char * stopIP) { diff --git a/tools/nut-scanner/nutscan-ip.h b/tools/nut-scanner/nutscan-ip.h index d67690a4aa..c78baad765 100644 --- a/tools/nut-scanner/nutscan-ip.h +++ b/tools/nut-scanner/nutscan-ip.h @@ -58,11 +58,36 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip); /* Track requested IP ranges (from CLI or auto-discovery) */ /* One IP address range: */ -typedef struct ip_range_s { +typedef struct nutscan_ip_range_s { char * start_ip; char * end_ip; - struct ip_range_s * next; -} ip_range_t; + struct nutscan_ip_range_s * next; +} nutscan_ip_range_t; + +/* List of IP address ranges and helper data: */ +typedef struct nutscan_ip_range_list_s { + nutscan_ip_range_t * ip_ranges; + nutscan_ip_range_t * ip_ranges_last; + size_t ip_ranges_count; +} nutscan_ip_range_list_t; + +/* Initialize fields of caller-provided list + * (can allocate one if arg is NULL - caller + * must free it later). Does not assume that + * caller's list values are valid and should + * be freed (can be some garbage from stack). + * + * Returns pointer to the original or allocated list. + */ +nutscan_ip_range_list_t *nutscan_init_ip_ranges(nutscan_ip_range_list_t *irl); + +/* Free information from the list (does not + * free the list object itself, can be static) + * so it can be further re-used or freed. + */ +void nutscan_free_ip_ranges(nutscan_ip_range_list_t *irl); + +size_t nutscan_add_ip_range(nutscan_ip_range_list_t *irl, char * start_ip, char * end_ip); #ifdef __cplusplus /* *INDENT-OFF* */ From cfeec4e1f2c86b8677af619157bcfdd81ecaadc2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 01:14:11 +0200 Subject: [PATCH 14/27] docs/man/nut-scanner.txt: clarify about text host names as start/stop/cidr IPs [#2519] Signed-off-by: Jim Klimov --- docs/man/nut-scanner.txt | 5 +++++ docs/nut.dict | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/man/nut-scanner.txt b/docs/man/nut-scanner.txt index f97ce2070f..400552c0fd 100644 --- a/docs/man/nut-scanner.txt +++ b/docs/man/nut-scanner.txt @@ -125,6 +125,11 @@ Also note that some buses require IP address(es) to scan, and others have a different behavior when exactly no addresses are specified (it is not currently possible to mix the two behaviors in one invocation of the `nut-scanner` tool). + +A single-address range may be a host name which would be resolved into one IP +address by the system resolver. A CIDR using a host name and netmask length +would be resolved into an IP address and subjected to the mask application, +to query hosts "near" the named one. ++ Finally note that currently even if multi-threaded support is available, each range specification is a separate fan-out of queries constrained by the timeout. Requests to scan many single IP addresses will take a while to complete, much diff --git a/docs/nut.dict b/docs/nut.dict index 637ca5338e..b37bab3381 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3181 utf-8 +personal_ws-1.1 en 3182 utf-8 AAC AAS ABI @@ -2369,6 +2369,7 @@ nd nds netcat netclient +netmask netserver netsh netsnmp From d05e7680b585492213c2780969e72fc60880fa24 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 01:23:52 +0200 Subject: [PATCH 15/27] docs/man/Makefile.am: move a comment up to cover more relevant entries Signed-off-by: Jim Klimov --- docs/man/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index dc6a747857..9d0e12edb5 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -331,13 +331,13 @@ MAN3_DEV_PAGES = \ nutscan_get_serial_ports_list.3 \ nutscan_init.3 +# Alias page for one text describing two commands: upscli_readline_timeout.3: upscli_readline.3 touch $@ upscli_sendline_timeout.3: upscli_sendline.3 touch $@ -# Alias page for one text describing two commands: nutscan_add_commented_option_to_device.3: nutscan_add_option_to_device.3 touch $@ From fd74b205d100b8d89ea25388637edcc9e4ad7317 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 01:24:23 +0200 Subject: [PATCH 16/27] docs/man/Makefile.am: extend HTML_DEV_MANS_FICTION to be on par with .3 man pages Signed-off-by: Jim Klimov --- docs/man/Makefile.am | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 9d0e12edb5..1e9935aff4 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -414,8 +414,16 @@ HTML_DEV_MANS = \ # Can't make this work on all make implementations at once, so disabled for now # Anyway it would be the same man-like page for several functions HTML_DEV_MANS_FICTION = \ + upscli_readline_timeout.html \ + upscli_sendline_timeout.html \ nutscan_add_commented_option_to_device.html +upscli_readline_timeout.html: upscli_readline.html + test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ + +upscli_sendline_timeout.html: upscli_sendline.html + test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ + nutscan_add_commented_option_to_device.html: nutscan_add_option_to_device.html test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ From fe90f6b3e3845be95542703e263db3106d91a072 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 01:25:55 +0200 Subject: [PATCH 17/27] docs/man/nutscan_add_ip_range.txt: fix copy-paste typo in title [#2244, #2511] Signed-off-by: Jim Klimov --- docs/man/nutscan_add_ip_range.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/man/nutscan_add_ip_range.txt b/docs/man/nutscan_add_ip_range.txt index 6d0a922202..052acff54b 100644 --- a/docs/man/nutscan_add_ip_range.txt +++ b/docs/man/nutscan_add_ip_range.txt @@ -1,5 +1,5 @@ -NUTSCAN_FREE_IP_RANGES(3) -========================= +NUTSCAN_ADD_IP_RANGE(3) +======================= NAME ---- From 2ddd4c5310a02d732b304e3aed63fcab8f947f85 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 01:26:18 +0200 Subject: [PATCH 18/27] docs/man/nutscan_add_ip_range.txt: update wording and structure comments [#2244, #2511] Signed-off-by: Jim Klimov --- docs/man/nutscan_add_ip_range.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/man/nutscan_add_ip_range.txt b/docs/man/nutscan_add_ip_range.txt index 052acff54b..b85af52f11 100644 --- a/docs/man/nutscan_add_ip_range.txt +++ b/docs/man/nutscan_add_ip_range.txt @@ -4,8 +4,8 @@ NUTSCAN_ADD_IP_RANGE(3) NAME ---- -nutscan_add_ip_range - Add an entry with IP address range (start and end -address) to a `nutscan_ip_range_list_t` structure. +nutscan_add_ip_range - Add an entry with IP address range (starting +and ending addresses) to a `nutscan_ip_range_list_t` structure. SYNOPSIS -------- @@ -21,9 +21,9 @@ SYNOPSIS /* List of IP address ranges and helper data: */ typedef struct nutscan_ip_range_list_s { - nutscan_ip_range_t * ip_ranges; - nutscan_ip_range_t * ip_ranges_last; - size_t ip_ranges_count; + nutscan_ip_range_t * ip_ranges; /* Actual linked list of entries, first entry */ + nutscan_ip_range_t * ip_ranges_last; /* Pointer to end of list for quicker additions */ + size_t ip_ranges_count; /* Counter of added entries */ } nutscan_ip_range_list_t; From d968dff94312f3ddadad1edaa0ea378d49205867 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 01:26:44 +0200 Subject: [PATCH 19/27] tools/nut-scanner/nutscan-ip.c: fix comment markup Signed-off-by: Jim Klimov --- tools/nut-scanner/nutscan-ip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 777661651a..2ab0ab90ff 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -290,8 +290,8 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const } /* return the next IP -return NULL if there is no more IP -*/ + * return NULL if there is no more IP + */ char * nutscan_ip_iter_inc(nutscan_ip_iter_t * ip) { char host[SMALLBUF]; From bf8cc0dfe4bbf1158da968162c93061998ec30d3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 01:27:14 +0200 Subject: [PATCH 20/27] tools/nut-scanner/nutscan-ip.h: add nutscan_ip_range_list_t structure comments [#2244, #2511] Signed-off-by: Jim Klimov --- tools/nut-scanner/nutscan-ip.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/nut-scanner/nutscan-ip.h b/tools/nut-scanner/nutscan-ip.h index c78baad765..819aa970f2 100644 --- a/tools/nut-scanner/nutscan-ip.h +++ b/tools/nut-scanner/nutscan-ip.h @@ -66,9 +66,9 @@ typedef struct nutscan_ip_range_s { /* List of IP address ranges and helper data: */ typedef struct nutscan_ip_range_list_s { - nutscan_ip_range_t * ip_ranges; - nutscan_ip_range_t * ip_ranges_last; - size_t ip_ranges_count; + nutscan_ip_range_t * ip_ranges; /* Actual linked list of entries, first entry */ + nutscan_ip_range_t * ip_ranges_last; /* Pointer to end of list for quicker additions */ + size_t ip_ranges_count; /* Counter of added entries */ } nutscan_ip_range_list_t; /* Initialize fields of caller-provided list From 1a93d95ab7e9b478461b1b95f76f85480e146198 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 00:00:45 +0200 Subject: [PATCH 21/27] nut-scanner: implement iteration across nutscan_ip_range_list_t collection [#2511] Signed-off-by: Jim Klimov --- NEWS.adoc | 2 +- docs/man/Makefile.am | 34 +++++++++ docs/man/nutscan_add_ip_range.txt | 4 +- docs/man/nutscan_free_ip_ranges.txt | 4 +- docs/man/nutscan_init_ip_ranges.txt | 4 +- docs/man/nutscan_ip_ranges_iter_inc.txt | 44 +++++++++++ docs/man/nutscan_ip_ranges_iter_init.txt | 48 ++++++++++++ docs/man/nutscan_scan_ipmi.txt | 27 ++++--- docs/man/nutscan_scan_nut.txt | 19 ++++- docs/man/nutscan_scan_snmp.txt | 16 +++- docs/man/nutscan_scan_xml_http_range.txt | 24 ++++-- docs/nut.dict | 5 +- tools/nut-scanner/nut-scan.h | 4 + tools/nut-scanner/nutscan-ip.c | 97 ++++++++++++++++++++++++ tools/nut-scanner/nutscan-ip.h | 13 ++++ 15 files changed, 318 insertions(+), 27 deletions(-) create mode 100644 docs/man/nutscan_ip_ranges_iter_inc.txt create mode 100644 docs/man/nutscan_ip_ranges_iter_init.txt diff --git a/NEWS.adoc b/NEWS.adoc index 83c12ddfa4..874373ca8a 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -147,7 +147,7 @@ installed. 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). - [issue #2244, PR #2509, PR #2513, PR #2517] + [issue #2244, issue #2511, PR #2509, PR #2513, PR #2517] * bumped version of `libnutscan` to 2.5.2, it now includes a few more methods and symbols from `libcommon`. [issue #2244, PR #2509] diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 1e9935aff4..976e2f35c7 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -201,6 +201,8 @@ SRC_DEV_PAGES = \ nutscan_init_ip_ranges.txt \ nutscan_free_ip_ranges.txt \ nutscan_add_ip_range.txt \ + nutscan_ip_ranges_iter_init.txt \ + nutscan_ip_ranges_iter_inc.txt \ nutscan_cidr_to_ip.txt \ nutscan_new_device.txt \ nutscan_free_device.txt \ @@ -322,6 +324,8 @@ MAN3_DEV_PAGES = \ nutscan_init_ip_ranges.3 \ nutscan_free_ip_ranges.3 \ nutscan_add_ip_range.3 \ + nutscan_ip_ranges_iter_init.3 \ + nutscan_ip_ranges_iter_inc.3 \ nutscan_cidr_to_ip.3 \ nutscan_new_device.3 \ nutscan_free_device.3 \ @@ -338,6 +342,18 @@ upscli_readline_timeout.3: upscli_readline.3 upscli_sendline_timeout.3: upscli_sendline.3 touch $@ +nutscan_scan_ip_range_snmp.3: nutscan_scan_snmp.3 + touch $@ + +nutscan_scan_ip_range_xml_http.3: nutscan_scan_xml_http_range.3 + touch $@ + +nutscan_scan_ip_range_nut.3: nutscan_scan_nut.3 + touch $@ + +nutscan_scan_ip_range_ipmi.3: nutscan_scan_ipmi.3 + touch $@ + nutscan_add_commented_option_to_device.3: nutscan_add_option_to_device.3 touch $@ @@ -400,6 +416,8 @@ HTML_DEV_MANS = \ nutscan_init_ip_ranges.html \ nutscan_free_ip_ranges.html \ nutscan_add_ip_range.html \ + nutscan_ip_ranges_iter_init.html \ + nutscan_ip_ranges_iter_inc.html \ nutscan_cidr_to_ip.html \ nutscan_new_device.html \ nutscan_free_device.html \ @@ -416,6 +434,10 @@ HTML_DEV_MANS = \ HTML_DEV_MANS_FICTION = \ upscli_readline_timeout.html \ upscli_sendline_timeout.html \ + nutscan_scan_ip_range_snmp.html \ + nutscan_scan_ip_range_xml_http.html \ + nutscan_scan_ip_range_nut.html \ + nutscan_scan_ip_range_ipmi.html \ nutscan_add_commented_option_to_device.html upscli_readline_timeout.html: upscli_readline.html @@ -424,6 +446,18 @@ upscli_readline_timeout.html: upscli_readline.html upscli_sendline_timeout.html: upscli_sendline.html test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ +nutscan_scan_ip_range_snmp.html: nutscan_scan_snmp.html + test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ + +nutscan_scan_ip_range_xml_http.html: nutscan_scan_xml_http_range.html + test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ + +nutscan_scan_ip_range_nut.html: nutscan_scan_nut.html + test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ + +nutscan_scan_ip_range_ipmi.html: nutscan_scan_ipmi.html + test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ + nutscan_add_commented_option_to_device.html: nutscan_add_option_to_device.html test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ diff --git a/docs/man/nutscan_add_ip_range.txt b/docs/man/nutscan_add_ip_range.txt index b85af52f11..1d27810c1b 100644 --- a/docs/man/nutscan_add_ip_range.txt +++ b/docs/man/nutscan_add_ip_range.txt @@ -71,4 +71,6 @@ SEE ALSO -------- linkman:nutscan_init_ip_ranges[3], linkman:nutscan_free_ip_ranges[3], -linkman:nutscan_cidr_to_ip[3] +linkman:nutscan_cidr_to_ip[3], +linkman:nutscan_ip_ranges_iter_init[3], +linkman:nutscan_ip_ranges_iter_inc[3] diff --git a/docs/man/nutscan_free_ip_ranges.txt b/docs/man/nutscan_free_ip_ranges.txt index 7b9ac15845..f9baa2848c 100644 --- a/docs/man/nutscan_free_ip_ranges.txt +++ b/docs/man/nutscan_free_ip_ranges.txt @@ -37,4 +37,6 @@ SEE ALSO -------- linkman:nutscan_init_ip_ranges[3], linkman:nutscan_add_ip_range[3], -linkman:nutscan_cidr_to_ip[3] +linkman:nutscan_cidr_to_ip[3], +linkman:nutscan_ip_ranges_iter_init[3], +linkman:nutscan_ip_ranges_iter_inc[3] diff --git a/docs/man/nutscan_init_ip_ranges.txt b/docs/man/nutscan_init_ip_ranges.txt index 4151e86498..2eabdaa872 100644 --- a/docs/man/nutscan_init_ip_ranges.txt +++ b/docs/man/nutscan_init_ip_ranges.txt @@ -38,4 +38,6 @@ SEE ALSO -------- linkman:nutscan_free_ip_ranges[3], linkman:nutscan_add_ip_range[3], -linkman:nutscan_cidr_to_ip[3] +linkman:nutscan_cidr_to_ip[3], +linkman:nutscan_ip_ranges_iter_init[3], +linkman:nutscan_ip_ranges_iter_inc[3] diff --git a/docs/man/nutscan_ip_ranges_iter_inc.txt b/docs/man/nutscan_ip_ranges_iter_inc.txt new file mode 100644 index 0000000000..abbd12aa0c --- /dev/null +++ b/docs/man/nutscan_ip_ranges_iter_inc.txt @@ -0,0 +1,44 @@ +NUTSCAN_IP_RANGES_ITER_INC(3) +============================= + +NAME +---- + +nutscan_ip_ranges_iter_inc - Proceed with iteration of an IP address range +using a `nutscan_ip_range_list_iter_t` structure. + +SYNOPSIS +-------- + + #include + + char * nutscan_ip_ranges_iter_inc(nutscan_ip_range_list_iter_t *irliter); + +DESCRIPTION +----------- + +The *nutscan_ip_ranges_iter_inc()* function can prepare an iterator from +the specified `nutscan_ip_range_list_t` structure. + +This function skips work if: + +* the structure pointer is `NULL` (`NULL` is returned); +* the structure pointer's `ip_range` list is `NULL` (`NULL` is returned), +* the structure pointer's `ip_range_iter` pointer is `NULL` (`NULL` is + returned). + +Returns the next IP address from the currently iterated registered IP +address range, or switches iteration to the next range if no addresses +remained in the current one. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-ip.h' file. + +SEE ALSO +-------- + +linkman:nutscan_init_ip_ranges[3], linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_cidr_to_ip[3], +linkman:nutscan_ip_ranges_iter_init[3] diff --git a/docs/man/nutscan_ip_ranges_iter_init.txt b/docs/man/nutscan_ip_ranges_iter_init.txt new file mode 100644 index 0000000000..4c871c8e9b --- /dev/null +++ b/docs/man/nutscan_ip_ranges_iter_init.txt @@ -0,0 +1,48 @@ +NUTSCAN_IP_RANGES_ITER_INIT(3) +============================== + +NAME +---- + +nutscan_ip_ranges_iter_init - Begin iteration of an IP address range using +a `nutscan_ip_range_list_iter_t` structure. + +SYNOPSIS +-------- + + #include + + char * nutscan_ip_ranges_iter_init( + nutscan_ip_range_list_iter_t *irliter, + const nutscan_ip_range_list_t *irl); + +DESCRIPTION +----------- + +The *nutscan_ip_ranges_iter_init()* function can prepare an iterator from +the specified `nutscan_ip_range_list_t` structure, saving it into the +caller-provided `nutscan_ip_range_list_iter_t` helper object. + +Different iterators may be created to walk the same `nutscan_ip_range_list_t` +list from different scans independently, but the list and its contents should +not be freed while anyone references it. + +This function skips work if: + +* the structure pointer is `NULL` (`NULL` is returned); +* the structure pointer's `ip_range` list is `NULL` (`NULL` is returned). + +Returns the first IP address from the first registered IP address range. +Subsequent addresses can be returned by `nutscan_ip_ranges_iter_inc()`. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-ip.h' file. + +SEE ALSO +-------- + +linkman:nutscan_init_ip_ranges[3], linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_cidr_to_ip[3], +linkman:nutscan_ip_ranges_iter_inc[3] diff --git a/docs/man/nutscan_scan_ipmi.txt b/docs/man/nutscan_scan_ipmi.txt index a1fddcfce8..9d43210f68 100644 --- a/docs/man/nutscan_scan_ipmi.txt +++ b/docs/man/nutscan_scan_ipmi.txt @@ -12,22 +12,29 @@ SYNOPSIS #include nutscan_device_t * nutscan_scan_ipmi( - const char * startIP, - const char * stopIP, - nutscan_ipmi_t * sec); + const char * startIP, + const char * stopIP, + nutscan_ipmi_t * sec); + + nutscan_device_t * nutscan_scan_ip_range_ipmi( + nutscan_ip_range_list_t * irl, + nutscan_ipmi_t * sec); DESCRIPTION ----------- -The *nutscan_scan_ipmi()* function tries to detect IPMI manageable devices. +The *nutscan_scan_ipmi()* and *nutscan_scan_ip_range_ipmi()* functions +try to detect IPMI manageable devices. -If 'start_ip' is NULL, the function searches for a local PSU. +If 'start_ip' for the former or 'irl' for the latter are NULL, the +respective function searches for a local PSU. Otherwise, it searches for remote hosts that would serve IPMI protocols, and would try to authenticate using the data in 'sec' structure. -It issues a NUT request on every IP ranging from 'startIP' to 'stopIP', -where 'stopIP' is optional. Those IP arguments may be either IPv4 or IPv6 -addresses or host names. +The former issues an IPMI request on every IP ranging from 'startIP' to +'stopIP', where 'stopIP' is optional; the latter can walk several IP +address ranges represented by a `nutscan_ip_range_list_t` structure. +Those IP arguments may be either IPv4 or IPv6 addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. @@ -57,5 +64,7 @@ linkman:nutscan_display_ups_conf_with_sanity_check[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], -linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3], +linkman:nutscan_init_ip_ranges[3], +linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_add_ip_range[3], linkman:nutscan_cidr_to_ip[3] diff --git a/docs/man/nutscan_scan_nut.txt b/docs/man/nutscan_scan_nut.txt index 08c6520c88..1263a6be23 100644 --- a/docs/man/nutscan_scan_nut.txt +++ b/docs/man/nutscan_scan_nut.txt @@ -18,13 +18,21 @@ SYNOPSIS const char * port, useconds_t usec_timeout); + nutscan_device_t * nutscan_scan_ip_range_nut( + nutscan_ip_range_list_t * irl, + const char * port, + useconds_t usec_timeout); + DESCRIPTION ----------- -The *nutscan_scan_nut()* function try to detect available NUT services -and their associated devices. It issues a NUT request on every IP ranging -from 'startIP' to 'stopIP'. 'startIP' is mandatory, 'stopIP' is optional. -Those IP arguments may be either IPv4 or IPv6 addresses or host names. +The *nutscan_scan_nut()* and *nutscan_scan_ip_range_nut()* functions +try to detect available NUT services and their associated devices. +The former issues a NUT request on every IP ranging from 'startIP' +to 'stopIP' (where 'startIP' is mandatory, 'stopIP' is optional); +the latter can walk several IP address ranges represented by a +`nutscan_ip_range_list_t` structure. Those IP arguments may be +either IPv4 or IPv6 addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. @@ -54,4 +62,7 @@ linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3], +linkman:nutscan_init_ip_ranges[3], +linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_add_ip_range[3], linkman:nutscan_cidr_to_ip[3] diff --git a/docs/man/nutscan_scan_snmp.txt b/docs/man/nutscan_scan_snmp.txt index 3ae72ccfd1..f89aebdde1 100644 --- a/docs/man/nutscan_scan_snmp.txt +++ b/docs/man/nutscan_scan_snmp.txt @@ -18,12 +18,19 @@ SYNOPSIS useconds_t timeout, nutscan_snmp_t * sec); + nutscan_device_t * nutscan_scan_ip_range_snmp( + nutscan_ip_range_list_t * irl, + useconds_t usec_timeout, + nutscan_snmp_t * sec); + DESCRIPTION ----------- -The *nutscan_scan_snmp()* function try to detect NUT compatible SNMP -devices. It tries SNMP queries on every IP ranging from 'start_ip' to -'stop_ip'. Those IP arguments may be either IPv4 or IPv6 addresses or +The *nutscan_scan_snmp()* and *nutscan_scan_ip_range_snmp()* functions +try to detect NUT compatible SNMP devices. The former tries SNMP queries +on every IP ranging from 'start_ip' to 'stop_ip'; the latter can walk +several IP address ranges represented by a `nutscan_ip_range_list_t` +structure. Those IP arguments may be either IPv4 or IPv6 addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. @@ -90,4 +97,7 @@ linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3], +linkman:nutscan_init_ip_ranges[3], +linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_add_ip_range[3], linkman:nutscan_cidr_to_ip[3] diff --git a/docs/man/nutscan_scan_xml_http_range.txt b/docs/man/nutscan_scan_xml_http_range.txt index 70e0a86b23..1c9b667c81 100644 --- a/docs/man/nutscan_scan_xml_http_range.txt +++ b/docs/man/nutscan_scan_xml_http_range.txt @@ -18,16 +18,24 @@ SYNOPSIS useconds_t usec_timeout, nutscan_xml_t * sec) + nutscan_device_t * nutscan_scan_ip_range_xml_http( + nutscan_ip_range_list_t * irl, + useconds_t usec_timeout, + nutscan_xml_t * sec) + DESCRIPTION ----------- -The *nutscan_scan_xml_http_range()* function tries to detect NUT compatible -XML/HTTP devices. +The *nutscan_scan_xml_http_range()* and *nutscan_scan_ip_range_xml_http()* +functions try to detect NUT compatible XML/HTTP devices. -If 'start_ip' is NULL, the function does this by issuing a broadcast message -on currently configured network interfaces. +If 'start_ip' for the former or 'irl' for the latter are NULL, the +respective function does this by issuing a broadcast message on all +currently configured network interfaces. -Otherwise, it queries every IP ranging from 'start_ip' to 'stop_ip'. +Otherwise, the former queries every IP ranging from 'start_ip' to 'stop_ip', +where 'stopIP' is optional; the latter can walk several IP address ranges +represented by a `nutscan_ip_range_list_t` structure. Those IP arguments may be either IPv4 or IPv6 addresses or host names. It waits up to 'usec_timeout' microseconds for a response from potential @@ -55,4 +63,8 @@ linkman:nutscan_display_ups_conf_with_sanity_check[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], -linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3] +linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3], +linkman:nutscan_init_ip_ranges[3], +linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_add_ip_range[3], +linkman:nutscan_cidr_to_ip[3] diff --git a/docs/nut.dict b/docs/nut.dict index b37bab3381..2f15275041 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3182 utf-8 +personal_ws-1.1 en 3185 utf-8 AAC AAS ABI @@ -1698,6 +1698,7 @@ cts ctypes cua cuaa +curr customizations cvt cwd @@ -2092,11 +2093,13 @@ ippon ipv ipxe irl +irliter isDefault isbmex ish iso isolator +iter ivtscd jNUT jNut diff --git a/tools/nut-scanner/nut-scan.h b/tools/nut-scanner/nut-scan.h index 4f8fcce0b5..fd6f4374a3 100644 --- a/tools/nut-scanner/nut-scan.h +++ b/tools/nut-scanner/nut-scan.h @@ -162,20 +162,24 @@ typedef struct nutscan_usb { /* Scanning */ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip, useconds_t usec_timeout, nutscan_snmp_t * sec); +nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, useconds_t usec_timeout, nutscan_snmp_t * sec); nutscan_device_t * nutscan_scan_usb(nutscan_usb_t * scanopts); /* If "ip" == NULL, do a broadcast scan */ /* If sec->usec_timeout <= 0 then the common usec_timeout arg overrides it */ nutscan_device_t * nutscan_scan_xml_http_range(const char *start_ip, const char *end_ip, useconds_t usec_timeout, nutscan_xml_t * sec); +nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl, useconds_t usec_timeout, nutscan_xml_t * sec); nutscan_device_t * nutscan_scan_nut(const char * startIP, const char * stopIP, const char * port, useconds_t usec_timeout); +nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, const char * port, useconds_t usec_timeout); nutscan_device_t * nutscan_scan_nut_simulation(void); nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout); nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, nutscan_ipmi_t * sec); +nutscan_device_t * nutscan_scan_ip_range_ipmi(nutscan_ip_range_list_t * irl, nutscan_ipmi_t * sec); nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_list); diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 2ab0ab90ff..495cd81c96 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -183,6 +183,103 @@ size_t nutscan_add_ip_range(nutscan_ip_range_list_t *irl, char * start_ip, char return irl->ip_ranges_count; } +/* Return the first ip or NULL if error */ +char * nutscan_ip_ranges_iter_init(nutscan_ip_range_list_iter_t *irliter, const nutscan_ip_range_list_t *irl) +{ + char *ip_str; + + if (!irliter) { + upsdebugx(5, "%s: skip, no nutscan_ip_range_list_iter_t was specified", __func__); + return NULL; + } + + if (!irl) { + upsdebugx(5, "%s: skip, no nutscan_ip_range_list_t was specified", __func__); + return NULL; + } + + if (!irl->ip_ranges) { + upsdebugx(5, "%s: skip, empty nutscan_ip_range_list_t was specified", __func__); + return NULL; + } + + memset(irliter, 0, sizeof(nutscan_ip_range_list_iter_t)); + irliter->irl = irl; + irliter->ip_ranges_iter = irl->ip_ranges; + memset(&(irliter->curr_ip_iter), 0, sizeof(nutscan_ip_iter_t)); + + upsdebugx(4, "%s: beginning iteration with first IP range [%s .. %s]", + __func__, irliter->ip_ranges_iter->start_ip, + irliter->ip_ranges_iter->end_ip); + + ip_str = nutscan_ip_iter_init( + &(irliter->curr_ip_iter), + irliter->ip_ranges_iter->start_ip, + irliter->ip_ranges_iter->end_ip); + + upsdebugx(5, "%s: got IP from range: %s", __func__, NUT_STRARG(ip_str)); + return ip_str; +} + +/* return the next IP + * return NULL if there is no more IP + */ +char * nutscan_ip_ranges_iter_inc(nutscan_ip_range_list_iter_t *irliter) +{ + char *ip_str; + + if (!irliter) { + upsdebugx(5, "%s: skip, no nutscan_ip_range_list_iter_t was specified", __func__); + return NULL; + } + + if (!irliter->irl) { + upsdebugx(5, "%s: skip, no nutscan_ip_range_list_t was specified", __func__); + return NULL; + } + + if (!irliter->irl->ip_ranges) { + upsdebugx(5, "%s: skip, empty nutscan_ip_range_list_t was specified", __func__); + return NULL; + } + + if (!irliter->ip_ranges_iter) { + upsdebugx(5, "%s: skip, finished nutscan_ip_range_list_t was specified", __func__); + return NULL; + } + + ip_str = nutscan_ip_iter_inc(&(irliter->curr_ip_iter)); + + if (ip_str) { + upsdebugx(5, "%s: got IP from range: %s", __func__, NUT_STRARG(ip_str)); + return ip_str; + } + + upsdebugx(5, "%s: end of IP range [%s .. %s]", + __func__, irliter->ip_ranges_iter->start_ip, + irliter->ip_ranges_iter->end_ip); + + /* else: end of one range, try to switch to next */ + irliter->ip_ranges_iter = irliter->ip_ranges_iter->next; + if (!(irliter->ip_ranges_iter)) { + upsdebugx(5, "%s: end of whole IP range list", __func__); + return NULL; + } + + memset(&(irliter->curr_ip_iter), 0, sizeof(nutscan_ip_iter_t)); + upsdebugx(4, "%s: beginning iteration with next IP range [%s .. %s]", + __func__, irliter->ip_ranges_iter->start_ip, + irliter->ip_ranges_iter->end_ip); + + ip_str = nutscan_ip_iter_init( + &(irliter->curr_ip_iter), + irliter->ip_ranges_iter->start_ip, + irliter->ip_ranges_iter->end_ip); + + upsdebugx(5, "%s: got IP from range: %s", __func__, NUT_STRARG(ip_str)); + return ip_str; +} + /* Return the first ip or NULL if error */ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const char * stopIP) { diff --git a/tools/nut-scanner/nutscan-ip.h b/tools/nut-scanner/nutscan-ip.h index 819aa970f2..82b2035980 100644 --- a/tools/nut-scanner/nutscan-ip.h +++ b/tools/nut-scanner/nutscan-ip.h @@ -89,6 +89,19 @@ void nutscan_free_ip_ranges(nutscan_ip_range_list_t *irl); size_t nutscan_add_ip_range(nutscan_ip_range_list_t *irl, char * start_ip, char * end_ip); +/* Iterator over given nutscan_ip_range_list_t structure + * and the currently pointed-to range in its list. + * Several iterators may use the same range. + */ +typedef struct nutscan_ip_range_list_iter_s { + const nutscan_ip_range_list_t * irl; /* Structure with actual linked list of address-range entries */ + nutscan_ip_range_t * ip_ranges_iter; /* Helper for iteration: across the list of IP ranges */ + nutscan_ip_iter_t curr_ip_iter; /* Helper for iteration: across one currently iterated IP range */ +} nutscan_ip_range_list_iter_t; + +char * nutscan_ip_ranges_iter_init(nutscan_ip_range_list_iter_t *irliter, const nutscan_ip_range_list_t *irl); +char * nutscan_ip_ranges_iter_inc(nutscan_ip_range_list_iter_t *irliter); + #ifdef __cplusplus /* *INDENT-OFF* */ } From 777b8aea238e90fa6106a8bf6c3a1af906a93507 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 03:06:23 +0200 Subject: [PATCH 22/27] tools/nut-scanner/nutscan-ip.c: nutscan_add_ip_range(): save irl.start_ip<=irl.stop_ip in alphanumeric comparison order [#2511] Signed-off-by: Jim Klimov --- tools/nut-scanner/nutscan-ip.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 495cd81c96..22a7002f29 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -160,8 +160,14 @@ size_t nutscan_add_ip_range(nutscan_ip_range_list_t *irl, char * start_ip, char p = xcalloc(1, sizeof(nutscan_ip_range_t)); - p->start_ip = start_ip; - p->end_ip = end_ip; + if (start_ip == end_ip || strcmp(start_ip, end_ip) <= 0) { + p->start_ip = start_ip; + p->end_ip = end_ip; + } else { + p->start_ip = end_ip; + p->end_ip = start_ip; + } + p->next = NULL; if (!irl->ip_ranges) { From fcafaec10228e80d68ee26dbf2eb561f4fca485f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 03:34:14 +0200 Subject: [PATCH 23/27] nut-scanner: introduce nutscan_stringify_ip_ranges() [#2244, #2511] Signed-off-by: Jim Klimov --- docs/man/Makefile.am | 3 ++ docs/man/nutscan_add_ip_range.txt | 1 + docs/man/nutscan_free_ip_ranges.txt | 1 + docs/man/nutscan_init_ip_ranges.txt | 1 + docs/man/nutscan_stringify_ip_ranges.txt | 39 +++++++++++++++++++ docs/nut.dict | 3 +- tools/nut-scanner/nutscan-ip.c | 48 ++++++++++++++++++++++++ tools/nut-scanner/nutscan-ip.h | 4 ++ 8 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 docs/man/nutscan_stringify_ip_ranges.txt diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 976e2f35c7..3b62b95261 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -200,6 +200,7 @@ SRC_DEV_PAGES = \ nutscan_display_parsable.txt \ nutscan_init_ip_ranges.txt \ nutscan_free_ip_ranges.txt \ + nutscan_stringify_ip_ranges.txt \ nutscan_add_ip_range.txt \ nutscan_ip_ranges_iter_init.txt \ nutscan_ip_ranges_iter_inc.txt \ @@ -323,6 +324,7 @@ MAN3_DEV_PAGES = \ nutscan_display_parsable.3 \ nutscan_init_ip_ranges.3 \ nutscan_free_ip_ranges.3 \ + nutscan_stringify_ip_ranges.3 \ nutscan_add_ip_range.3 \ nutscan_ip_ranges_iter_init.3 \ nutscan_ip_ranges_iter_inc.3 \ @@ -415,6 +417,7 @@ HTML_DEV_MANS = \ nutscan_display_parsable.html \ nutscan_init_ip_ranges.html \ nutscan_free_ip_ranges.html \ + nutscan_stringify_ip_ranges.html \ nutscan_add_ip_range.html \ nutscan_ip_ranges_iter_init.html \ nutscan_ip_ranges_iter_inc.html \ diff --git a/docs/man/nutscan_add_ip_range.txt b/docs/man/nutscan_add_ip_range.txt index 1d27810c1b..8270c6cc69 100644 --- a/docs/man/nutscan_add_ip_range.txt +++ b/docs/man/nutscan_add_ip_range.txt @@ -71,6 +71,7 @@ SEE ALSO -------- linkman:nutscan_init_ip_ranges[3], linkman:nutscan_free_ip_ranges[3], +linkman:nutscan_stringify_ip_ranges[3], linkman:nutscan_cidr_to_ip[3], linkman:nutscan_ip_ranges_iter_init[3], linkman:nutscan_ip_ranges_iter_inc[3] diff --git a/docs/man/nutscan_free_ip_ranges.txt b/docs/man/nutscan_free_ip_ranges.txt index f9baa2848c..567e89dcfe 100644 --- a/docs/man/nutscan_free_ip_ranges.txt +++ b/docs/man/nutscan_free_ip_ranges.txt @@ -37,6 +37,7 @@ SEE ALSO -------- linkman:nutscan_init_ip_ranges[3], linkman:nutscan_add_ip_range[3], +linkman:nutscan_stringify_ip_ranges[3], linkman:nutscan_cidr_to_ip[3], linkman:nutscan_ip_ranges_iter_init[3], linkman:nutscan_ip_ranges_iter_inc[3] diff --git a/docs/man/nutscan_init_ip_ranges.txt b/docs/man/nutscan_init_ip_ranges.txt index 2eabdaa872..73094aaefc 100644 --- a/docs/man/nutscan_init_ip_ranges.txt +++ b/docs/man/nutscan_init_ip_ranges.txt @@ -38,6 +38,7 @@ SEE ALSO -------- linkman:nutscan_free_ip_ranges[3], linkman:nutscan_add_ip_range[3], +linkman:nutscan_stringify_ip_ranges[3], linkman:nutscan_cidr_to_ip[3], linkman:nutscan_ip_ranges_iter_init[3], linkman:nutscan_ip_ranges_iter_inc[3] diff --git a/docs/man/nutscan_stringify_ip_ranges.txt b/docs/man/nutscan_stringify_ip_ranges.txt new file mode 100644 index 0000000000..cc14793f10 --- /dev/null +++ b/docs/man/nutscan_stringify_ip_ranges.txt @@ -0,0 +1,39 @@ +NUTSCAN_STRINGIFY_IP_RANGES(3) +============================== + +NAME +---- + +nutscan_stringify_ip_ranges - Collect contents of a `nutscan_ip_range_list_t` +structure into a string buffer that can be further printed into logs. + +SYNOPSIS +-------- + + #include + + const char * nutscan_stringify_ip_ranges(nutscan_ip_range_list_t *irl); + +DESCRIPTION +----------- + +The *nutscan_stringify_ip_ranges()* function can walk a `nutscan_ip_range_list_t` +structure to report its contents: count of list items, and a comma-separated +listing with each item as a single token (if `start_ip==end_ip` in that range) +or a range as `start_ip .. end_ip`. + +Returns a pointer to internal statically allocated buffer which would be +overwritten by subsequent calls, but does not have to be freed by caller. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-ip.h' file. + +SEE ALSO +-------- + +linkman:nutscan_free_ip_ranges[3], linkman:nutscan_add_ip_range[3], +linkman:nutscan_cidr_to_ip[3], +linkman:nutscan_ip_ranges_iter_init[3], +linkman:nutscan_ip_ranges_iter_inc[3] diff --git a/docs/nut.dict b/docs/nut.dict index 2f15275041..6bb3967ff3 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3185 utf-8 +personal_ws-1.1 en 3186 utf-8 AAC AAS ABI @@ -2831,6 +2831,7 @@ strcpy strdup strerror strftime +stringify strlen strnlen strptime diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 22a7002f29..0d73b7f039 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -189,6 +189,54 @@ size_t nutscan_add_ip_range(nutscan_ip_range_list_t *irl, char * start_ip, char return irl->ip_ranges_count; } +const char * nutscan_stringify_ip_ranges(nutscan_ip_range_list_t *irl) +{ + static char buf[LARGEBUF - 64]; /* Leave some space for upsdebugx() prefixes */ + size_t len = 0; + + memset(buf, 0, sizeof(buf)); + len += snprintf(buf + len, sizeof(buf) - len, + "(%" PRIuSIZE ")[", + (irl ? irl->ip_ranges_count : 0)); + + if (irl && irl->ip_ranges && irl->ip_ranges_count) { + nutscan_ip_range_t *p; + size_t j; + + for ( + j = 0, p = irl->ip_ranges; + p && len < sizeof(buf) - 6; + p = p->next, j++ + ) { + if (j) { + buf[len++] = ','; + buf[len++] = ' '; + } + + if (len > sizeof(buf) - 6) { + /* Too little left, but enough for this */ + buf[len++] = '.'; + buf[len++] = '.'; + buf[len++] = '.'; + break; + } + + if (p->start_ip == p->end_ip || !strcmp(p->start_ip, p->end_ip)) { + len += snprintf(buf + len, sizeof(buf) - len, + "%s", p->start_ip); + } else { + len += snprintf(buf + len, sizeof(buf) - len, + "%s .. %s", p->start_ip, p->end_ip); + } + } + } + + if (len < sizeof(buf) - 1) + buf[len++] = ']'; + + return buf; +} + /* Return the first ip or NULL if error */ char * nutscan_ip_ranges_iter_init(nutscan_ip_range_list_iter_t *irliter, const nutscan_ip_range_list_t *irl) { diff --git a/tools/nut-scanner/nutscan-ip.h b/tools/nut-scanner/nutscan-ip.h index 82b2035980..a585d8d3ca 100644 --- a/tools/nut-scanner/nutscan-ip.h +++ b/tools/nut-scanner/nutscan-ip.h @@ -87,6 +87,10 @@ nutscan_ip_range_list_t *nutscan_init_ip_ranges(nutscan_ip_range_list_t *irl); */ void nutscan_free_ip_ranges(nutscan_ip_range_list_t *irl); +/* Prints contents of irl into a groovy-like string, + * using a static buffer (rewritten by each call) */ +const char * nutscan_stringify_ip_ranges(nutscan_ip_range_list_t *irl); + size_t nutscan_add_ip_range(nutscan_ip_range_list_t *irl, char * start_ip, char * end_ip); /* Iterator over given nutscan_ip_range_list_t structure From e5570963f8e8104d9bf2e1b5db597d81f8e8aa6d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 03:44:40 +0200 Subject: [PATCH 24/27] nut-scanner: convert IPMI scan to use nutscan_ip_ranges_iter*() [#2511] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 24 ++------------ tools/nut-scanner/scan_ipmi.c | 56 ++++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index d82d100f56..233d84989b 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -300,31 +300,11 @@ static void * run_avahi(void *arg) static void * run_ipmi(void * arg) { nutscan_ipmi_t * sec = (nutscan_ipmi_t *)arg; - nutscan_device_t * dev_ret; - nutscan_ip_range_t *p = ip_ranges_list.ip_ranges; - + upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", __func__, ip_ranges_list.ip_ranges_count); - if (!p) { - /* Probe local device */ - dev[TYPE_IPMI] = nutscan_scan_ipmi(NULL, NULL, sec); - - upsdebugx(2, "Finished %s query", __func__); - return NULL; - } - - dev[TYPE_IPMI] = NULL; - while (p) { - dev_ret = nutscan_scan_ipmi(p->start_ip, p->end_ip, sec); - if (!dev[TYPE_IPMI]) { - dev[TYPE_IPMI] = dev_ret; - } else { - dev[TYPE_IPMI] = nutscan_rewind_device( - nutscan_add_device_to_device(dev_ret, dev[TYPE_IPMI])); - } - p = p->next; - } + dev[TYPE_IPMI] = nutscan_scan_ip_range_ipmi(&ip_ranges_list, sec); upsdebugx(2, "Finished %s loop", __func__); return NULL; diff --git a/tools/nut-scanner/scan_ipmi.c b/tools/nut-scanner/scan_ipmi.c index 13ec34680e..14ca3576fc 100644 --- a/tools/nut-scanner/scan_ipmi.c +++ b/tools/nut-scanner/scan_ipmi.c @@ -602,7 +602,35 @@ nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t * Return NULL on error, or a valid nutscan_device_t otherwise */ nutscan_device_t * nutscan_scan_ipmi(const char * start_ip, const char * stop_ip, nutscan_ipmi_t * sec) { - nutscan_ip_iter_t ip; + nutscan_device_t *ndret; + + /* Are we scanning locally, or through the network? */ + if (start_ip || stop_ip) { + nutscan_ip_range_list_t irl; + + nutscan_init_ip_ranges(&irl); + nutscan_add_ip_range(&irl, (char *)start_ip, (char *)stop_ip); + + ndret = nutscan_scan_ip_range_ipmi(&irl, sec); + + /* Avoid nuking caller's strings here */ + irl.ip_ranges->start_ip = NULL; + irl.ip_ranges->end_ip = NULL; + nutscan_free_ip_ranges(&irl); + } else { + /* Probe local device */ + ndret = nutscan_scan_ip_range_ipmi(NULL, sec); + } + + return ndret; +} + +/* General IPMI scan entry point: scan 1 to n devices, local or remote, + * for IPMI support + * Return NULL on error, or a valid nutscan_device_t otherwise */ +nutscan_device_t * nutscan_scan_ip_range_ipmi(nutscan_ip_range_list_t * irl, nutscan_ipmi_t * sec) +{ + nutscan_ip_range_list_iter_t ip; char * ip_str = NULL; nutscan_ipmi_t * tmp_sec; nutscan_device_t * nut_dev = NULL; @@ -613,20 +641,23 @@ nutscan_device_t * nutscan_scan_ipmi(const char * start_ip, const char * stop_ip } /* Are we scanning locally, or through the network? */ - if (start_ip == NULL) + if (irl == NULL || irl->ip_ranges == NULL) { upsdebugx(1, "%s: Local PSU scan", __func__); current_nut_dev = nutscan_scan_ipmi_device(NULL, NULL); } else { - if (start_ip == stop_ip || !stop_ip) { + if (irl->ip_ranges_count == 1 + && (irl->ip_ranges->start_ip == irl->ip_ranges->end_ip + || !strcmp(irl->ip_ranges->start_ip, irl->ip_ranges->end_ip) + )) { upsdebugx(1, "%s: Scanning remote PSU for single IP address: %s", - __func__, start_ip); + __func__, irl->ip_ranges->start_ip); } else { - upsdebugx(1, "%s: Scanning remote PSU for IP address range: %s .. %s", - __func__, start_ip, stop_ip); + upsdebugx(1, "%s: Scanning remote PSU for IP address range(s): %s", + __func__, nutscan_stringify_ip_ranges(irl)); } - ip_str = nutscan_ip_iter_init(&ip, start_ip, stop_ip); + ip_str = nutscan_ip_ranges_iter_init(&ip, irl); while (ip_str != NULL) { tmp_sec = malloc(sizeof(nutscan_ipmi_t)); @@ -637,7 +668,7 @@ nutscan_device_t * nutscan_scan_ipmi(const char * start_ip, const char * stop_ip current_nut_dev = nutscan_add_device_to_device(current_nut_dev, nut_dev); } /* Prepare the next iteration */ - ip_str = nutscan_ip_iter_inc(&ip); + ip_str = nutscan_ip_ranges_iter_inc(&ip); } } @@ -656,4 +687,13 @@ nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, return NULL; } +/* stub function */ +nutscan_device_t * nutscan_scan_ip_range_ipmi(nutscan_ip_range_list_t * irl, nutscan_ipmi_t * sec) +{ + NUT_UNUSED_VARIABLE(irl); + NUT_UNUSED_VARIABLE(sec); + + return NULL; +} + #endif /* WITH_IPMI */ From defe4bc565f1be7d2908f24283d085430357073d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 04:02:12 +0200 Subject: [PATCH 25/27] nut-scanner: convert Old NUT scan to use nutscan_ip_ranges_iter*() [#2511] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 14 +--------- tools/nut-scanner/scan_nut.c | 45 +++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 233d84989b..67b8db342d 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -258,24 +258,12 @@ static void * run_xml(void * arg) static void * run_nut_old(void *arg) { - nutscan_device_t * dev_ret; - nutscan_ip_range_t *p = ip_ranges_list.ip_ranges; NUT_UNUSED_VARIABLE(arg); upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", __func__, ip_ranges_list.ip_ranges_count); - dev[TYPE_NUT] = NULL; - while (p) { - dev_ret = nutscan_scan_nut(p->start_ip, p->end_ip, port, timeout); - if (!dev[TYPE_NUT]) { - dev[TYPE_NUT] = dev_ret; - } else { - dev[TYPE_NUT] = nutscan_rewind_device( - nutscan_add_device_to_device(dev_ret, dev[TYPE_NUT])); - } - p = p->next; - } + dev[TYPE_NUT] = nutscan_scan_ip_range_nut(&ip_ranges_list, port, timeout); upsdebugx(2, "Finished %s loop", __func__); return NULL; diff --git a/tools/nut-scanner/scan_nut.c b/tools/nut-scanner/scan_nut.c index 9b5aa97fad..5ac4dc1995 100644 --- a/tools/nut-scanner/scan_nut.c +++ b/tools/nut-scanner/scan_nut.c @@ -235,10 +235,28 @@ static void * list_nut_devices(void * arg) return NULL; } -nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, const char* port, useconds_t usec_timeout) +nutscan_device_t * nutscan_scan_nut(const char* start_ip, const char* stop_ip, const char* port, useconds_t usec_timeout) +{ + nutscan_device_t *ndret; + nutscan_ip_range_list_t irl; + + nutscan_init_ip_ranges(&irl); + nutscan_add_ip_range(&irl, (char *)start_ip, (char *)stop_ip); + + ndret = nutscan_scan_ip_range_nut(&irl, port, usec_timeout); + + /* Avoid nuking caller's strings here */ + irl.ip_ranges->start_ip = NULL; + irl.ip_ranges->end_ip = NULL; + nutscan_free_ip_ranges(&irl); + + return ndret; +} + +nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, const char* port, useconds_t usec_timeout) { bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ - nutscan_ip_iter_t ip; + nutscan_ip_range_list_iter_t ip; char * ip_str = NULL; char * ip_dest = NULL; char buf[SMALLBUF]; @@ -303,14 +321,21 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con return NULL; } - if (!startIP) { + if (irl == NULL || irl->ip_ranges == NULL) { + return NULL; + } + + if (!irl->ip_ranges->start_ip) { upsdebugx(1, "%s: no starting IP address specified", __func__); - } else if (startIP == stopIP || !stopIP) { + } else if (irl->ip_ranges_count == 1 + && (irl->ip_ranges->start_ip == irl->ip_ranges->end_ip + || !strcmp(irl->ip_ranges->start_ip, irl->ip_ranges->end_ip) + )) { upsdebugx(1, "%s: Scanning \"Old NUT\" bus for single IP address: %s", - __func__, startIP); + __func__, irl->ip_ranges->start_ip); } else { - upsdebugx(1, "%s: Scanning \"Old NUT\" bus for IP address range: %s .. %s", - __func__, startIP, stopIP); + upsdebugx(1, "%s: Scanning \"Old NUT\" bus for IP address range(s): %s", + __func__, nutscan_stringify_ip_ranges(irl)); } #ifndef WIN32 @@ -330,7 +355,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con } #endif - ip_str = nutscan_ip_iter_init(&ip, startIP, stopIP); + ip_str = nutscan_ip_ranges_iter_init(&ip, irl); while (ip_str != NULL) { #ifdef HAVE_PTHREAD @@ -433,7 +458,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con if (pass) { if (port) { - if (ip.type == IPv4) { + if (ip.curr_ip_iter.type == IPv4) { snprintf(buf, sizeof(buf), "%s:%s", ip_str, port); } else { @@ -483,7 +508,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con list_nut_devices(nut_arg); #endif /* if HAVE_PTHREAD */ free(ip_str); - ip_str = nutscan_ip_iter_inc(&ip); + ip_str = nutscan_ip_ranges_iter_inc(&ip); } else { /* if not pass -- all slots busy */ #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE From 006b1de8edf7030c2746c12b20e34e62ecc9d959 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 04:10:02 +0200 Subject: [PATCH 26/27] nut-scanner: convert SNMP scan to use nutscan_ip_ranges_iter*() [#2511] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 14 +-------- tools/nut-scanner/scan_snmp.c | 52 ++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 67b8db342d..3bca00f128 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -201,23 +201,11 @@ static void * run_usb(void *arg) static void * run_snmp(void * arg) { nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; - nutscan_device_t * dev_ret; - nutscan_ip_range_t *p = ip_ranges_list.ip_ranges; upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", __func__, ip_ranges_list.ip_ranges_count); - dev[TYPE_SNMP] = NULL; - while (p) { - dev_ret = nutscan_scan_snmp(p->start_ip, p->end_ip, timeout, sec); - if (!dev[TYPE_SNMP]) { - dev[TYPE_SNMP] = dev_ret; - } else { - dev[TYPE_SNMP] = nutscan_rewind_device( - nutscan_add_device_to_device(dev_ret, dev[TYPE_SNMP])); - } - p = p->next; - } + dev[TYPE_SNMP] = nutscan_scan_ip_range_snmp(&ip_ranges_list, timeout, sec); upsdebugx(2, "Finished %s loop", __func__); return NULL; diff --git a/tools/nut-scanner/scan_snmp.c b/tools/nut-scanner/scan_snmp.c index 7c28a85235..210d138d76 100644 --- a/tools/nut-scanner/scan_snmp.c +++ b/tools/nut-scanner/scan_snmp.c @@ -1022,11 +1022,30 @@ static void * try_SysOID(void * arg) nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip, useconds_t usec_timeout, nutscan_snmp_t * sec) +{ + nutscan_device_t *ndret; + nutscan_ip_range_list_t irl; + + nutscan_init_ip_ranges(&irl); + nutscan_add_ip_range(&irl, (char *)start_ip, (char *)stop_ip); + + ndret = nutscan_scan_ip_range_snmp(&irl, usec_timeout, sec); + + /* Avoid nuking caller's strings here */ + irl.ip_ranges->start_ip = NULL; + irl.ip_ranges->end_ip = NULL; + nutscan_free_ip_ranges(&irl); + + return ndret; +} + +nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, + useconds_t usec_timeout, nutscan_snmp_t * sec) { bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ nutscan_device_t * result; nutscan_snmp_t * tmp_sec; - nutscan_ip_iter_t ip; + nutscan_ip_range_list_iter_t ip; char * ip_str = NULL; #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE @@ -1085,14 +1104,21 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip return NULL; } - if (!start_ip) { + if (irl == NULL || irl->ip_ranges == NULL) { + return NULL; + } + + if (!irl->ip_ranges->start_ip) { upsdebugx(1, "%s: no starting IP address specified", __func__); - } else if (start_ip == stop_ip || !stop_ip) { + } else if (irl->ip_ranges_count == 1 + && (irl->ip_ranges->start_ip == irl->ip_ranges->end_ip + || !strcmp(irl->ip_ranges->start_ip, irl->ip_ranges->end_ip) + )) { upsdebugx(1, "%s: Scanning SNMP for single IP address: %s", - __func__, start_ip); + __func__, irl->ip_ranges->start_ip); } else { - upsdebugx(1, "%s: Scanning SNMP for IP address range: %s .. %s", - __func__, start_ip, stop_ip); + upsdebugx(1, "%s: Scanning SNMP for IP address range(s): %s", + __func__, nutscan_stringify_ip_ranges(irl)); } g_usec_timeout = usec_timeout; @@ -1106,7 +1132,7 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip /* Initialize the SNMP library */ (*nut_init_snmp)("nut-scanner"); - ip_str = nutscan_ip_iter_init(&ip, start_ip, stop_ip); + ip_str = nutscan_ip_ranges_iter_init(&ip, irl); while (ip_str != NULL) { #ifdef HAVE_PTHREAD @@ -1241,7 +1267,7 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip try_SysOID((void *)tmp_sec); #endif /* if HAVE_PTHREAD */ /* free(ip_str); */ /* Do not free() here - seems to cause a double-free instead */ - ip_str = nutscan_ip_iter_inc(&ip); + ip_str = nutscan_ip_ranges_iter_inc(&ip); /* free(tmp_sec); */ } else { /* if not pass -- all slots busy */ #ifdef HAVE_PTHREAD @@ -1348,4 +1374,14 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip return NULL; } +/* stub function */ +nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, + useconds_t usec_timeout, nutscan_snmp_t * sec) +{ + NUT_UNUSED_VARIABLE(irl); + NUT_UNUSED_VARIABLE(usec_timeout); + NUT_UNUSED_VARIABLE(sec); + return NULL; +} + #endif /* WITH_SNMP */ From 808e3d3add3aa7a138184f9cd7da5e6cfee94070 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 9 Jul 2024 04:25:02 +0200 Subject: [PATCH 27/27] nut-scanner: convert NetXML scan to use nutscan_ip_ranges_iter*() [#2511] Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 22 +---------- tools/nut-scanner/scan_xml_http.c | 61 ++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 3bca00f128..eb11d35f5b 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -214,31 +214,11 @@ static void * run_snmp(void * arg) static void * run_xml(void * arg) { nutscan_xml_t * sec = (nutscan_xml_t *)arg; - nutscan_device_t * dev_ret; - nutscan_ip_range_t *p = ip_ranges_list.ip_ranges; upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", __func__, ip_ranges_list.ip_ranges_count); - if (!p) { - /* Probe broadcast */ - dev[TYPE_XML] = nutscan_scan_xml_http_range(NULL, NULL, timeout, sec); - - upsdebugx(2, "Finished %s query", __func__); - return NULL; - } - - dev[TYPE_XML] = NULL; - while (p) { - dev_ret = nutscan_scan_xml_http_range(p->start_ip, p->end_ip, timeout, sec); - if (!dev[TYPE_XML]) { - dev[TYPE_XML] = dev_ret; - } else { - dev[TYPE_XML] = nutscan_rewind_device( - nutscan_add_device_to_device(dev_ret, dev[TYPE_XML])); - } - p = p->next; - } + dev[TYPE_XML] = nutscan_scan_ip_range_xml_http(&ip_ranges_list, timeout, sec); upsdebugx(2, "Finished %s loop", __func__); return NULL; diff --git a/tools/nut-scanner/scan_xml_http.c b/tools/nut-scanner/scan_xml_http.c index ed635aba33..5032bd98f3 100644 --- a/tools/nut-scanner/scan_xml_http.c +++ b/tools/nut-scanner/scan_xml_http.c @@ -415,6 +415,31 @@ static void * nutscan_scan_xml_http_generic(void * arg) } nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char * end_ip, useconds_t usec_timeout, nutscan_xml_t * sec) +{ + nutscan_device_t *ndret; + + /* Are we scanning locally, or through the network? */ + if (start_ip || end_ip) { + nutscan_ip_range_list_t irl; + + nutscan_init_ip_ranges(&irl); + nutscan_add_ip_range(&irl, (char *)start_ip, (char *)end_ip); + + ndret = nutscan_scan_ip_range_xml_http(&irl, usec_timeout, sec); + + /* Avoid nuking caller's strings here */ + irl.ip_ranges->start_ip = NULL; + irl.ip_ranges->end_ip = NULL; + nutscan_free_ip_ranges(&irl); + } else { + /* Probe local device */ + ndret = nutscan_scan_ip_range_xml_http(NULL, usec_timeout, sec); + } + + return ndret; +} + +nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl, useconds_t usec_timeout, nutscan_xml_t * sec) { bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ nutscan_xml_t * tmp_sec = NULL; @@ -424,19 +449,18 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char return NULL; } - if (start_ip == NULL && end_ip != NULL) { - start_ip = end_ip; - } - - if (start_ip == NULL) { + if (irl == NULL || irl->ip_ranges == NULL) { upsdebugx(1, "%s: Scanning XML/HTTP bus using broadcast.", __func__); } else { - if ((start_ip == end_ip) || (end_ip == NULL) || (0 == strncmp(start_ip, end_ip, 128))) { + if (irl->ip_ranges_count == 1 + && (irl->ip_ranges->start_ip == irl->ip_ranges->end_ip + || !strcmp(irl->ip_ranges->start_ip, irl->ip_ranges->end_ip) + )) { upsdebugx(1, "%s: Scanning XML/HTTP bus for single IP address: %s", - __func__, start_ip); + __func__, irl->ip_ranges->start_ip); } else { /* Iterate the range of IPs to scan */ - nutscan_ip_iter_t ip; + nutscan_ip_range_list_iter_t ip; char * ip_str = NULL; #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE @@ -452,8 +476,8 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char # endif #endif - upsdebugx(1, "%s: Scanning XML/HTTP bus for IP address range: %s .. %s", - __func__, start_ip, end_ip); + upsdebugx(1, "%s: Scanning XML/HTTP bus for IP address range(s): %s", + __func__, nutscan_stringify_ip_ranges(irl)); #ifdef HAVE_PTHREAD pthread_mutex_init(&dev_mutex, NULL); @@ -489,7 +513,7 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char #endif /* HAVE_PTHREAD */ - ip_str = nutscan_ip_iter_init(&ip, start_ip, end_ip); + ip_str = nutscan_ip_ranges_iter_init(&ip, irl); while (ip_str != NULL) { #ifdef HAVE_PTHREAD @@ -633,7 +657,7 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char #endif /* if HAVE_PTHREAD */ /* free(ip_str); */ /* One of these free()s seems to cause a double-free instead */ - ip_str = nutscan_ip_iter_inc(&ip); + ip_str = nutscan_ip_ranges_iter_inc(&ip); /* free(tmp_sec); */ } else { /* if not pass -- all slots busy */ #ifdef HAVE_PTHREAD @@ -736,10 +760,10 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char } memcpy(tmp_sec, sec, sizeof(nutscan_xml_t)); - if (start_ip == NULL) { + if (irl == NULL || irl->ip_ranges == NULL || irl->ip_ranges->start_ip == NULL) { tmp_sec->peername = NULL; } else { - tmp_sec->peername = strdup(start_ip); + tmp_sec->peername = strdup(irl->ip_ranges->start_ip); } if (tmp_sec->usec_timeout <= 0) { @@ -765,4 +789,13 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char return NULL; } +/* stub function */ +nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl, useconds_t usec_timeout, nutscan_xml_t * sec) +{ + NUT_UNUSED_VARIABLE(irl); + NUT_UNUSED_VARIABLE(usec_timeout); + NUT_UNUSED_VARIABLE(sec); + return NULL; +} + #endif /* WITH_NEON */