mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-03 21:56:40 +02:00
Compare commits
4 Commits
modem-v1.2
...
sock_utils
Author | SHA1 | Date | |
---|---|---|---|
67191f3bb5 | |||
6d94ad646d | |||
685d47cd2f | |||
31f57ad067 |
2
.github/workflows/clang-tidy.yml
vendored
2
.github/workflows/clang-tidy.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
||||
working-directory: test_app
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
idf.py clang-check --include-paths $GITHUB_WORKSPACE --exclude-paths $PWD --run-clang-tidy-py run-clang-tidy
|
||||
idf.py clang-check --include-paths $GITHUB_WORKSPACE --exclude-paths $PWD --run-clang-tidy-py run-clang-tidy --run-clang-tidy-options "-checks=-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling"
|
||||
cp warnings.txt ../
|
||||
- name: Convert clang-tidy results into SARIF output
|
||||
run: |
|
||||
|
1
.github/workflows/publish-docs-component.yml
vendored
1
.github/workflows/publish-docs-component.yml
vendored
@ -100,5 +100,6 @@ jobs:
|
||||
components/console_cmd_wifi;
|
||||
components/mbedtls_cxx;
|
||||
components/mosquitto;
|
||||
components/sock_utils;
|
||||
namespace: "espressif"
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
|
95
.github/workflows/sockutls_build.yml
vendored
Normal file
95
.github/workflows/sockutls_build.yml
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
name: "sock_utils: build-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_sock_utils:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'sock_utils') || github.event_name == 'push'
|
||||
name: Socket helpers build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3"]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
TEST_DIR: components/sock_utils/examples/simple
|
||||
TARGET_TEST_DIR: build_esp32_default
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
python ci/build_apps.py ${TEST_DIR}
|
||||
cd ${TEST_DIR}
|
||||
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
|
||||
zip -qur artifacts.zip ${TARGET_TEST_DIR}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sock_utils_target_esp32_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TEST_DIR }}/artifacts.zip
|
||||
if-no-files-found: error
|
||||
host_test_sock_utils:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'sock_utils') || github.event_name == 'push'
|
||||
name: Socket helpers host test
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3"]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
TEST_DIR: components/sock_utils/test/host
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
cd ${TEST_DIR}
|
||||
idf.py build
|
||||
./build/sock_utils_host_test.elf
|
||||
|
||||
test_sock_utils:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'sock_utils') || github.event_name == 'push' )
|
||||
name: Socket helpers target test
|
||||
needs: build_sock_utils
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
env:
|
||||
TEST_DIR: components/sock_utils/examples/simple
|
||||
TARGET_TEST_DIR: build_esp32_default
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: sock_utils_target_esp32_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TEST_DIR }}/ci/
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
mv $dir build
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./results_esp32_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=esp32
|
||||
done
|
@ -61,8 +61,8 @@ repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: commit message scopes
|
||||
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq)\)\:)'
|
||||
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls)\)\:)'
|
||||
language: pygrep
|
||||
args: [--multiline]
|
||||
stages: [commit-msg]
|
||||
|
@ -62,3 +62,7 @@ Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
|
||||
|
||||
* Brief introduction [README](components/mosquitto/README.md)
|
||||
* API documentation [api.md](components/mosquitto/api.md)
|
||||
|
||||
### Socket helpers (sock-utils)
|
||||
|
||||
* Brief introduction [README](components/sock_utils/README.md)
|
||||
|
8
components/sock_utils/.cz.yaml
Normal file
8
components/sock_utils/.cz.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
commitizen:
|
||||
bump_message: 'bump(sockutls): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py sock_utils
|
||||
tag_format: sock_utils-v$version
|
||||
version: 0.1.0
|
||||
version_files:
|
||||
- idf_component.yml
|
7
components/sock_utils/CHANGELOG.md
Normal file
7
components/sock_utils/CHANGELOG.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## [0.1.0](https://github.com/espressif/esp-protocols/commits/sock_utils-v0.1.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Add initial support for socket helpers ([31f57ad0](https://github.com/espressif/esp-protocols/commit/31f57ad0))
|
6
components/sock_utils/CMakeLists.txt
Normal file
6
components/sock_utils/CMakeLists.txt
Normal file
@ -0,0 +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)
|
201
components/sock_utils/LICENSE
Normal file
201
components/sock_utils/LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed 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.
|
17
components/sock_utils/README.md
Normal file
17
components/sock_utils/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Socket Utilities
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Supported Functions
|
||||
|
||||
|
||||
| API | Description | Limitations |
|
||||
|------------------|-------------------------------------------------------------|-------------------------------------------------------------------|
|
||||
| `ifaddrs()` | Retrieves interface addresses using `esp_netif` | IPv4 addresses only |
|
||||
| `socketpair()` | Creates a pair of connected sockets using `lwIP` loopback stream sockets | IPv4 sockets only |
|
||||
| `pipe()` | Wraps `socketpair()` to provide unidirectional pipe-like functionality | Uses bidirectional sockets in place of true pipes |
|
||||
| `getnameinfo()` | Converts IP addresses to human-readable form using `lwIP`'s `inet_ntop()` | IPv4 only; supports `NI_NUMERICHOST` and `NI_NUMERICSERV` flags only |
|
||||
| `gai_strerror()` | Returns error code as a string | Simple numeric string representation only |
|
||||
|
||||
**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.
|
8
components/sock_utils/examples/simple/CMakeLists.txt
Normal file
8
components/sock_utils/examples/simple/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(sock_utils_example)
|
30
components/sock_utils/examples/simple/README.md
Normal file
30
components/sock_utils/examples/simple/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Simple example
|
||||
|
||||
This example demonstrates using socket helpers on ESP_PLATFORM
|
||||
|
||||
The functionality of this example is meant to be very generic, so the application is compilable on linux without any IDF dependencies:
|
||||
```bash
|
||||
gcc main/app.c -lpthread
|
||||
```
|
||||
|
||||
## Example output on ESP_PLATFORM
|
||||
|
||||
```
|
||||
I (8073) esp_netif_handlers: example_netif_sta ip: 192.168.0.32, mask: 255.255.255.0, gw: 192.168.0.1
|
||||
I (8073) example_connect: Got IPv4 event: Interface "example_netif_sta" address: 192.168.0.32
|
||||
I (8073) example_common: Connected to example_netif_sta
|
||||
I (8083) example_common: - IPv4 address: 192.168.0.32,
|
||||
I (8093) example_common: - IPv6 address: fe80:0000:0000:0000:XXXX, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (8103) sock_utils_example: Received signal: IP4
|
||||
I (8103) sock_utils_example: IPv4 address of interface "sta": 192.168.0.32
|
||||
I (8113) main_task: Returned from app_main()
|
||||
```
|
||||
|
||||
## Example output on linux platform
|
||||
|
||||
```
|
||||
I[sock_utils_example]: Received signal: IP4
|
||||
I[sock_utils_example]: IPv4 address of interface "lo": 127.0.0.1
|
||||
I[sock_utils_example]: IPv4 address of interface "en0": 192.168.0.28
|
||||
I[sock_utils_example]: IPv4 address of interface "docker0": 172.17.0.1
|
||||
```
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "app.c"
|
||||
INCLUDE_DIRS ".")
|
123
components/sock_utils/examples/simple/main/app.c
Normal file
123
components/sock_utils/examples/simple/main/app.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_log.h"
|
||||
#include "protocol_examples_common.h"
|
||||
#else
|
||||
// Provides simplified support for building on linux (whithout official IDF linux target)
|
||||
// in order to check with the actual host functionality
|
||||
// use `gcc main/app.c -lpthread`
|
||||
#define SIMPLE_LOG( lev, tag, format, ... ) do { printf("%s[%s]: ", lev, tag); printf(format, ##__VA_ARGS__); printf("\n"); } while(0)
|
||||
#define ESP_LOGE(tag, format, ... ) SIMPLE_LOG("E", tag, format, ##__VA_ARGS__)
|
||||
#define ESP_LOGI(tag, format, ... ) SIMPLE_LOG("I", tag, format, ##__VA_ARGS__)
|
||||
#define ESP_ERROR_CHECK(action) action
|
||||
static void example_connect(void) { }
|
||||
#endif
|
||||
|
||||
static const char *TAG = "sock_utils_example";
|
||||
|
||||
static void *reader_thread(void *arg)
|
||||
{
|
||||
int *pipe_fds = (int *)arg;
|
||||
char buffer[64];
|
||||
int len;
|
||||
while ((len = read(pipe_fds[0], buffer, sizeof(buffer))) < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
ESP_LOGE(TAG, "Failed reading from pipe: %d", errno);
|
||||
return NULL;
|
||||
}
|
||||
usleep(100000);
|
||||
}
|
||||
buffer[len] = '\0';
|
||||
ESP_LOGI(TAG, "Received signal: %s", buffer);
|
||||
if (strcmp(buffer, "IP4") != 0) {
|
||||
ESP_LOGE(TAG, "Unknown signal");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ifaddrs *addresses, *addr;
|
||||
if (getifaddrs(&addresses) == -1) {
|
||||
ESP_LOGE(TAG, "getifaddrs() failed");
|
||||
return NULL;
|
||||
}
|
||||
addr = addresses;
|
||||
|
||||
while (addr) {
|
||||
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 ((addr->ifa_flags & IFF_UP) == 0) {
|
||||
ESP_LOGI(TAG, "Network interface \"%s\" is down", addr->ifa_name);
|
||||
} else {
|
||||
if (getnameinfo((struct sockaddr *)sock_addr, sizeof(*sock_addr),
|
||||
buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) != 0) {
|
||||
freeifaddrs(addresses);
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "IPv4 address of interface \"%s\": %s", addr->ifa_name, buffer);
|
||||
}
|
||||
}
|
||||
addr = addr->ifa_next;
|
||||
}
|
||||
freeifaddrs(addresses);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void simple_pipe_example(void)
|
||||
{
|
||||
int pipe_fds[2];
|
||||
if (pipe(pipe_fds) == -1) {
|
||||
ESP_LOGE(TAG, "Failed to create pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
// creates reader thread to wait for a pipe signal
|
||||
// and print out our IPv4 addresses
|
||||
pthread_t reader;
|
||||
pthread_create(&reader, NULL, reader_thread, pipe_fds);
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
// at this point we should have received an IP address -> send signal to the reader thread
|
||||
const char signal[] = "IP4";
|
||||
write(pipe_fds[1], signal, sizeof(signal));
|
||||
pthread_join(reader, NULL);
|
||||
|
||||
// Close pipe file descriptors
|
||||
close(pipe_fds[0]);
|
||||
close(pipe_fds[1]);
|
||||
}
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
simple_pipe_example();
|
||||
}
|
||||
#else
|
||||
int main(void)
|
||||
{
|
||||
simple_pipe_example();
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
sock_utils:
|
||||
version: "*"
|
||||
override_path: '../../../'
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
13
components/sock_utils/examples/simple/pytest_sockutls.py
Normal file
13
components/sock_utils/examples/simple/pytest_sockutls.py
Normal file
@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
def test_sockutls(dut):
|
||||
"""
|
||||
steps:
|
||||
1. Start the example
|
||||
2. Check the resultant IPv4 address from station netif
|
||||
"""
|
||||
# Signal from the pipe simple implementation
|
||||
dut.expect('Received signal: IP4')
|
||||
# and the IPv4 address of the connected netif
|
||||
dut.expect('IPv4 address of interface "en1"')
|
2
components/sock_utils/examples/simple/sdkconfig.ci
Normal file
2
components/sock_utils/examples/simple/sdkconfig.ci
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
6
components/sock_utils/idf_component.yml
Normal file
6
components/sock_utils/idf_component.yml
Normal file
@ -0,0 +1,6 @@
|
||||
version: 0.1.0
|
||||
description: The component provides helper implementation of common system/socket utilities
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/sock_utils
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=5.0'
|
31
components/sock_utils/include/gai_strerror.h
Normal file
31
components/sock_utils/include/gai_strerror.h
Normal file
@ -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 numeric string representing of `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
|
40
components/sock_utils/include/getnameinfo.h
Normal file
40
components/sock_utils/include/getnameinfo.h
Normal file
@ -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
|
56
components/sock_utils/include/ifaddrs.h
Normal file
56
components/sock_utils/include/ifaddrs.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#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 */
|
||||
struct sockaddr *ifa_addr; /* Address of interface */
|
||||
unsigned 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 __cplusplus
|
||||
}
|
||||
#endif
|
34
components/sock_utils/include/netdb_macros.h
Normal file
34
components/sock_utils/include/netdb_macros.h
Normal file
@ -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
|
8
components/sock_utils/include/poll.h
Normal file
8
components/sock_utils/include/poll.h
Normal file
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// This is a placeholder for poll API
|
||||
// Note: some libraries require `poll.h` in public include dirs
|
49
components/sock_utils/include/socketpair.h
Normal file
49
components/sock_utils/include/socketpair.h
Normal file
@ -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
|
17
components/sock_utils/src/gai_strerror.c
Normal file
17
components/sock_utils/src/gai_strerror.c
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "gai_strerror.h"
|
||||
|
||||
_Thread_local char gai_strerror_string[32];
|
||||
|
||||
const char *gai_strerror(int ecode)
|
||||
{
|
||||
if (snprintf(gai_strerror_string, sizeof(gai_strerror_string), "EAI error:%d", ecode) < 0) {
|
||||
return "gai_strerror() failed";
|
||||
}
|
||||
return gai_strerror_string;
|
||||
}
|
54
components/sock_utils/src/getnameinfo.c
Normal file
54
components/sock_utils/src/getnameinfo.c
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <fcntl.h>
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "ifaddrs.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
|
||||
char *host, socklen_t hostlen,
|
||||
char *serv, socklen_t servlen, int flags)
|
||||
{
|
||||
// Validate flags to ensure only allowed flags are passed.
|
||||
if (flags & ~(NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM)) {
|
||||
return EAI_BADFLAGS;
|
||||
}
|
||||
|
||||
const struct sockaddr_in *sinp = (const struct sockaddr_in *) addr;
|
||||
|
||||
switch (addr->sa_family) {
|
||||
case AF_INET:
|
||||
// Handle numeric host address
|
||||
if (flags & NI_NUMERICHOST) {
|
||||
if (host == NULL || hostlen == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (inet_ntop(AF_INET, &sinp->sin_addr, host, hostlen) == NULL) {
|
||||
return EOVERFLOW; // Error if address couldn't be converted
|
||||
}
|
||||
}
|
||||
|
||||
// Handle numeric service (port)
|
||||
if (flags & NI_NUMERICSERV) {
|
||||
// Print the port number, but for UDP services (if NI_DGRAM), we treat it the same way
|
||||
int port = ntohs(sinp->sin_port); // Convert port from network byte order
|
||||
if (serv == NULL || servlen == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (snprintf(serv, servlen, "%d", port) < 0) {
|
||||
return EOVERFLOW; // Error if snprintf failed
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return EAI_FAMILY; // Unsupported address family
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
101
components/sock_utils/src/ifaddrs.c
Normal file
101
components/sock_utils/src/ifaddrs.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include "esp_netif.h"
|
||||
#include "esp_check.h"
|
||||
#include <stdlib.h>
|
||||
#include "ifaddrs.h"
|
||||
|
||||
static const char *TAG = "sockutls_getifaddr";
|
||||
|
||||
|
||||
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;
|
||||
char if_name[5]; // lwip netif name buffer (e.g. `st1`, 2 letters + number)
|
||||
esp_netif_ip_info_t ip;
|
||||
esp_err_t 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_ERROR(esp_netif_get_netif_impl_name(netif, if_name), err, TAG, "Failed to get netif name");
|
||||
ESP_GOTO_ON_FALSE((ifaddr->ifa_name = strdup(if_name)),
|
||||
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");
|
||||
ifaddr->ifa_addr = (struct sockaddr *)addr_in;
|
||||
ESP_GOTO_ON_ERROR(esp_netif_get_ip_info(netif, &ip), err, TAG, "Failed to get netif IP");
|
||||
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_flags = esp_netif_is_netif_up(netif) ? IFF_UP : 0;
|
||||
next_addr = &ifaddr->ifa_next;
|
||||
}
|
||||
if (next_addr == NULL) {
|
||||
*ifap = NULL; // no addresses found
|
||||
} else {
|
||||
*next_addr = NULL; // terminate the list
|
||||
}
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if (next_addr) {
|
||||
*next_addr = NULL;
|
||||
freeifaddrs(*ifap);
|
||||
} else {
|
||||
freeifaddrs(ifaddr);
|
||||
}
|
||||
*ifap = NULL;
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int getifaddrs(struct ifaddrs **ifap)
|
||||
{
|
||||
if (ifap == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1; // Invalid argument
|
||||
}
|
||||
|
||||
esp_err_t ret = esp_netif_tcpip_exec(getifaddrs_unsafe, ifap);
|
||||
switch (ret) {
|
||||
case ESP_OK:
|
||||
return 0;
|
||||
case ESP_ERR_NO_MEM:
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
case ESP_ERR_INVALID_ARG:
|
||||
case ESP_ERR_ESP_NETIF_INVALID_PARAMS:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
default:
|
||||
case ESP_FAIL:
|
||||
errno = EIO; // Generic I/O Error
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void freeifaddrs(struct ifaddrs *ifa)
|
||||
{
|
||||
while (ifa != NULL) {
|
||||
struct ifaddrs *next = ifa->ifa_next;
|
||||
free(ifa->ifa_name);
|
||||
free(ifa->ifa_addr);
|
||||
free(ifa);
|
||||
ifa = next;
|
||||
}
|
||||
}
|
76
components/sock_utils/src/socketpair.c
Normal file
76
components/sock_utils/src/socketpair.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <fcntl.h>
|
||||
#include "lwip/sockets.h"
|
||||
#include "socketpair.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
#define INVALID_SOCKET (-1)
|
||||
|
||||
static const char *TAG = "socket_helpers";
|
||||
|
||||
int socketpair(int domain, int type, int protocol, int sv[2])
|
||||
{
|
||||
if (protocol != 0 || type != SOCK_STREAM || domain != AF_UNIX) {
|
||||
errno = ENOSYS; // not implemented
|
||||
return -1;
|
||||
}
|
||||
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(connect(fd1, (struct sockaddr *)&ss, ss_len) >= 0, -1, err, TAG, "Failed to connect fd1");
|
||||
fd2 = accept(listenfd, NULL, 0);
|
||||
ESP_GOTO_ON_FALSE(fd2 != INVALID_SOCKET, -1, err, TAG, "Failed to accept fd2");
|
||||
|
||||
close(listenfd);
|
||||
sv[0] = fd1;
|
||||
sv[1] = fd2;
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if (listenfd != INVALID_SOCKET) {
|
||||
close(listenfd);
|
||||
}
|
||||
if (fd1 != INVALID_SOCKET) {
|
||||
close(fd1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pipe(int pipefd[2])
|
||||
{
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd) == -1) {
|
||||
return -1;
|
||||
}
|
||||
// Close the unwanted ends to make it a unidirectional pipe
|
||||
if (shutdown(pipefd[0], SHUT_WR) == -1 || shutdown(pipefd[1], SHUT_RD) == -1) {
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
7
components/sock_utils/test/host/CMakeLists.txt
Normal file
7
components/sock_utils/test/host/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
project(sock_utils_host_test)
|
6
components/sock_utils/test/host/main/CMakeLists.txt
Normal file
6
components/sock_utils/test/host/main/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
idf_component_register(SRCS "test_sock_utils.cpp"
|
||||
INCLUDE_DIRS "$ENV{IDF_PATH}/tools"
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC -fsanitize=address -fconcepts)
|
||||
target_link_options(${COMPONENT_LIB} PUBLIC -fsanitize=address)
|
5
components/sock_utils/test/host/main/idf_component.yml
Normal file
5
components/sock_utils/test/host/main/idf_component.yml
Normal file
@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
espressif/catch2: "^3.4.0"
|
||||
sock_utils:
|
||||
version: "*"
|
||||
override_path: '../../../'
|
167
components/sock_utils/test/host/main/test_sock_utils.cpp
Normal file
167
components/sock_utils/test/host/main/test_sock_utils.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "ifaddrs.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "catch2/catch_test_macros.hpp"
|
||||
#include "catch2/catch_session.hpp"
|
||||
|
||||
#define TEST_PORT_NUMBER 3333
|
||||
#define TEST_PORT_STRING "3333"
|
||||
|
||||
static char buffer[64];
|
||||
|
||||
static esp_err_t dummy_transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t dummy_transmit_wrap(void *h, void *buffer, size_t len, void *pbuf)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_netif_t *create_test_netif(const char *if_key, uint8_t last_octet)
|
||||
{
|
||||
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, last_octet);
|
||||
base_cfg.ip_info = &ip;
|
||||
base_cfg.if_key = if_key;
|
||||
// create a dummy driver, so the netif could be started and set up
|
||||
base_cfg.flags = ESP_NETIF_FLAG_AUTOUP;
|
||||
esp_netif_driver_ifconfig_t driver_cfg = {};
|
||||
driver_cfg.handle = (esp_netif_iodriver_handle)1;
|
||||
driver_cfg.transmit = dummy_transmit;
|
||||
driver_cfg.transmit_wrap = dummy_transmit_wrap;
|
||||
esp_netif_config_t cfg = { .base = &base_cfg, .driver = &driver_cfg, .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA };
|
||||
esp_netif_t *netif = esp_netif_new(&cfg);
|
||||
// need to start the netif to be considered in tests
|
||||
esp_netif_action_start(netif, nullptr, 0, nullptr);
|
||||
return netif;
|
||||
}
|
||||
|
||||
TEST_CASE("esp_getnameinfo() for IPv4", "[sock_utils]")
|
||||
{
|
||||
struct sockaddr_in sock_addr = {};
|
||||
sock_addr.sin_family = AF_INET;
|
||||
sock_addr.sin_port = htons(TEST_PORT_NUMBER); // sock_addr fields are in network byte order
|
||||
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(esp_getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), NULL, 0, buffer, sizeof(buffer), NI_NUMERICSERV) == 0);
|
||||
CHECK(strcmp(TEST_PORT_STRING, buffer) == 0);
|
||||
}
|
||||
|
||||
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(esp_getnameinfo((struct sockaddr *)&sock_addr, sizeof(sock_addr), buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST) != 0);
|
||||
}
|
||||
|
||||
static void test_getifaddr(int expected_nr_of_addrs)
|
||||
{
|
||||
struct ifaddrs *addresses, *addr;
|
||||
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 (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);
|
||||
if (strcmp(addr->ifa_name, "st1") == 0) {
|
||||
CHECK(strcmp("1.2.3.1", buffer) == 0);
|
||||
} else if (strcmp(addr->ifa_name, "st2") == 0) {
|
||||
CHECK(strcmp("1.2.3.2", buffer) == 0);
|
||||
} else {
|
||||
FAIL("unexpected network interface");
|
||||
}
|
||||
}
|
||||
}
|
||||
addr = addr->ifa_next;
|
||||
}
|
||||
// check that we got 1 address with exact content
|
||||
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", 1); // st1
|
||||
REQUIRE(esp_netif != NULL);
|
||||
test_getifaddr(1);
|
||||
esp_netif_t *esp_netif2 = create_test_netif("station2", 2); // st2
|
||||
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(write(write_fd, buffer, 1) > 0);
|
||||
CHECK(write(write_fd, buffer, 1) > 0);
|
||||
CHECK(read(read_fd, buffer, 1) == 1);
|
||||
CHECK(read(read_fd, buffer, 1) == 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());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
7
components/sock_utils/test/host/sdkconfig.defaults
Normal file
7
components/sock_utils/test/host/sdkconfig.defaults
Normal file
@ -0,0 +1,7 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_LWIP_ENABLE=y
|
@ -14,6 +14,7 @@ set(EXTRA_COMPONENT_DIRS
|
||||
../components/console_cmd_wifi
|
||||
../components/console_simple_init
|
||||
../components/mbedtls_cxx
|
||||
../components/sock_utils
|
||||
../components/mdns)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user