diff --git a/components/sock_utils/CMakeLists.txt b/components/sock_utils/CMakeLists.txt index d6020f3cc05..4f4e1898350 100644 --- a/components/sock_utils/CMakeLists.txt +++ b/components/sock_utils/CMakeLists.txt @@ -1,4 +1,6 @@ idf_component_register(SRCS "src/getnameinfo.c" "src/ifaddrs.c" + "src/gai_strerror.c" + "src/socketpair.c" INCLUDE_DIRS "include" PRIV_REQUIRES lwip esp_netif) diff --git a/components/sock_utils/include/gai_strerror.h b/components/sock_utils/include/gai_strerror.h new file mode 100644 index 00000000000..2180ba1b308 --- /dev/null +++ b/components/sock_utils/include/gai_strerror.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "lwip/sockets.h" +#include "netdb_macros.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_IDF_TARGET_LINUX +// namespace with esp_ on linux to avoid duplication of symbols +#define gai_strerror esp_gai_strerror +#endif + +/** +* @brief Returns a string describing a `getaddrinfo()` error code. +* +* @param[in] ecode Error code returned by `getaddrinfo()`. +* +* @return A pointer to a string describing the error. +*/ +const char *gai_strerror(int ecode); + +#ifdef __cplusplus +} +#endif diff --git a/components/sock_utils/include/getnameinfo.h b/components/sock_utils/include/getnameinfo.h new file mode 100644 index 00000000000..2749dc91cdf --- /dev/null +++ b/components/sock_utils/include/getnameinfo.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "lwip/sockets.h" + +#ifdef CONFIG_IDF_TARGET_LINUX +// namespace with esp_ on linux to avoid conflict of symbols +#define getnameinfo esp_getnameinfo +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @brief Converts a socket address to a corresponding host and service name. +* +* @param[in] addr Pointer to the socket address structure. +* @param[in] addrlen Length of the socket address. +* @param[out] host Buffer to store the host name. +* @param[in] hostlen Length of the host buffer. +* @param[out] serv Buffer to store the service name. +* @param[in] servlen Length of the service buffer. +* @param[in] flags Flags to modify the behavior of the function. +* +* @return +* - 0 on success. +* - Non-zero on failure, with `errno` set to indicate the error. +*/ +int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, + char *host, socklen_t hostlen, + char *serv, socklen_t servlen, int flags); + +#ifdef __cplusplus +} +#endif diff --git a/components/sock_utils/include/ifaddrs.h b/components/sock_utils/include/ifaddrs.h index a933760a34c..74d13ee1869 100644 --- a/components/sock_utils/include/ifaddrs.h +++ b/components/sock_utils/include/ifaddrs.h @@ -6,39 +6,25 @@ #pragma once #include "lwip/sockets.h" - -#ifndef NI_NUMERICHOST -#define NI_NUMERICHOST 0x1 -#endif - -#ifndef IFF_UP -#define IFF_UP 0x1 -#endif - -#ifndef IFF_LOOPBACK -#define IFF_LOOPBACK 0x8 -#endif - -#ifndef NI_NUMERICSERV -#define NI_NUMERICSERV 0x8 -#endif - -#ifndef NI_DGRAM -#define NI_DGRAM 0x00000010 -#endif - -#ifndef EAI_BADFLAGS -#define EAI_BADFLAGS 3 -#endif - -#ifndef AF_UNIX -#define AF_UNIX 1 -#endif +#include "netdb_macros.h" +// include also other related headers to simplify porting of linux libs +#include "getnameinfo.h" +#include "socketpair.h" +#include "gai_strerror.h" #ifdef __cplusplus extern "C" { #endif +#ifdef CONFIG_IDF_TARGET_LINUX +// namespace with esp_ on linux to avoid duplication of symbols +#define getifaddrs esp_getifaddrs +#define freeifaddrs esp_freeifaddrs +#endif + +/** + * @brief Simplified version of ifaddr struct + */ struct ifaddrs { struct ifaddrs *ifa_next; /* Next item in list */ char *ifa_name; /* Name of interface */ @@ -46,20 +32,25 @@ struct ifaddrs { int ifa_flags; }; +/** + * @brief Retrieves a list of network interfaces and their addresses. + * + * @param[out] ifap Pointer to a linked list of `struct ifaddrs`. On success, `*ifap` will be set + * to the head of the list. + * + * @return + * - 0 on success. + * - -1 on failure, with `errno` set to indicate the error. + */ int getifaddrs(struct ifaddrs **ifap); +/** + * @brief Frees the memory allocated by `getifaddrs()`. + * + * @param[in] ifa Pointer to the linked list of network interfaces to be freed. + */ void freeifaddrs(struct ifaddrs *ifa); -#ifdef CONFIG_IDF_TARGET_LINUX -#define getnameinfo lwip_getnameinfo -#endif - -int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, - char *host, socklen_t hostlen, - char *serv, socklen_t servlen, int flags); - -int socketpair(int domain, int type, int protocol, int sv[2]); - #ifdef __cplusplus } #endif diff --git a/components/sock_utils/include/netdb_macros.h b/components/sock_utils/include/netdb_macros.h new file mode 100644 index 00000000000..3ec32b3df26 --- /dev/null +++ b/components/sock_utils/include/netdb_macros.h @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifndef NI_NUMERICHOST +#define NI_NUMERICHOST 0x1 +#endif + +#ifndef IFF_UP +#define IFF_UP 0x1 +#endif + +#ifndef IFF_LOOPBACK +#define IFF_LOOPBACK 0x8 +#endif + +#ifndef NI_NUMERICSERV +#define NI_NUMERICSERV 0x8 +#endif + +#ifndef NI_DGRAM +#define NI_DGRAM 0x00000010 +#endif + +#ifndef EAI_BADFLAGS +#define EAI_BADFLAGS 3 +#endif + +#ifndef AF_UNIX +#define AF_UNIX 1 +#endif diff --git a/components/sock_utils/include/socketpair.h b/components/sock_utils/include/socketpair.h new file mode 100644 index 00000000000..996a18ed6fa --- /dev/null +++ b/components/sock_utils/include/socketpair.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "lwip/sockets.h" +#include "netdb_macros.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_IDF_TARGET_LINUX +// namespace with esp_ on linux to avoid conflict of symbols +#define socketpair esp_socketpair +#define pipe esp_pipe +#endif + +/** +* @brief Creates a pair of connected sockets. +* +* @param[in] domain Communication domain (e.g., AF_UNIX). +* @param[in] type Socket type (e.g., SOCK_STREAM). +* @param[in] protocol Protocol to be used (usually 0). +* @param[out] sv Array of two integers to store the file descriptors of the created sockets. +* +* @return +* - 0 on success. +* - -1 on failure, with `errno` set to indicate the error. +*/ +int socketpair(int domain, int type, int protocol, int sv[2]); + +/** + * @brief Creates a unidirectional data channel (pipe). + * + * @param[out] pipefd Array of two integers where the file descriptors for the read and write ends + * of the pipe will be stored. + * + * @return + * - 0 on success. + * - -1 on failure, with `errno` set to indicate the error. + */ +int pipe(int pipefd[2]); + +#ifdef __cplusplus +} +#endif diff --git a/components/sock_utils/src/gai_strerror.c b/components/sock_utils/src/gai_strerror.c new file mode 100644 index 00000000000..9e8e32d0bff --- /dev/null +++ b/components/sock_utils/src/gai_strerror.c @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "gai_strerror.h" + +const char *gai_strerror(int ecode) +{ + static char str[32]; + if (snprintf(str, sizeof(str), "EAI error:%d", ecode) < 0) { + return "gai_strerror() failed"; + } + return str; +} diff --git a/components/sock_utils/src/getnameinfo.c b/components/sock_utils/src/getnameinfo.c index 3d76f73d104..a0d883746a3 100644 --- a/components/sock_utils/src/getnameinfo.c +++ b/components/sock_utils/src/getnameinfo.c @@ -46,82 +46,3 @@ int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, return 0; } - -#define INVALID_SOCKET (-1) - -static const char *TAG = "socket_helpers"; - -static int set_nonblocking(int sock) -{ - int opt; - opt = fcntl(sock, F_GETFL, 0); - if (opt == -1) { - return -1; - } - if (fcntl(sock, F_SETFL, opt | O_NONBLOCK) == -1) { - return -1; - } - return 0; -} - -int socketpair(int domain, int type, int protocol, int sv[2]) -{ - struct sockaddr_storage ss; - struct sockaddr_in *sa = (struct sockaddr_in *)&ss; - socklen_t ss_len = sizeof(struct sockaddr_in); - int fd1 = INVALID_SOCKET; - int fd2 = INVALID_SOCKET; - int listenfd = INVALID_SOCKET; - int ret = 0; // Success - - sa->sin_family = AF_INET; - sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - sa->sin_port = 0; - listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - ESP_GOTO_ON_FALSE(listenfd != INVALID_SOCKET, -1, err, TAG, "Cannot create listening socket"); - ESP_GOTO_ON_FALSE(listen(listenfd, 1) == 0, -1, err, TAG, "Failed to listen"); - - memset(&ss, 0, sizeof(ss)); - ss_len = sizeof(ss); - ESP_GOTO_ON_FALSE(getsockname(listenfd, (struct sockaddr *)&ss, &ss_len) >= 0, -1, err, TAG, "getsockname failed"); - - sa->sin_family = AF_INET; - sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - fd1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - ESP_GOTO_ON_FALSE(fd1 != INVALID_SOCKET, -1, err, TAG, "Cannot create read side socket"); - ESP_GOTO_ON_FALSE(set_nonblocking(fd1) == 0, -1, err, TAG, "Failed to set socket to nonblocking mode"); - if (connect(fd1, (struct sockaddr *)&ss, ss_len) < 0) { - ESP_GOTO_ON_FALSE(errno == EINPROGRESS || errno == EWOULDBLOCK, -1, err, TAG, "Failed to connect fd1"); - } - fd2 = accept(listenfd, NULL, 0); - if (fd2 == -1) { - ESP_GOTO_ON_FALSE(errno == EINPROGRESS || errno == EWOULDBLOCK, -1, err, TAG, "Failed to accept fd2"); - } - ESP_GOTO_ON_FALSE(set_nonblocking(fd2) == 0, -1, err, TAG, "Failed to set socket to nonblocking mode"); - - close(listenfd); - sv[0] = fd1; - sv[1] = fd2; - return ret; - -err: - if (listenfd != INVALID_SOCKET) { - close(listenfd); - } - if (fd1 != INVALID_SOCKET) { - close(fd1); - } - if (fd2 != INVALID_SOCKET) { - close(fd2); - } - return ret; -} - -int pipe(int pipefd[2]) -{ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd) == -1) { - return -1; - } - return 0; -} diff --git a/components/sock_utils/src/ifaddrs.c b/components/sock_utils/src/ifaddrs.c index 5036e0696f8..01c766f9784 100644 --- a/components/sock_utils/src/ifaddrs.c +++ b/components/sock_utils/src/ifaddrs.c @@ -6,62 +6,64 @@ #include #include #include "esp_netif.h" +#include "esp_check.h" #include #include "ifaddrs.h" -int getifaddrs(struct ifaddrs **ifap) -{ - if (ifap == NULL) { - return -1; // Invalid argument - } +static const char *TAG = "sockutls_getifaddr"; - // Allocate memory for a single ifaddrs structure - struct ifaddrs *ifaddr = (struct ifaddrs *)calloc(1, sizeof(struct ifaddrs)); - if (ifaddr == NULL) { - return -1; // Memory allocation failure - } - - // Allocate memory for the interface name - ifaddr->ifa_name = strdup("sta"); // Replace with actual interface name if known - if (ifaddr->ifa_name == NULL) { - free(ifaddr); - return -1; - } - - // Allocate memory for the sockaddr structure - struct sockaddr_in *addr_in = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in)); - if (addr_in == NULL) { - free(ifaddr->ifa_name); - free(ifaddr); - return -1; - } - // Retrieve IP information from the ESP netif +static esp_err_t getifaddrs_unsafe(void *ctx) +{ + struct ifaddrs **ifap = ctx; + struct ifaddrs *ifaddr = NULL; + struct ifaddrs **next_addr = NULL; + struct sockaddr_in *addr_in = NULL; + esp_netif_t *netif = NULL; esp_netif_ip_info_t ip; - esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); - if (netif == NULL) { - free(addr_in); - free(ifaddr->ifa_name); - free(ifaddr); - return -1; + int ret = ESP_OK; + + while ((netif = esp_netif_next_unsafe(netif)) != NULL) { + ESP_GOTO_ON_FALSE((ifaddr = (struct ifaddrs *)calloc(1, sizeof(struct ifaddrs))), + ESP_ERR_NO_MEM, err, TAG, "Failed to allocate ifaddr"); + if (next_addr == NULL) { // the first address -> attach the head + *ifap = ifaddr; + } else { + *next_addr = ifaddr; // attach next addr + } + ESP_GOTO_ON_FALSE((ifaddr->ifa_name = strdup(esp_netif_get_ifkey(netif))), + ESP_ERR_NO_MEM, err, TAG, "Failed to allocate if name"); + ESP_GOTO_ON_FALSE((addr_in = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in))), + ESP_ERR_NO_MEM, err, TAG, "Failed to allocate addr_in"); + ESP_GOTO_ON_ERROR(esp_netif_get_ip_info(netif, &ip), err, TAG, "Failed to allocate if name"); + ESP_LOGD(TAG, "IPv4 address: " IPSTR, IP2STR(&ip.ip)); + addr_in->sin_family = AF_INET; + addr_in->sin_addr.s_addr = ip.ip.addr; + ifaddr->ifa_addr = (struct sockaddr *)addr_in; + ifaddr->ifa_flags = IFF_UP; // Mark the interface as UP, add more flags as needed + next_addr = &ifaddr->ifa_next; } - if (esp_netif_get_ip_info(netif, &ip) != ESP_OK) { - free(addr_in); - free(ifaddr->ifa_name); - free(ifaddr); - return -1; + if (next_addr == NULL) { + *ifap = NULL; // no addresses found + } else { + *next_addr = NULL; // terminate the list } + return ret; - // Set up the sockaddr_in structure - addr_in->sin_family = AF_INET; - addr_in->sin_addr.s_addr = ip.ip.addr; +err: + freeifaddrs(ifaddr); + *ifap = NULL; + return ret; + +} - // Link the sockaddr to ifaddrs - ifaddr->ifa_addr = (struct sockaddr *)addr_in; - ifaddr->ifa_flags = IFF_UP; // Mark the interface as UP, add more flags as needed +int getifaddrs(struct ifaddrs **ifap) +{ + if (ifap == NULL) { + return -1; // Invalid argument + } - *ifap = ifaddr; // Return the linked list - return 0; // Success + return esp_netif_tcpip_exec(getifaddrs_unsafe, ifap) == ESP_OK ? 0 : -1; } void freeifaddrs(struct ifaddrs *ifa) diff --git a/components/sock_utils/src/socketpair.c b/components/sock_utils/src/socketpair.c new file mode 100644 index 00000000000..f9bb91726f3 --- /dev/null +++ b/components/sock_utils/src/socketpair.c @@ -0,0 +1,88 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "lwip/sockets.h" +#include "socketpair.h" +#include "esp_check.h" + +#define INVALID_SOCKET (-1) + +static const char *TAG = "socket_helpers"; + +static int set_nonblocking(int sock) +{ + int opt; + opt = fcntl(sock, F_GETFL, 0); + if (opt == -1) { + return -1; + } + if (fcntl(sock, F_SETFL, opt | O_NONBLOCK) == -1) { + return -1; + } + return 0; +} + +int socketpair(int domain, int type, int protocol, int sv[2]) +{ + struct sockaddr_storage ss; + struct sockaddr_in *sa = (struct sockaddr_in *)&ss; + socklen_t ss_len = sizeof(struct sockaddr_in); + int fd1 = INVALID_SOCKET; + int fd2 = INVALID_SOCKET; + int listenfd = INVALID_SOCKET; + int ret = 0; // Success + + sa->sin_family = AF_INET; + sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sa->sin_port = 0; + listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ESP_GOTO_ON_FALSE(listenfd != INVALID_SOCKET, -1, err, TAG, "Cannot create listening socket"); + ESP_GOTO_ON_FALSE(listen(listenfd, 1) == 0, -1, err, TAG, "Failed to listen"); + + memset(&ss, 0, sizeof(ss)); + ss_len = sizeof(ss); + ESP_GOTO_ON_FALSE(getsockname(listenfd, (struct sockaddr *)&ss, &ss_len) >= 0, -1, err, TAG, "getsockname failed"); + + sa->sin_family = AF_INET; + sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + fd1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ESP_GOTO_ON_FALSE(fd1 != INVALID_SOCKET, -1, err, TAG, "Cannot create read side socket"); + ESP_GOTO_ON_FALSE(set_nonblocking(fd1) == 0, -1, err, TAG, "Failed to set socket to nonblocking mode"); + if (connect(fd1, (struct sockaddr *)&ss, ss_len) < 0) { + ESP_GOTO_ON_FALSE(errno == EINPROGRESS || errno == EWOULDBLOCK, -1, err, TAG, "Failed to connect fd1"); + } + fd2 = accept(listenfd, NULL, 0); + if (fd2 == -1) { + ESP_GOTO_ON_FALSE(errno == EINPROGRESS || errno == EWOULDBLOCK, -1, err, TAG, "Failed to accept fd2"); + } + ESP_GOTO_ON_FALSE(set_nonblocking(fd2) == 0, -1, err, TAG, "Failed to set socket to nonblocking mode"); + + close(listenfd); + sv[0] = fd1; + sv[1] = fd2; + return ret; + +err: + if (listenfd != INVALID_SOCKET) { + close(listenfd); + } + if (fd1 != INVALID_SOCKET) { + close(fd1); + } + if (fd2 != INVALID_SOCKET) { + close(fd2); + } + return ret; +} + +int pipe(int pipefd[2]) +{ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd) == -1) { + return -1; + } + return 0; +} diff --git a/components/sock_utils/test/host/CMakeLists.txt b/components/sock_utils/test/host/CMakeLists.txt index 2609149504a..e6ec6b7fb20 100644 --- a/components/sock_utils/test/host/CMakeLists.txt +++ b/components/sock_utils/test/host/CMakeLists.txt @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.16) set(COMPONENTS main) -#list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/") - include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(sockutls_host_test) diff --git a/components/sock_utils/test/host/main/test_sock_utils.cpp b/components/sock_utils/test/host/main/test_sock_utils.cpp index b64f3e1569f..934f4a56db1 100644 --- a/components/sock_utils/test/host/main/test_sock_utils.cpp +++ b/components/sock_utils/test/host/main/test_sock_utils.cpp @@ -6,79 +6,134 @@ #include "ifaddrs.h" #include "esp_netif.h" #include "esp_event.h" -#define CATCH_CONFIG_MAIN #include "catch2/catch_test_macros.hpp" #include "catch2/catch_session.hpp" -#include static char buffer[64]; -TEST_CASE("getnameinfo() for IPv4", "[sock_utils]") +static esp_netif_t *create_test_netif(const char *if_key) +{ + esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); + esp_netif_ip_info_t ip = { }; + ip.ip.addr = ESP_IP4TOADDR(1, 2, 3, 4); + base_cfg.ip_info = &ip; + base_cfg.if_key = if_key; + esp_netif_config_t cfg = { .base = &base_cfg, .driver = NULL, .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA }; + return esp_netif_new(&cfg); +} + +TEST_CASE("esp_getnameinfo() for IPv4", "[sock_utils]") { struct sockaddr_in sock_addr = {}; sock_addr.sin_family = AF_INET; sock_addr.sin_port = 257; // (0x0101: same number for LE and BE) - REQUIRE(getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) == 0); + REQUIRE(esp_getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) == 0); CHECK(strcmp("0.0.0.0", buffer) == 0); - CHECK(getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), NULL, 0, buffer, sizeof(buffer), NI_NUMERICSERV) == 0); + CHECK(esp_getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), NULL, 0, buffer, sizeof(buffer), NI_NUMERICSERV) == 0); CHECK(strcmp("257", buffer) == 0); } -TEST_CASE("getnameinfo() for IPv6", "[sock_utils]") +TEST_CASE("esp_getnameinfo() for IPv6", "[sock_utils]") { struct sockaddr_in sock_addr = {}; sock_addr.sin_family = AF_INET6; // IPv6 not supported for now - CHECK(getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) != 0); + CHECK(esp_getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) != 0); } -TEST_CASE("getifaddr()", "[sock_utils]") +static void test_getifaddr(int expected_nr_of_addrs) { - struct ifaddrs *addresses, *addr; - REQUIRE(getifaddrs(&addresses) != -1); - addr = addresses; - CHECK(addr != NULL); int nr_of_addrs = 0; + CHECK(esp_getifaddrs(&addresses) != -1); + addr = addresses; + while (addr) { ++nr_of_addrs; if (addr->ifa_addr && addr->ifa_addr->sa_family == AF_INET) { // look for IP4 addresses struct sockaddr_in *sock_addr = (struct sockaddr_in *) addr->ifa_addr; - if (getnameinfo((struct sockaddr *)sock_addr, sizeof(*sock_addr), - buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) != 0) { - printf("getnameinfo() failed\n"); + if (esp_getnameinfo((struct sockaddr *)sock_addr, sizeof(*sock_addr), + buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) != 0) { + printf("esp_getnameinfo() failed\n"); } else { printf("IPv4 address of interface \"%s\": %s\n", addr->ifa_name, buffer); + CHECK(strcmp("1.2.3.4", buffer) == 0); } } addr = addr->ifa_next; } // check that we got 1 address with exact content - CHECK(nr_of_addrs == 1); - CHECK(strcmp("1.2.3.4", buffer) == 0); - freeifaddrs(addresses); + CHECK(nr_of_addrs == expected_nr_of_addrs); + esp_freeifaddrs(addresses); } +TEST_CASE("esp_getifaddrs() with 0, 1, and 2 addresses", "[sock_utils]") +{ + test_getifaddr(0); + esp_netif_t *esp_netif = create_test_netif("station"); + REQUIRE(esp_netif != NULL); + test_getifaddr(1); + esp_netif_t *esp_netif2 = create_test_netif("station2"); + REQUIRE(esp_netif2 != NULL); + test_getifaddr(2); + esp_netif_destroy(esp_netif); + esp_netif_destroy(esp_netif2); +} + +static void test_pipe(int read_fd, int write_fd) +{ + CHECK(read_fd >= 0); + CHECK(write_fd >= 0); + CHECK(read(read_fd, buffer, sizeof(buffer)) < 0); + CHECK(write(write_fd, buffer, 1) > 0); + CHECK(read(read_fd, buffer, sizeof(buffer)) == 1); + +} + +TEST_CASE("socketpair()", "[sock_utils]") +{ + int fds[2]; + CHECK(esp_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); + printf("socketpair created fds: %d, %d\n", fds[0], fds[1]); + // check both directions + test_pipe(fds[0], fds[1]); + test_pipe(fds[1], fds[0]); + close(fds[0]); + close(fds[1]); +} + +TEST_CASE("pipe()", "[sock_utils]") +{ + int fds[2]; + CHECK(esp_pipe(fds) == 0); + printf("pipe created fds: %d, %d\n", fds[0], fds[1]); + // check only one direction + test_pipe(fds[0], fds[1]); + close(fds[0]); + close(fds[1]); +} + +TEST_CASE("gai_strerror()", "[sock_utils]") +{ + const char *str_error = esp_gai_strerror(EAI_BADFLAGS); + CHECK(str_error != NULL); +} + + extern "C" void app_main(void) { ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); - esp_netif_ip_info_t ip = { }; - ip.ip.addr = ESP_IP4TOADDR(1, 2, 3, 4); - base_cfg.ip_info = &ip; - esp_netif_config_t cfg = { .base = &base_cfg, .driver = NULL, .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA }; - esp_netif_t *esp_netif = esp_netif_new(&cfg); - Catch::Session session; int failures = session.run(); if (failures > 0) { printf("TEST FAILED! number of failures=%d\n", failures); + exit(1); } else { printf("Test passed!\n"); + exit(0); } - esp_netif_destroy(esp_netif); }