diff --git a/components/sock_utils/CMakeLists.txt b/components/sock_utils/CMakeLists.txt index 4f4e189835..87c5e0ee01 100644 --- a/components/sock_utils/CMakeLists.txt +++ b/components/sock_utils/CMakeLists.txt @@ -2,5 +2,6 @@ idf_component_register(SRCS "src/getnameinfo.c" "src/ifaddrs.c" "src/gai_strerror.c" "src/socketpair.c" + "src/gethostname.c" INCLUDE_DIRS "include" PRIV_REQUIRES lwip esp_netif) diff --git a/components/sock_utils/include/gethostname.h b/components/sock_utils/include/gethostname.h new file mode 100644 index 0000000000..b43ed236a5 --- /dev/null +++ b/components/sock_utils/include/gethostname.h @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "sdkconfig.h" + +#ifdef CONFIG_IDF_TARGET_LINUX +// namespace with esp_ on linux to avoid conflict of symbols +#define gethostname esp_gethostname +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Retrieves the hostname of the device. + * + * This function provides the hostname associated with the network interface. + * Unlike the standard behavior where the hostname represents a system-wide name, + * this implementation returns lwip netif hostname (used as a hostname in DHCP packets) + * + * @param[out] name A pointer to a buffer where the hostname will be stored. + * The buffer must be allocated by the caller. + * @param[in] len The size of the buffer pointed to by @p name. The hostname, + * including the null-terminator, must fit within this size. + * + * @return + * - 0 on success + * - -1 on error, with `errno` set to indicate the error: + * - `EINVAL`: Invalid argument, name is NULL, or hostname is too long + * + * @note This implementation retrieves the hostname associated with the network + * interface using the `esp_netif_get_hostname()` function, which in turn + * returns lwip netif hostname used in DHCP packets if LWIP_NETIF_HOSTNAME=1 (hardcoded) + * in ESP-IDF lwip port. + * As there could be multiple network interfaces in the system, the logic tries + * to find the default (active) netif first, then it looks for any (inactive) netif + * with highest route priority. If none of the above found or esp_netif_get_hostname() fails + * for the selected interface, this API returns the default value of `CONFIG_LWIP_LOCAL_HOSTNAME`, + * the local hostname from lwip component configuration menu. + */ +int gethostname(char *name, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/components/sock_utils/src/gethostname.c b/components/sock_utils/src/gethostname.c new file mode 100644 index 0000000000..b9941ed5d3 --- /dev/null +++ b/components/sock_utils/src/gethostname.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "gethostname.h" +#include "esp_netif.h" +#include "errno.h" +#include "esp_log.h" + + +static bool highest_prio_netif(esp_netif_t *netif, void *ctx) +{ + esp_netif_t **highest_so_far = ctx; + if (esp_netif_get_route_prio(netif) > esp_netif_get_route_prio(*highest_so_far)) { + *highest_so_far = netif; + } + return false; // go over the entire list to find the netif with the highest route-prio +} + +int gethostname(char *name, size_t len) +{ + if (name == NULL) { + errno = EINVAL; + return -1; + } + const char *netif_hostname = CONFIG_LWIP_LOCAL_HOSTNAME; // default value from Kconfig + + // Find the default netif + esp_netif_t *default_netif = esp_netif_get_default_netif(); + if (default_netif == NULL) { // if no netif is active/up -> find the highest prio netif + esp_netif_find_if(highest_prio_netif, &default_netif); + } + // now the `default_netif` could be NULL and/or the esp_netif_get_hostname() could fail + // but we ignore the return code, as if it fails, the `netif_hostname` still holds the default value + esp_netif_get_hostname(default_netif, &netif_hostname); + + if (netif_hostname == NULL || len < strlen(netif_hostname) + 1) { // including the NULL terminator + errno = EINVAL; + return -1; + } + strcpy(name, netif_hostname); + return 0; +} 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 e1d3facf6d..fbd77a3ac0 100644 --- a/components/sock_utils/test/host/main/test_sock_utils.cpp +++ b/components/sock_utils/test/host/main/test_sock_utils.cpp @@ -3,6 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ +#include "gethostname.h" #include "ifaddrs.h" #include "esp_netif.h" #include "esp_event.h" @@ -148,6 +149,30 @@ TEST_CASE("gai_strerror()", "[sock_utils]") CHECK(str_error != NULL); } +TEST_CASE("gethostname()", "[sock_utils]") +{ + const char *test_netif_name = "station"; + char hostname[32]; + int ret; + + // expect failure + ret = gethostname(hostname, strlen(CONFIG_LWIP_LOCAL_HOSTNAME) - 1); + CHECK(ret == -1); + + // happy flow with the default name + ret = gethostname(hostname, sizeof(hostname)); + CHECK(ret == 0); + CHECK(strcmp(hostname, CONFIG_LWIP_LOCAL_HOSTNAME) == 0); + + // happy flow with the netif name + esp_netif_t *esp_netif = create_test_netif(test_netif_name, 1); + REQUIRE(esp_netif != NULL); + CHECK(esp_netif_set_hostname(esp_netif, test_netif_name) == ESP_OK); + ret = gethostname(hostname, sizeof(hostname)); + CHECK(ret == 0); + CHECK(strcmp(hostname, test_netif_name) == 0); + esp_netif_destroy(esp_netif); +} extern "C" void app_main(void) {