Skip to content

Commit 874b955

Browse files
committed
fix(sockutls): Per review comments
1 parent 4430a5b commit 874b955

File tree

12 files changed

+124
-67
lines changed

12 files changed

+124
-67
lines changed

.github/workflows/sockutls_build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
pip install idf-component-manager idf-build-apps --upgrade
6161
cd ${TEST_DIR}
6262
idf.py build
63-
./build/sockutls_host_test.elf
63+
./build/sock_utils_host_test.elf
6464
6565
test_sock_utils:
6666
# Skip running on forks since it won't have access to secrets

components/sock_utils/README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
# Socket Utilities
22

3-
This component provides a simplified implementation of various socket utilities using the `lwIP` and `esp_netif` components. It is particularly useful for porting Linux-based libraries where performance and memory constraints are not a primary concern.
3+
This component provides simplified implementations of common socket-related utilities using `lwIP` and `esp_netif`. It is especially useful for porting Linux-based libraries to ESP32 where performance and memory constraints are secondary considerations.
4+
45

56
## Supported Functions
67

7-
| API | Description | Limitations |
8-
|----------------|------------------------------------|--------------------------------------------------|
9-
| `ifaddrs()` | Uses `esp_netif` internally | Only supports IPv4 addresses |
10-
| `socketpair()` | Implements using two `lwIP` loopback stream sockets | Only supports IPv4 sockets |
11-
| `pipe()` | Wraps `socketpair()` | Utilizes bidirectional sockets instead of pipes |
12-
| `getnameinfo()`| Uses `lwIP`'s `inet_ntop()` | Supports only IPv4, and only `NI_NUMERICHOST` and `NI_NUMERICSERV` flags |
13-
| `gai_strerror()`| Simple `itoa`-like implementation | Only returns the numeric error code as a string |
8+
9+
| API | Description | Limitations |
10+
|------------------|-------------------------------------------------------------|-------------------------------------------------------------------|
11+
| `ifaddrs()` | Retrieves interface addresses using `esp_netif` | IPv4 addresses only |
12+
| `socketpair()` | Creates a pair of connected sockets using `lwIP` loopback stream sockets | IPv4 sockets only |
13+
| `pipe()` | Wraps `socketpair()` to provide unidirectional pipe-like functionality | Uses bidirectional sockets in place of true pipes |
14+
| `getnameinfo()` | Converts IP addresses to human-readable form using `lwIP`'s `inet_ntop()` | IPv4 only; supports `NI_NUMERICHOST` and `NI_NUMERICSERV` flags only |
15+
| `gai_strerror()` | Returns error code as a string | Simple numeric string representation only |
16+
17+
**Note**: `socketpair()` and `pipe()` are built on top of `lwIP` TCP sockets, inheriting the same characteristics. For instance, the maximum transmit buffer size is based on the `TCP_SND_BUF` setting.

components/sock_utils/examples/simple/main/app.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <unistd.h>
1111
#include <string.h>
1212
#include <errno.h>
13+
#include <net/if.h>
1314
#include <ifaddrs.h>
1415

