This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/509-remove-cpputests in repository https://gitbox.apache.org/repos/asf/celix.git
commit d1f7d10abe3ce23006d1007d1a16100b8af1b8c2 Author: Pepijn Noltes <[email protected]> AuthorDate: Sat Dec 30 17:31:22 2023 +0100 Refactor ip utils and ip utils tests Also improves error handling --- libs/utils/gtest/CMakeLists.txt | 1 + .../gtest/src/IpUtilsErrorInjectionTestSuite.cc | 16 +- libs/utils/gtest/src/IpUtilsTestSuite.cc | 125 ++++++++++++++ libs/utils/include/celix_ip_utils.h | 108 ++++++++++++ libs/utils/include_deprecated/ip_utils.h | 52 ------ libs/utils/private/test/ip_utils_test.cpp | 108 ------------ libs/utils/src/ip_utils.c | 192 +++++++++++++-------- 7 files changed, 361 insertions(+), 241 deletions(-) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index b492009f..44a4aa42 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(test_utils src/ThreadsTestSuite.cc src/CelixErrnoTestSuite.cc src/CelixUtilsAutoCleanupTestSuite.cc + src/IpUtilsTestSuite.cc src/DeprecatedHashmapTestSuite.cc ) diff --git a/libs/utils/gtest/src/IpUtilsErrorInjectionTestSuite.cc b/libs/utils/gtest/src/IpUtilsErrorInjectionTestSuite.cc index 57a4c83c..93bd5626 100644 --- a/libs/utils/gtest/src/IpUtilsErrorInjectionTestSuite.cc +++ b/libs/utils/gtest/src/IpUtilsErrorInjectionTestSuite.cc @@ -17,10 +17,12 @@ under the License. */ -#include <errno.h> #include <gtest/gtest.h> + +#include <errno.h> + #include "ifaddrs_ei.h" -#include "ip_utils.h" +#include "celix_ip_utils.h" #include "celix_utils_ei.h" class IpUtilsWithErrorInjectionTestSuite : public ::testing::Test { @@ -28,20 +30,20 @@ public: IpUtilsWithErrorInjectionTestSuite() = default; ~IpUtilsWithErrorInjectionTestSuite() override { celix_ei_expect_getifaddrs(nullptr, 0, 0); - celix_ei_expect_celix_utils_strdup(nullptr, 0, 0); + celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr); } }; TEST_F(IpUtilsWithErrorInjectionTestSuite, failToGetInterfaceAddresses) { - celix_ei_expect_getifaddrs((void *)&ipUtils_findIpBySubnet, 0, -1); - auto ipAddresses = ipUtils_findIpBySubnet("192.168.1.0/24"); + celix_ei_expect_getifaddrs((void *)&celix_utils_findIpInSubnet, 0, -1); + auto ipAddresses = celix_utils_findIpInSubnet("192.168.1.0/24"); EXPECT_EQ(ipAddresses, nullptr); EXPECT_EQ(errno, EMFILE); } TEST_F(IpUtilsWithErrorInjectionTestSuite, failToDuplicateString) { - celix_ei_expect_celix_utils_strdup((void *) &ipUtils_findIpBySubnet, 0, nullptr); - auto ipAddresses = ipUtils_findIpBySubnet("192.168.1.0/24"); + celix_ei_expect_celix_utils_strdup((void *) &celix_utils_findIpInSubnet, 0, nullptr); + auto ipAddresses = celix_utils_findIpInSubnet("192.168.1.0/24"); EXPECT_EQ(ipAddresses, nullptr); EXPECT_EQ(errno, ENOMEM); } \ No newline at end of file diff --git a/libs/utils/gtest/src/IpUtilsTestSuite.cc b/libs/utils/gtest/src/IpUtilsTestSuite.cc new file mode 100644 index 00000000..5a2d1aa4 --- /dev/null +++ b/libs/utils/gtest/src/IpUtilsTestSuite.cc @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <gtest/gtest.h> + +#include "celix_err.h" +#include "celix_ip_utils.h" +#include "celix_stdlib_cleanup.h" + +class IpUtilsTestSuite : public ::testing::Test { +public: + IpUtilsTestSuite() { + celix_err_resetErrors(); + } + ~IpUtilsTestSuite() override { + celix_err_printErrors(stderr, nullptr, nullptr); + } +}; + +TEST_F(IpUtilsTestSuite, ipToUnsignedIntTest) { + const char *ip = "192.168.1.64"; + uint32_t expected = 3232235840; + uint32_t actual = celix_utils_convertIpToUint(ip, nullptr); + EXPECT_EQ(expected, actual); + + bool converted; + actual = celix_utils_convertIpToUint(ip, &converted); + EXPECT_TRUE(converted); + EXPECT_EQ(expected, actual); + + EXPECT_EQ(0, celix_utils_convertIpToUint("a.b.c.d", &converted)); + EXPECT_FALSE(converted); + EXPECT_EQ(1, celix_err_getErrorCount()); + + EXPECT_EQ(0, celix_utils_convertIpToUint("273.168.1.64", &converted)); + EXPECT_FALSE(converted); + EXPECT_EQ(2, celix_err_getErrorCount()); + + EXPECT_EQ(0, celix_utils_convertIpToUint("10.1.1.1.1", &converted)); + EXPECT_FALSE(converted); + EXPECT_EQ(3, celix_err_getErrorCount()); +} + +TEST_F(IpUtilsTestSuite, unsignedIntToIpTest) { + uint32_t ipAsUint = 3232235840; + const char *expected = "192.168.1.64"; + char* ip = celix_utils_convertUintToIp(ipAsUint); + EXPECT_STREQ(expected, ip); + free(ip); + + ip = celix_utils_convertUintToIp(0); + EXPECT_STREQ("0.0.0.0", ip); + free(ip); + + ip = celix_utils_convertUintToIp(UINT32_MAX); + EXPECT_STREQ("255.255.255.255", ip); + free(ip); +} + +TEST_F(IpUtilsTestSuite, prefixToBitmaskTest) { + uint32_t bitmask = celix_utils_ipPrefixLengthToBitmask(27); + EXPECT_EQ(4294967264, bitmask); + + bitmask = celix_utils_ipPrefixLengthToBitmask(0); + EXPECT_EQ(0, bitmask); + + bitmask = celix_utils_ipPrefixLengthToBitmask(32); + EXPECT_EQ(UINT32_MAX, bitmask); + + bitmask = celix_utils_ipPrefixLengthToBitmask(-1); + EXPECT_EQ(0, bitmask); + + bitmask = celix_utils_ipPrefixLengthToBitmask(33); + EXPECT_EQ(0, bitmask); +} + +TEST_F(IpUtilsTestSuite, NetmaskToPrefixTest) { + int prefix = celix_utils_ipNetmaskToPrefixLength("255.255.255.0"); + EXPECT_EQ(24, prefix); + + prefix = celix_utils_ipNetmaskToPrefixLength("0.0.0.0"); + EXPECT_EQ(0, prefix); + + prefix = celix_utils_ipNetmaskToPrefixLength("255.255.255.255"); + EXPECT_EQ(32, prefix); + + prefix = celix_utils_ipNetmaskToPrefixLength("255.0.0.0"); + EXPECT_EQ(8, prefix); + + EXPECT_EQ(-1, celix_utils_ipNetmaskToPrefixLength("a.b.c.d")); + EXPECT_EQ(1, celix_err_getErrorCount()); +} + +TEST_F(IpUtilsTestSuite, FindIpInSubnetWithInvalidInputTest) { + EXPECT_EQ(nullptr, celix_utils_findIpInSubnet("198.168.0.1")); // missing subnet + EXPECT_EQ(1, celix_err_getErrorCount()); + + EXPECT_EQ(nullptr, celix_utils_findIpInSubnet("198.168.0.1/abc")); // invalid subnet + EXPECT_EQ(2, celix_err_getErrorCount()); + + EXPECT_EQ(nullptr, celix_utils_findIpInSubnet("198.168.0.1/40")); // out of range subnet + EXPECT_EQ(3, celix_err_getErrorCount()); + + EXPECT_EQ(nullptr, celix_utils_findIpInSubnet("198.168.0.1/-1")); // out of range subnet + EXPECT_EQ(4, celix_err_getErrorCount()); + + EXPECT_EQ(nullptr, celix_utils_findIpInSubnet("a.b.c.d/8")); // invalid ip + EXPECT_EQ(5, celix_err_getErrorCount()); +} diff --git a/libs/utils/include/celix_ip_utils.h b/libs/utils/include/celix_ip_utils.h new file mode 100644 index 00000000..3e487476 --- /dev/null +++ b/libs/utils/include/celix_ip_utils.h @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @file celix_ip_utils.h + * @brief Utility functions for IP address manipulation. + * + * This header file contains declarations of utility functions for + * converting IP addresses and manipulating IP address data. + */ + +#ifndef CELIX_IP_UTILS_H +#define CELIX_IP_UTILS_H + +#include "celix_utils_export.h" + +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Converts an IP address in string format to an unsigned integer. + * + * This function takes an IP address as a string and converts it to + * its corresponding unsigned integer representation. + * + * @param[in] ip The IP address in string format (e.g., "192.168.0.1"). + * @param[out] converted A boolean indicating whether the conversion was successful. Can be NULL. + * @return The IP address as an uint32_t. Returns 0 if the conversion fails. + */ +uint32_t celix_utils_convertIpToUint(const char* ip, bool* converted); + +/** + * @brief Converts an unsigned integer to its corresponding IP address in string format. + * + * This function converts an unsigned integer representing an IP address + * to a string format. + * + * @param[in] ip The IP address as an uint32_t. + * @return The IP address in string format (e.g., "192.168.0.1"). + */ +char* celix_utils_convertUintToIp(uint32_t ip); + +/** + * @brief Converts a subnet prefix length to a bitmask. + * + * This function takes a subnet prefix length and converts it into + * a bitmask in unsigned integer format. + * + * @param[in] prefix The subnet prefix length. + * @return The corresponding bitmask as an unsigned integer. Returns 0 if the prefix is invalid or 0. + */ +uint32_t celix_utils_ipPrefixLengthToBitmask(int prefix); + +/** + * @brief Converts a netmask string to a prefix length. + * + * This function converts a netmask in string format (e.g., "255.255.255.0") + * to its corresponding prefix length. + * + * @param[in] netmask The netmask in string format. + * @return The prefix length or -1 if the conversion fails. + */ +int celix_utils_ipNetmaskToPrefixLength(const char* netmask); + +/** + * @brief Finds an IP address within a given subnet on the host's network interfaces. + * + * This function searches through the network interfaces of the host to find + * an IP address that falls within the specified subnet. It analyzes the IP addresses + * assigned to each network interface and checks if any of them match the given subnet criteria. + * The function returns the first IP address that is within the specified subnet range. + * + * The input parameter is expected to be in CIDR notation. + * CIDR notation is a concise format for specifying IP addresses ranges using IP address and subnet prefix length. + * It takes the form of 'IP_ADDRESS/PREFIX_LENGTH' (e.g., "192.168.0.1/24"). + * + * @param[in] subnetCidrNotation The IP address with subnet prefix. + * @return A string containing an IP address within the specified subnet that is also + * assigned to a network interface on the host, or NULL if no matching IP address is found. The caller is owner of the + * returned string. + */ +char* celix_utils_findIpInSubnet(const char* subnetCidrNotation); + +#ifdef __cplusplus +} +#endif + +#endif /* CELIX_IP_UTILS_H */ diff --git a/libs/utils/include_deprecated/ip_utils.h b/libs/utils/include_deprecated/ip_utils.h deleted file mode 100644 index 75adcb59..00000000 --- a/libs/utils/include_deprecated/ip_utils.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/** - * ip_utils.h - * - * \date Jun 24, 2019 - * \author <a href="mailto:[email protected]">Apache Celix Project Team</a> - * \copyright Apache License, Version 2.0 - */ - -#ifndef IP_UTILS_H_ -#define IP_UTILS_H_ - -#include <ctype.h> - -#include "celix_errno.h" -#include "celix_utils_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -CELIX_UTILS_DEPRECATED_EXPORT unsigned int ipUtils_ipToUnsignedInt(char *ip); - -CELIX_UTILS_DEPRECATED_EXPORT char *ipUtils_unsignedIntToIp(unsigned int ip); - -CELIX_UTILS_DEPRECATED_EXPORT unsigned int ipUtils_prefixToBitmask(unsigned int prefix); - -CELIX_UTILS_DEPRECATED_EXPORT int ipUtils_netmaskToPrefix(const char *netmask); - -CELIX_UTILS_DEPRECATED_EXPORT char *ipUtils_findIpBySubnet(const char *ipWithPrefix); - -#ifdef __cplusplus -} -#endif -#endif /* IP_UTILS_H_ */ diff --git a/libs/utils/private/test/ip_utils_test.cpp b/libs/utils/private/test/ip_utils_test.cpp deleted file mode 100644 index 7b576b3a..00000000 --- a/libs/utils/private/test/ip_utils_test.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/** - * ip_utils_test.cpp - * - * \date Jun 24, 2019 - * \author <a href="mailto:[email protected]">Apache Celix Project Team</a> - * \copyright Apache License, Version 2.0 - */ - -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <string.h> - -#include "CppUTest/TestHarness.h" -#include "CppUTest/TestHarness_c.h" -#include "CppUTest/CommandLineTestRunner.h" - -extern "C" -{ -#include "ip_utils.h" -} - -int main(int argc, char** argv) { - MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); - return RUN_ALL_TESTS(argc, argv); -} - -static char* my_strdup(const char* s){ - if(s==NULL){ - return NULL; - } - - size_t len = strlen(s); - - char *d = (char*) calloc (len + 1,sizeof(char)); - - if (d == NULL){ - return NULL; - } - - strncpy (d,s,len); - return d; -} - -TEST_GROUP(ip_utils) { - void setup(void) { - } - - void teardown() { - } -}; - -TEST(ip_utils, ipToUnsignedInt){ - char *ip = my_strdup("192.168.1.64"); - - unsigned int expected = 3232235840; - unsigned int actual = ipUtils_ipToUnsignedInt(ip); - UNSIGNED_LONGS_EQUAL(expected, actual); - - free(ip); -} - -TEST(ip_utils, unsignedIntToIp){ - unsigned int ipAsUint = 3232235840; - - const char *expected = "192.168.1.64"; - char *actual = ipUtils_unsignedIntToIp(ipAsUint); - STRCMP_EQUAL(expected, actual); - free(actual); -} - -TEST(ip_utils, prefixToBitmask){ - unsigned int expected = 4294967264; - unsigned int actual = ipUtils_prefixToBitmask(27); - - UNSIGNED_LONGS_EQUAL(expected, actual); -} - -TEST(ip_utils, netmaskToPrefix){ - char *netmask = my_strdup("255.255.255.0"); - - int expected = 24; - int actual = ipUtils_netmaskToPrefix(netmask); - LONGS_EQUAL(expected, actual); - - free(netmask); - - actual = ipUtils_netmaskToPrefix("a.b.c.d"); - LONGS_EQUAL(-1, actual); -} diff --git a/libs/utils/src/ip_utils.c b/libs/utils/src/ip_utils.c index d168fa36..833bd203 100644 --- a/libs/utils/src/ip_utils.c +++ b/libs/utils/src/ip_utils.c @@ -16,110 +16,151 @@ * specific language governing permissions and limitations * under the License. */ -/** - * ip_utils.c - * - * \date Jun 24, 2019 - * \author <a href="mailto:[email protected]">Apache Celix Project Team</a> - * \copyright Apache License, Version 2.0 - */ -#include "ip_utils.h" +#include "celix_ip_utils.h" + +#include "celix_err.h" +#include "celix_stdlib_cleanup.h" #include "celix_utils.h" +#include "celix_convert_utils.h" +#include <arpa/inet.h> #include <errno.h> +#include <ifaddrs.h> +#include <limits.h> +#include <math.h> +#include <netdb.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> -#include <stdbool.h> #include <string.h> -#include <math.h> - -#include <arpa/inet.h> #include <sys/socket.h> -#include <netdb.h> -#include <ifaddrs.h> -#include <unistd.h> -unsigned int ipUtils_ipToUnsignedInt(char *ip) { - unsigned int ipAsUint = 0; +uint32_t celix_utils_convertIpToUint(const char* ip, bool* converted) { + if (converted) { + *converted = false; + } + + // copy for strtok_r + celix_autofree char* input = strdup(ip); + if (!input) { + celix_err_push("Failed to duplicate input string for IP conversion"); + return 0; + } - char *partOfIp = NULL, *savePtr = NULL; - char *input = strdup(ip); // Make a copy because otherwise strtok_r manipulates the input string - partOfIp = strtok_r(input, ".\0", &savePtr); ipAsUint += strtoul(partOfIp, NULL, 10) * (unsigned int) pow(256, 3); - partOfIp = strtok_r(NULL, ".\0", &savePtr); ipAsUint += strtoul(partOfIp, NULL, 10) * (unsigned int) pow(256, 2); - partOfIp = strtok_r(NULL, ".\0", &savePtr); ipAsUint += strtoul(partOfIp, NULL, 10) * (unsigned int) pow(256, 1); - partOfIp = strtok_r(NULL, ".\0", &savePtr); ipAsUint += strtoul(partOfIp, NULL, 10) * (unsigned int) pow(256, 0); - free(input); + uint32_t ipAsUint = 0; + char* partOfIp = NULL; + char* savePtr = NULL; + int count = 0; + while ((partOfIp = strtok_r(partOfIp == NULL ? input : NULL, ".\0", &savePtr)) != NULL) { + if (count > 3) { + celix_err_pushf("Failed to convert IP address %s to unsigned int, to many parts", ip); + return 0; + } + bool longConverted = false; + long partAsLong = celix_utils_convertStringToLong(partOfIp, ULONG_MAX, &longConverted); + if (!longConverted) { + celix_err_pushf("Failed to convert IP address %s to unsigned int, part `%s` is not a number", ip, partOfIp); + return 0; + } else if (partAsLong > 255 || partAsLong < 0) { + celix_err_pushf("Failed to convert IP address %s to unsigned int, part `%s` is out of range", ip, partOfIp); + return 0; + } + ipAsUint += (uint32_t)(partAsLong * powl(256, 3 - count++)); + } + if (converted) { + *converted = true; + } return ipAsUint; } -char *ipUtils_unsignedIntToIp(unsigned int ip) { - char *ipStr = calloc(16, sizeof(char)); +char* celix_utils_convertUintToIp(uint32_t ip) { + char* ipStr = calloc(16, sizeof(char)); + if (!ipStr) { + celix_err_push("Failed to allocate memory for IP address string"); + return NULL; + } - int ipPart1 = ip / (int) pow(256, 3); ip -= ipPart1 * (int) pow(256, 3); - int ipPart2 = ip / (int) pow(256, 2); ip -= ipPart2 * (int) pow(256, 2); - int ipPart3 = ip / (int) pow(256, 1); ip -= ipPart3 * (int) pow(256, 1); - int ipPart4 = ip / (int) pow(256, 0); + int64_t ipPart1 = ip / (int64_t)pow(256, 3); + ip -= ipPart1 * (int64_t)pow(256, 3); + int64_t ipPart2 = ip / (int64_t)pow(256, 2); + ip -= ipPart2 * (int64_t)pow(256, 2); + int64_t ipPart3 = ip / (int64_t)pow(256, 1); + ip -= ipPart3 * (int64_t)pow(256, 1); + int64_t ipPart4 = ip / (int64_t)pow(256, 0); - snprintf(ipStr, 16, "%d.%d.%d.%d", ipPart1, ipPart2, ipPart3, ipPart4); + snprintf(ipStr, 16, "%li.%li.%li.%li", ipPart1, ipPart2, ipPart3, ipPart4); return ipStr; } -unsigned int ipUtils_prefixToBitmask(unsigned int prefix) { - return (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF; +uint32_t celix_utils_ipPrefixLengthToBitmask(int prefix) { + if (prefix > 32 || prefix <= 0) { + return 0; + } + return (uint32_t )((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF); } -int ipUtils_netmaskToPrefix(const char *netmask) { +int celix_utils_ipNetmaskToPrefixLength(const char* netmask) { // Convert netmask to in_addr object struct in_addr in; int ret = inet_pton(AF_INET, netmask, &in); if (ret != 1) { + celix_err_pushf("Failed to convert netmask %s to in_addr object", netmask); return -1; } // Now convert the mask to a prefix int prefix = 0; - bool processed_one = false; - unsigned int i = ntohl(in.s_addr); + uint32_t i = ntohl(in.s_addr); while (i > 0) { if (i & 1) { prefix++; - processed_one = true; - } else { - if (processed_one) return -1; } - i >>= 1; } return prefix; } -/** Finds an IP of the available network interfaces of the machine by specifying an CIDR subnet. - * - * @param ipWithPrefix IP with prefix, e.g. 192.168.1.0/24 - * @return ip In case a matching interface could be found, an allocated string containing the IP of the - * interface will be returned, e.g. 192.168.1.16. Memory for the new string can be freed with free(). - * When no matching interface is found NULL will be returned. - */ -char *ipUtils_findIpBySubnet(const char *ipWithPrefix) { - char *ip = NULL; +char* celix_utils_findIpInSubnet(const char* subnetCidrNotation) { + char* ip = NULL; - char *input = celix_utils_strdup(ipWithPrefix); // Make a copy as otherwise strtok_r manipulates the input string - if (input == NULL) { - goto strdup_failed; + //create copy for strtok_r + celix_autofree char* input = celix_utils_strdup(subnetCidrNotation); + if (!input) { + celix_err_push("Failed to duplicate input string for subnet search"); + return NULL; } - char *savePtr; - char *inputIp = strtok_r(input, "/", &savePtr); - char *inputPrefixStr = strtok_r(NULL, "\0", &savePtr); - unsigned int inputPrefix = (unsigned int) strtoul(inputPrefixStr, NULL, 10); + char* savePtr; + char* inputIp = strtok_r(input, "/", &savePtr); + char* inputPrefixStr = strtok_r(NULL, "\0", &savePtr); - unsigned int ipAsUint = ipUtils_ipToUnsignedInt(inputIp); - unsigned int bitmask = ipUtils_prefixToBitmask(inputPrefix); + if (!inputPrefixStr) { + celix_err_pushf("Failed to parse IP address with prefix %s. Missing a '/'", subnetCidrNotation); + return NULL; + } + + bool convertedLong = false; + int inputPrefix = (int)celix_utils_convertStringToLong(inputPrefixStr, INT_MAX, &convertedLong); + if (!convertedLong) { + celix_err_pushf("Failed to parse prefix in IP address with prefix %s", subnetCidrNotation); + return NULL; + } else if (inputPrefix > 32 || inputPrefix < 0) { + celix_err_pushf( + "Failed to parse IP address with prefix %s. Prefix %s is out of range", subnetCidrNotation, inputPrefixStr); + return NULL; + } + + bool converted; + uint32_t ipAsUint = celix_utils_convertIpToUint(inputIp, &converted); + if (!converted) { + return NULL; + } + uint32_t bitmask = celix_utils_ipPrefixLengthToBitmask(inputPrefix); unsigned int ipRangeStart = ipAsUint & bitmask; unsigned int ipRangeStop = ipAsUint | ~bitmask; @@ -127,44 +168,47 @@ char *ipUtils_findIpBySubnet(const char *ipWithPrefix) { // Requested IP range is known now, now loop through network interfaces struct ifaddrs *ifap, *ifa; - if(getifaddrs (&ifap) == -1) { - goto getifaddrs_failed; + if (getifaddrs(&ifap) == -1) { + celix_err_push("Failed to get network interfaces"); + return NULL; } + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL) + if (ifa->ifa_addr == NULL) { continue; + } - if (ifa->ifa_addr->sa_family != AF_INET) + if (ifa->ifa_addr->sa_family != AF_INET) { continue; + } // Retrieve IP address for interface char if_addr[NI_MAXHOST]; - int rv = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), - if_addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + int rv = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), if_addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (rv != 0) { - printf("getnameinfo() failed: %s\n", gai_strerror(rv)); + celix_err_pushf("Failed to get IP address for interface %s: %s", ifa->ifa_name, gai_strerror(rv)); continue; } // Retrieve netmask - struct sockaddr_in *sa = (struct sockaddr_in *) ifa->ifa_netmask; - char *if_netmask = inet_ntoa(sa->sin_addr); + struct sockaddr_in* sa = (struct sockaddr_in*)ifa->ifa_netmask; + char* if_netmask = inet_ntoa(sa->sin_addr); - unsigned int ifIpAsUint = ipUtils_ipToUnsignedInt(if_addr); - int ifPrefix = ipUtils_netmaskToPrefix(if_netmask); + uint32_t ifIpAsUint = celix_utils_convertIpToUint(if_addr, NULL); + int ifPrefix = celix_utils_ipNetmaskToPrefixLength(if_netmask); if (ifPrefix == -1) { break; } if (ifIpAsUint >= ipRangeStart && ifIpAsUint <= ipRangeStop && inputPrefix >= ifPrefix) { - ip = strndup(if_addr, 1024); + ip = celix_utils_strdup(if_addr); + if (!ip) { + celix_err_push("Failed to duplicate IP address"); + break; + } break; } } - freeifaddrs(ifap); -getifaddrs_failed: - free(input); -strdup_failed: return ip; }