1516
#ifdef ESP_PLATFORM
@@ -60,13 +61,16 @@ static void *reader_thread(void *arg)
6061
while (addr) {
6162
if (addr->ifa_addr && addr->ifa_addr->sa_family == AF_INET) { // look for IP4 addresses
6263
struct sockaddr_in *sock_addr = (struct sockaddr_in *) addr->ifa_addr;
63-
if (getnameinfo((struct sockaddr *)sock_addr, sizeof(*sock_addr),
64-
buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) != 0) {
65-
ESP_LOGE(TAG, "getnameinfo() failed");
66-
freeifaddrs(addresses);
67-
return NULL;
64+
if ((addr->ifa_flags & IFF_UP) == 0) {
65+
ESP_LOGI(TAG, "Network interface \"%s\" is down", addr->ifa_name);
66+
} else {
67+
if (getnameinfo((struct sockaddr *)sock_addr, sizeof(*sock_addr),
68+
buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) != 0) {
69+
freeifaddrs(addresses);
70+
return NULL;
71+
}
72+
ESP_LOGI(TAG, "IPv4 address of interface \"%s\": %s", addr->ifa_name, buffer);
6873
}
69-
ESP_LOGI(TAG, "IPv4 address of interface \"%s\": %s", addr->ifa_name, buffer);
7074
}
7175
addr = addr->ifa_next;
7276
}

components/sock_utils/examples/simple/pytest_sockutls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ def test_sockutls(dut):
1010
# Signal from the pipe simple implementation
1111
dut.expect('Received signal: IP4')
1212
# and the IPv4 address of the connected netif
13-
dut.expect('IPv4 address of interface "ETH_DEF"')
13+
dut.expect('IPv4 address of interface "en1"')

components/sock_utils/include/gai_strerror.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ extern "C" {
1818
#endif
1919

2020
/**
21-
* @brief Returns a string describing a `getaddrinfo()` error code.
21+
* @brief Returns a numeric string representing of `getaddrinfo()` error code.
2222
*
2323
* @param[in] ecode Error code returned by `getaddrinfo()`.
2424
*

components/sock_utils/include/ifaddrs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ struct ifaddrs {
2929
struct ifaddrs *ifa_next; /* Next item in list */
3030
char *ifa_name; /* Name of interface */
3131
struct sockaddr *ifa_addr; /* Address of interface */
32-
int ifa_flags;
32+
unsigned int ifa_flags;
3333
};
3434

3535
/**

components/sock_utils/include/poll.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
// This is a placeholder for poll API
8+
// Note: some libraries require `poll.h` in public include dirs

components/sock_utils/src/getnameinfo.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
2424
case AF_INET:
2525
// Handle numeric host address
2626
if (flags & NI_NUMERICHOST) {
27+
if (host == NULL || hostlen == 0) {
28+
return 0;
29+
}
2730
if (inet_ntop(AF_INET, &sinp->sin_addr, host, hostlen) == NULL) {
2831
return EOVERFLOW; // Error if address couldn't be converted
2932
}
@@ -33,6 +36,9 @@ int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
3336
if (flags & NI_NUMERICSERV) {
3437
// Print the port number, but for UDP services (if NI_DGRAM), we treat it the same way
3538
int port = ntohs(sinp->sin_port); // Convert port from network byte order
39+
if (serv == NULL || servlen == 0) {
40+
return 0;
41+
}
3642
if (snprintf(serv, servlen, "%d", port) < 0) {
3743
return EOVERFLOW; // Error if snprintf failed
3844
}

components/sock_utils/src/ifaddrs.c

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ static esp_err_t getifaddrs_unsafe(void *ctx)
2020
struct ifaddrs **next_addr = NULL;
2121
struct sockaddr_in *addr_in = NULL;
2222
esp_netif_t *netif = NULL;
23+
char if_name[5]; // lwip netif name buffer (e.g. `st1`, 2 letters + number)
2324
esp_netif_ip_info_t ip;
24-
int ret = ESP_OK;
25+
esp_err_t ret = ESP_OK;
2526

2627
while ((netif = esp_netif_next_unsafe(netif)) != NULL) {
2728
ESP_GOTO_ON_FALSE((ifaddr = (struct ifaddrs *)calloc(1, sizeof(struct ifaddrs))),
@@ -31,16 +32,17 @@ static esp_err_t getifaddrs_unsafe(void *ctx)
3132
} else {
3233
*next_addr = ifaddr; // attach next addr
3334
}
34-
ESP_GOTO_ON_FALSE((ifaddr->ifa_name = strdup(esp_netif_get_ifkey(netif))),
35+
ESP_GOTO_ON_ERROR(esp_netif_get_netif_impl_name(netif, if_name), err, TAG, "Failed to get netif name");
36+
ESP_GOTO_ON_FALSE((ifaddr->ifa_name = strdup(if_name)),
3537
ESP_ERR_NO_MEM, err, TAG, "Failed to allocate if name");
3638
ESP_GOTO_ON_FALSE((addr_in = (struct sockaddr_in *)calloc(1, sizeof(struct sockaddr_in))),
3739
ESP_ERR_NO_MEM, err, TAG, "Failed to allocate addr_in");
38-
ESP_GOTO_ON_ERROR(esp_netif_get_ip_info(netif, &ip), err, TAG, "Failed to allocate if name");
40+
ifaddr->ifa_addr = (struct sockaddr *)addr_in;
41+
ESP_GOTO_ON_ERROR(esp_netif_get_ip_info(netif, &ip), err, TAG, "Failed to get netif IP");
3942
ESP_LOGD(TAG, "IPv4 address: " IPSTR, IP2STR(&ip.ip));
4043
addr_in->sin_family = AF_INET;
4144
addr_in->sin_addr.s_addr = ip.ip.addr;
42-
ifaddr->ifa_addr = (struct sockaddr *)addr_in;
43-
ifaddr->ifa_flags = IFF_UP; // Mark the interface as UP, add more flags as needed
45+
ifaddr->ifa_flags = esp_netif_is_netif_up(netif) ? IFF_UP : 0;
4446
next_addr = &ifaddr->ifa_next;
4547
}
4648
if (next_addr == NULL) {
@@ -51,7 +53,12 @@ static esp_err_t getifaddrs_unsafe(void *ctx)
5153
return ret;
5254

5355
err:
54-
freeifaddrs(ifaddr);
56+
if (next_addr) {
57+
*next_addr = NULL;
58+
freeifaddrs(*ifap);
59+
} else {
60+
freeifaddrs(ifaddr);
61+
}
5562
*ifap = NULL;
5663
return ret;
5764

@@ -60,22 +67,34 @@ static esp_err_t getifaddrs_unsafe(void *ctx)
6067
int getifaddrs(struct ifaddrs **ifap)
6168
{
6269
if (ifap == NULL) {
70+
errno = EINVAL;
6371
return -1; // Invalid argument
6472
}
6573

66-
return esp_netif_tcpip_exec(getifaddrs_unsafe, ifap) == ESP_OK ? 0 : -1;
74+
esp_err_t ret = esp_netif_tcpip_exec(getifaddrs_unsafe, ifap);
75+
switch (ret) {
76+
case ESP_OK:
77+
return 0;
78+
case ESP_ERR_NO_MEM:
79+
errno = ENOMEM;
80+
return -1;
81+
case ESP_ERR_INVALID_ARG:
82+
case ESP_ERR_ESP_NETIF_INVALID_PARAMS:
83+
errno = EINVAL;
84+
return -1;
85+
default:
86+
case ESP_FAIL:
87+
errno = EIO; // Generic I/O Error
88+
return -1;
89+
}
6790
}
6891

6992
void freeifaddrs(struct ifaddrs *ifa)
7093
{
7194
while (ifa != NULL) {
7295
struct ifaddrs *next = ifa->ifa_next;
73-
if (ifa->ifa_name) {
74-
free(ifa->ifa_name);
75-
}
76-
if (ifa->ifa_addr) {
77-
free(ifa->ifa_addr);
78-
}
96+
free(ifa->ifa_name);
97+
free(ifa->ifa_addr);
7998
free(ifa);
8099
ifa = next;
81100
}

components/sock_utils/src/socketpair.c

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,12 @@
1212

1313
static const char *TAG = "socket_helpers";
1414

15-
static int set_nonblocking(int sock)
15+
int socketpair(int domain, int type, int protocol, int sv[2])
1616
{
17-
int opt;
18-
opt = fcntl(sock, F_GETFL, 0);
19-
if (opt == -1) {
20-
return -1;
21-
}
22-
if (fcntl(sock, F_SETFL, opt | O_NONBLOCK) == -1) {
17+
if (protocol != 0 || type != SOCK_STREAM || domain != AF_UNIX) {
18+
errno = ENOSYS; // not implemented
2319
return -1;
2420
}
25-
return 0;
26-
}
27-
28-
int socketpair(int domain, int type, int protocol, int sv[2])
29-
{
3021
struct sockaddr_storage ss;
3122
struct sockaddr_in *sa = (struct sockaddr_in *)&ss;
3223
socklen_t ss_len = sizeof(struct sockaddr_in);
@@ -51,15 +42,9 @@ int socketpair(int domain, int type, int protocol, int sv[2])
5142

5243
fd1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
5344
ESP_GOTO_ON_FALSE(fd1 != INVALID_SOCKET, -1, err, TAG, "Cannot create read side socket");
54-
ESP_GOTO_ON_FALSE(set_nonblocking(fd1) == 0, -1, err, TAG, "Failed to set socket to nonblocking mode");
55-
if (connect(fd1, (struct sockaddr *)&ss, ss_len) < 0) {
56-
ESP_GOTO_ON_FALSE(errno == EINPROGRESS || errno == EWOULDBLOCK, -1, err, TAG, "Failed to connect fd1");
57-
}
45+
ESP_GOTO_ON_FALSE(connect(fd1, (struct sockaddr *)&ss, ss_len) >= 0, -1, err, TAG, "Failed to connect fd1");
5846
fd2 = accept(listenfd, NULL, 0);
59-
if (fd2 == -1) {
60-
ESP_GOTO_ON_FALSE(errno == EINPROGRESS || errno == EWOULDBLOCK, -1, err, TAG, "Failed to accept fd2");
61-
}
62-
ESP_GOTO_ON_FALSE(set_nonblocking(fd2) == 0, -1, err, TAG, "Failed to set socket to nonblocking mode");
47+
ESP_GOTO_ON_FALSE(fd2 != INVALID_SOCKET, -1, err, TAG, "Failed to accept fd2");
6348

6449
close(listenfd);
6550
sv[0] = fd1;
@@ -73,9 +58,6 @@ int socketpair(int domain, int type, int protocol, int sv[2])
7358
if (fd1 != INVALID_SOCKET) {
7459
close(fd1);
7560
}
76-
if (fd2 != INVALID_SOCKET) {
77-
close(fd2);
78-
}
7961
return ret;
8062
}
8163

@@ -84,5 +66,11 @@ int pipe(int pipefd[2])
8466
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd) == -1) {
8567
return -1;
8668
}
69+
// Close the unwanted ends to make it a unidirectional pipe
70+
if (shutdown(pipefd[0], SHUT_WR) == -1 || shutdown(pipefd[1], SHUT_RD) == -1) {
71+
close(pipefd[0]);
72+
close(pipefd[1]);
73+
return -1;
74+
}
8775
return 0;
8876
}

components/sock_utils/test/host/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ set(COMPONENTS main)
44

55
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
66

7-
project(sockutls_host_test)
7+
project(sock_utils_host_test)

components/sock_utils/test/host/main/test_sock_utils.cpp

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,50 @@
99
#include "catch2/catch_test_macros.hpp"
1010
#include "catch2/catch_session.hpp"
1111

12+
#define TEST_PORT_NUMBER 3333
13+
#define TEST_PORT_STRING "3333"
14+
1215
static char buffer[64];
1316

14-
static esp_netif_t *create_test_netif(const char *if_key)
17+
static esp_err_t dummy_transmit(void *h, void *buffer, size_t len)
18+
{
19+
return ESP_OK;
20+
}
21+
22+
static esp_err_t dummy_transmit_wrap(void *h, void *buffer, size_t len, void *pbuf)
23+
{
24+
return ESP_OK;
25+
}
26+
27+
static esp_netif_t *create_test_netif(const char *if_key, uint8_t last_octet)
1528
{
1629
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
1730
esp_netif_ip_info_t ip = { };
18-
ip.ip.addr = ESP_IP4TOADDR(1, 2, 3, 4);
31+
ip.ip.addr = ESP_IP4TOADDR(1, 2, 3, last_octet);
1932
base_cfg.ip_info = &ip;
2033
base_cfg.if_key = if_key;
21-
esp_netif_config_t cfg = { .base = &base_cfg, .driver = NULL, .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA };
22-
return esp_netif_new(&cfg);
34+
// create a dummy driver, so the netif could be started and set up
35+
base_cfg.flags = ESP_NETIF_FLAG_AUTOUP;
36+
esp_netif_driver_ifconfig_t driver_cfg = {};
37+
driver_cfg.handle = (esp_netif_iodriver_handle)1;
38+
driver_cfg.transmit = dummy_transmit;
39+
driver_cfg.transmit_wrap = dummy_transmit_wrap;
40+
esp_netif_config_t cfg = { .base = &base_cfg, .driver = &driver_cfg, .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA };
41+
esp_netif_t *netif = esp_netif_new(&cfg);
42+
// need to start the netif to be considered in tests
43+
esp_netif_action_start(netif, nullptr, 0, nullptr);
44+
return netif;
2345
}
2446

2547
TEST_CASE("esp_getnameinfo() for IPv4", "[sock_utils]")
2648
{
2749
struct sockaddr_in sock_addr = {};
2850
sock_addr.sin_family = AF_INET;
29-
sock_addr.sin_port = 257; // (0x0101: same number for LE and BE)
51+
sock_addr.sin_port = htons(TEST_PORT_NUMBER); // sock_addr fields are in network byte order
3052
REQUIRE(esp_getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) == 0);
3153
CHECK(strcmp("0.0.0.0", buffer) == 0);
3254
CHECK(esp_getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), NULL, 0, buffer, sizeof(buffer), NI_NUMERICSERV) == 0);
33-
CHECK(strcmp("257", buffer) == 0);
55+
CHECK(strcmp(TEST_PORT_STRING, buffer) == 0);
3456
}
3557

3658
TEST_CASE("esp_getnameinfo() for IPv6", "[sock_utils]")
@@ -58,7 +80,13 @@ static void test_getifaddr(int expected_nr_of_addrs)
5880
printf("esp_getnameinfo() failed\n");
5981
} else {
6082
printf("IPv4 address of interface \"%s\": %s\n", addr->ifa_name, buffer);
61-
CHECK(strcmp("1.2.3.4", buffer) == 0);
83+
if (strcmp(addr->ifa_name, "st1") == 0) {
84+
CHECK(strcmp("1.2.3.1", buffer) == 0);
85+
} else if (strcmp(addr->ifa_name, "st2") == 0) {
86+
CHECK(strcmp("1.2.3.2", buffer) == 0);
87+
} else {
88+
FAIL("unexpected network interface");
89+
}
6290
}
6391
}
6492
addr = addr->ifa_next;
@@ -71,10 +99,10 @@ static void test_getifaddr(int expected_nr_of_addrs)
7199
TEST_CASE("esp_getifaddrs() with 0, 1, and 2 addresses", "[sock_utils]")
72100
{
73101
test_getifaddr(0);
74-
esp_netif_t *esp_netif = create_test_netif("station");
102+
esp_netif_t *esp_netif = create_test_netif("station", 1); // st1
75103
REQUIRE(esp_netif != NULL);
76104
test_getifaddr(1);
77-
esp_netif_t *esp_netif2 = create_test_netif("station2");
105+
esp_netif_t *esp_netif2 = create_test_netif("station2", 2); // st2
78106
REQUIRE(esp_netif2 != NULL);
79107
test_getifaddr(2);
80108
esp_netif_destroy(esp_netif);
@@ -85,10 +113,10 @@ static void test_pipe(int read_fd, int write_fd)
85113
{
86114
CHECK(read_fd >= 0);
87115
CHECK(write_fd >= 0);
88-
CHECK(read(read_fd, buffer, sizeof(buffer)) < 0);
89116
CHECK(write(write_fd, buffer, 1) > 0);
90-
CHECK(read(read_fd, buffer, sizeof(buffer)) == 1);
91-
117+
CHECK(write(write_fd, buffer, 1) > 0);
118+
CHECK(read(read_fd, buffer, 1) == 1);
119+
CHECK(read(read_fd, buffer, 1) == 1);
92120
}
93121

94122
TEST_CASE("socketpair()", "[sock_utils]")

0 commit comments

Comments
 (0)