Compare commits

...

210 Commits

Author SHA1 Message Date
b8cdd373f4 Merge pull request #809 from david-cermak/fix/mqtt_cxx_clang_tidy_warns
fix(mqtt_cxx): Fix clang-tidy warnings with C++ exceptions
2025-05-06 14:29:57 +02:00
66a5ac0190 Merge pull request #808 from Lapshin/master
fix(mosq): include config.h before any system header (IDFGH-15223)
2025-05-02 10:26:41 +02:00
40cce5e926 fix(mqtt_cxx): Fix clang-tidy warnings with C++ exceptions 2025-05-02 10:18:40 +02:00
1b1ede435d fix(mosq): include config.h before any system header 2025-05-01 23:58:53 +07:00
c6f08ee961 Merge pull request #622 from david-cermak/feat/eppp_emac2emac
[eppp]: Support for transport via Ethernet (phy or emac)
2025-04-30 12:32:48 +02:00
a21ce883ef feat(eppp): Add support for transport via Ethernet link 2025-04-30 11:56:40 +02:00
79d6fa2607 Merge pull request #806 from david-cermak/fix/clang_tidy_disable_lws
fix(lws): Remove clang tidy checks
2025-04-29 14:30:26 +02:00
ad90558420 fix(lws): Remove clang tidy checks
Since lws hardcodes some flags in CMakeLists.txt, which causes compilation issues
with mbedtls, generated lws_config.h and -mlongcalls
2025-04-25 16:50:13 +02:00
8541753443 Merge pull request #781 from espressif-abhikroy/component/esp_dns
feat(common): Add ESP DNS module with support for UDP, DoT, and DoH protocols
2025-04-18 21:37:32 +10:00
6660f71d03 bump(dns): First version [1.0.0]
1.0.0
Features
- Add ESP DNS module with support for UDP, TCP, DoT, and DoH protocols (57cd6080)
2025-04-16 22:03:33 +10:00
57cd60807e feat(dns): Add ESP DNS module with support for UDP, TCP, DoT, and DoH protocols
This commit introduces a custom DNS module for ESP32, enabling DNS resolution capabilities
over various protocols including UDP, TCP, DNS over TLS (DoT), and DNS over HTTPS (DoH).
The module includes initialization and cleanup functionalities, along with
protocol-specific implementations for each DNS type.
2025-04-16 21:30:06 +10:00
03dd8006b2 Merge pull request #798 from david-cermak/fix/lws_clang_tidy
[lws]: Fix many file-not-found errors on clang-tidy job
2025-04-10 17:41:32 +02:00
3863c26b79 Merge pull request #704 from bryghtlabs-richard/fix/websocketWriterLag
fix(websocket): release client-lock during WEBSOCKET_EVENT_DATA (IDFGH-14545)
2025-04-10 12:16:31 -03:00
1d1ff365c2 Merge pull request #797 from david-cermak/fix/asio_certs
[asio]: Fixed TLS server-client example failure
2025-04-10 14:50:37 +02:00
ef3870573c fix(lws): Fix many file-not-found errors on clang-tidy job 2025-04-10 12:49:46 +02:00
ad94cc9502 feat(asio): Add mbedtls specific APIs to use TLS stack specific features
Use mbedtls specific API to configure hostname for verification
2025-04-08 17:08:27 +02:00
4885d28294 fix(asio): Fixed TLS server-client example failure per cert expiry 2025-04-07 12:01:30 +02:00
df00a03084 Merge pull request #685 from david-cermak/fix/modem_generate_code
[modem]: Use pre-generated command declarations to improve IDE navigation
2025-04-04 16:25:27 +02:00
e2fa11103c fix(modem): Use generated AT command definitions for IDE navigation
BREAKING CHANGE: inc headers for AT command definitions are no longer used directly, but pregenerated into *.h(pp)
2025-04-04 15:39:39 +02:00
d2e94e5db2 Merge pull request #609 from david-cermak/fix/example_multinetif
[examples]: Make multi-netif example working with DNS_PER_DEFAULT_NETIF
2025-04-02 07:54:49 +02:00
bc8ac4c684 fix(examples): Make multi-netif example working with DNS_PER_DEFAULT_NETIF feature 2025-04-01 12:39:37 +02:00
b710dbd2ad Merge pull request #793 from david-cermak/fix/modem_bump
[modem]: Bump 1.3.0 -> 1.4.0
2025-03-28 14:57:48 +01:00
5eeb29cb94 bump(modem): 1.3.0 -> 1.4.0
1.4.0
Features
- added config_edrx api function (74b7d85d)
- added sqn_gm02s connect function (b97dfc08)
- add support for sequans GM02S modem (8560f021)
Bug Fixes
- Fix cmux log message (6ed672da)
- fixed minor code mistakes. (317faf89)
- handle nullptr in DTE constructors to prevent invalid access (95b56600)
2025-03-28 13:47:52 +01:00
a00117fbdb Merge pull request #790 from david-cermak/fix/modem_cmux_log_pr
[modem]: Fix cmux log message
2025-03-28 08:51:37 +01:00
6ed672da7b fix(modem): Fix cmux log message 2025-03-26 18:20:03 +01:00
1b4baaed0e Merge pull request #786 from robbedptechnics/support-sqn-gm02s
feat(module): add support for sequans GM02S modem (DPTechnics) (IDFGH-14891)
2025-03-25 12:24:51 +01:00
74b7d85d4a feat(modem): added config_edrx api function 2025-03-25 11:28:16 +01:00
317faf89ff fix(modem): fixed minor code mistakes. 2025-03-25 11:27:14 +01:00
b97dfc08b8 feat(modem): added sqn_gm02s connect function 2025-03-25 11:25:07 +01:00
e9d7350219 Merge pull request #784 from david-cermak/fix/mdns_fuzz_issue
[mdns]: Fix parsing incorrect txt records
2025-03-21 13:11:28 +01:00
61da30b013 bump(mdns): 1.8.1 -> 1.8.2
1.8.2
Bug Fixes
- Fix parsing incorrect txt records (8fd2c99f)
2025-03-21 12:40:42 +01:00
8fd2c99f15 fix(mdns): Fix parsing incorrect txt records
Issue discovered when fuzzing packet parser,
received packet with inconsistent txt section caused issues on final cleanup
2025-03-21 12:40:12 +01:00
8560f02191 feat(module): add support for sequans GM02S modem 2025-03-19 14:45:32 +01:00
703c49297f Merge pull request #785 from david-cermak/fix/mosq_test
[mosq]: Fix pytest to use latest embedded packages
2025-03-19 10:25:00 +01:00
2fe8345fa5 fix(mosq): Fix pytest to use latest embedded packages 2025-03-19 08:49:22 +01:00
3fc26a5e5c Merge pull request #780 from david-cermak/fix/mdns_task_delete_race
[mdns]: Fix potential task delete race
2025-03-17 11:31:00 +01:00
5db6be7422 bump(mdns): 1.8.0 -> 1.8.1
1.8.1
Bug Fixes
- Fix potential task delete race (8ca45f34)
2025-03-17 10:51:14 +01:00
8ca45f34fa fix(mdns): Fix potential task delete race
Need to wait for the task to be deleted before destroy its stack
2025-03-17 10:51:02 +01:00
2e28774228 Merge pull request #775 from euripedesrocha/feature/mosquitto_plugin
Add config to enable SYS on mosquito and bump version
2025-03-06 14:48:20 -03:00
e14e21ab32 bump(mosq): 2.0.20~1 -> 2.0.20~2
##
[2.0.20~2](https://github.com/espressif/esp-protocols/commits/mosq-v2.0.20_2)###
Features- Allow user to enable SYS topic
([905b84fb](https://github.com/espressif/esp-protocols/commit/905b84fb))###
Bug Fixes- Remove temp modification of IDF files
([9162de11](https://github.com/espressif/esp-protocols/commit/9162de11))-
Add a note about stack size
([dbd164dd](https://github.com/espressif/esp-protocols/commit/dbd164dd))
2025-03-06 11:14:26 +01:00
6f00c369fc Merge pull request #774 from david-cermak/ci/fix_deprecated_containers
ci(common): Remove deprecated CI runners on ubuntu-20.04
2025-03-05 15:52:35 +01:00
905b84fb97 feat(mosq): Allow user to enable SYS topic
Mosquitto provides several topics under $SYS/# to capture logs and load
statistics. This allows users to enable it.
2025-03-05 15:03:59 +01:00
d7f5322a39 ci(common): Remove deprecated CI runners on ubuntu-20.04 2025-03-04 16:11:51 +01:00
eb12d05bf3 Merge pull request #773 from david-cermak/fix/mdns_add_version_macro
[mdns]: Bump 1.7.0 -> 1.8.0
2025-03-04 15:59:54 +01:00
520b8194fc fix(common): Fix clang-tidy runner to use supported versions 2025-03-04 15:29:38 +01:00
40142cdcfd bump(mdns): 1.7.0 -> 1.8.0
1.8.0
Features
- Add version keys (e01e67e7)
Bug Fixes
- Reformat mdns sources per indent-cont=120 (c7663cde)
2025-03-04 15:20:49 +01:00
c7663cdef2 fix(mdns): Reformat mdns sources per indent-cont=120 2025-02-27 10:42:03 +01:00
e01e67e7eb feat(mdns): Add version keys 2025-02-27 10:33:53 +01:00
37f84ee5a4 Merge pull request #768 from deodatomatheus/fix/handle-nullptr-dte-constructors
fix(modem): handle nullptr in DTE constructors to prevent invalid access (IDFGH-14688)
2025-02-27 09:19:16 +01:00
95b56600ed fix(modem): handle nullptr in DTE constructors to prevent invalid access 2025-02-26 11:37:02 -03:00
a22c3da49e Merge pull request #762 from david-cermak/fix/examples_target_tests
[examples]: Test the SLIP netif example on target as well
2025-02-20 14:32:54 +01:00
5d0fd5c1c5 Merge pull request #770 from david-cermak/fix/lws_license
[lws]: Adds missing license info
2025-02-20 14:17:47 +01:00
7ea6879a19 fix(lws): Adds missing license info 2025-02-20 12:34:40 +01:00
2db11bbb8c fix(examples): Fix SLIP netif example to make esp_netif use special netif
by enabling PPP netif, so esp_netif knows that lwip's netif->state
will be reserved for special netif info (slip)

Closes https://github.com/espressif/esp-protocols/issues/759
2025-02-20 11:58:54 +01:00
cdb7bfd188 fix(examples): Test the SLIP netif example on target as well 2025-02-20 11:58:49 +01:00
c679ceed85 Merge pull request #718 from glmfe/feat/lws
[lws]: Add initial support for libwebsockets
2025-02-19 11:26:03 -03:00
c009892968 bump(lws): First version 4.3.3
4.3.3
Features
- Add initial support libwebsockets component (ef3443d)
2025-02-19 10:21:35 -03:00
5f66f35f5f feat(lws): Add initial support libwebsockets component 2025-02-19 10:16:42 -03:00
dea5f1c431 Merge pull request #764 from david-cermak/feat/mdns_malloc_caps
[mdns]: Allow allocate memory with configured caps
2025-02-18 13:23:41 +01:00
384d1c23ba bump(mdns): 1.6.0 -> 1.7.0
1.7.0
Features
- Support user defined allocators (88162d1f)
- Allow allocate memory with configured caps (7d29b476)
Bug Fixes
- Adjust some formatting per indent-cont=120 (5b2077e3)
2025-02-18 12:53:46 +01:00
88162d1f3a feat(mdns): Support user defined allocators
Defines mem-alloc function optionally weak, so users can override them
and implement their own allocation, or a static/bss memory for the mdns
task stack.
2025-02-18 12:52:21 +01:00
e65f22ab6c ci(mdns): Check mdns sources for std alloc functions 2025-02-13 15:02:18 +01:00
7d29b47676 feat(mdns): Allow allocate memory with configured caps 2025-02-13 13:51:22 +01:00
f1a72ec42c Merge pull request #760 from glmfe/feat/add-ws-unregister
feat(websocket): add unregister event to websocket client
2025-02-12 18:50:52 +01:00
ce160505dc feat(websocket): add unregister event to websocket client 2025-02-12 08:46:39 -03:00
87e96b4682 Merge pull request #761 from david-cermak/fix/ci_astyle
[ci]: Fix pre-commit hook to call astyle correctly
2025-02-12 11:47:40 +01:00
5b2077e373 fix(common): Adjust some formatting per indent-cont=120
As we updated astyle configuration to be in line with IDF style
2025-02-12 11:40:21 +01:00
030cb75ece fix(websocket): release client-lock during WEBSOCKET_EVENT_DATA
This resolves:

 1) Deadlock when trying to reserve a lock in WEBSOCKET_EVENT_DATA,
    but lock is held by a thread trying to send a websocket message.
 2) High latency caused by writers serialized with WEBSOCKET_EVENT_DATA
    while calling esp_websocket_client_send(), even when TCP window
    has enough space for the entire message being queued to send.

Multiple writers are still serialized at fragment boundaries, but
only with other writers and websocket error updates.

Fixes #625
2025-02-11 12:04:39 -06:00
b45fe143a4 ci(common): Fix pre-commit hook to call astyle correctly 2025-02-11 17:16:11 +01:00
ee2fbbbee7 Merge pull request #756 from david-cermak/feat/mdns_alloc_task
[mdns]: support allocating mDNS task with caps
2025-02-10 11:50:59 +01:00
cb061c9c38 bump(mdns): 1.5.3 -> 1.6.0
1.6.0
Features
- support allocating mDNS task from SPIRAM (8fcad10c)
Bug Fixes
- Use correct task delete function (eb4ab524)
Updated
- ci(mdns): Fix mdns host test layers with static task creation (0690eba3)
2025-02-10 11:18:55 +01:00
0690eba3a8 ci(mdns): Fix mdns host test layers with static task creation 2025-02-10 11:18:19 +01:00
eb4ab52487 fix(mdns): Use correct task delete function 2025-02-10 11:18:19 +01:00
zwx
8fcad10ccf feat(mdns): support allocating mDNS task from SPIRAM 2025-02-10 11:18:19 +01:00
936e43f9d8 Merge pull request #758 from david-cermak/fix/mdns_ignore_only_invalid_queries
[mdns]: Fix the responder to ignore only invalid queries
2025-02-07 15:11:14 +01:00
64d818b2d3 bump(mdns): 1.5.2 -> 1.5.3
1.5.3
Bug Fixes
- Fix responder to ignore only invalid queries (cd07228f, #754)
2025-02-07 14:31:18 +01:00
cd07228f81 fix(mdns): Fix responder to ignore only invalid queries
not the entire packet, so we can still reply to next questions

Closes https://github.com/espressif/esp-protocols/issues/754
2025-02-07 14:30:10 +01:00
1c6580e22b Merge pull request #753 from david-cermak/fix/ci_mosq
[mosquitto]: Fix minor CI issue
2025-02-03 14:54:07 +01:00
87f835af0f fix(mosq): Run IDF build test only on master or labeled PRs 2025-01-31 11:19:00 +01:00
85a8dac42d Merge pull request #750 from david-cermak/bump/ws_client
[websocket]:  Bump 1.3.0 -> 1.4.0
2025-01-30 08:48:45 +01:00
39866116f5 bump(websocket): 1.3.0 -> 1.4.0
1.4.0
Features
- Support DS peripheral for mutual TLS (55385ec3)
Bug Fixes
- wait for task on destroy (42674b49)
- Fix pytest to verify client correctly (9046af8f)
- propagate error type (eeeb9006)
- fix example buffer leak (5219c39d)
Updated
- chore(websocket): align structure members (beb6e57e)
- chore(websocket): remove unused client variable (15d3a01e)
2025-01-30 07:16:24 +01:00
7740b591b6 Merge pull request #749 from david-cermak/fix/ws_client_test
[websocket]: Fix pytest to verify client correctly
2025-01-30 07:09:34 +01:00
b57979d967 Merge pull request #751 from johanstokking/fix/await-task-on-destroy
fix(websocket): wait for task on destroy (IDFGH-14533)
2025-01-29 15:27:38 +01:00
42674b49f9 fix(websocket): wait for task on destroy 2025-01-29 14:30:32 +01:00
e069ae7762 Merge pull request #520 from johanstokking/feature/websocket/client_ds_data
feat(websocket): Support DS peripheral for mutual TLS (IDFGH-12285)
2025-01-28 16:59:34 +01:00
44d476fc50 docs(websocket): fix minor readability issues 2025-01-28 14:59:24 +01:00
55385ec312 feat(websocket): Support DS peripheral for mutual TLS 2025-01-28 14:58:54 +01:00
a3c2bbed9e Merge pull request #748 from david-cermak/fix/mdns_coverity
[mdns]: Bump v1.5.2
2025-01-28 10:23:24 +01:00
84c47c37f1 bump(mdns): 1.5.1 -> 1.5.2
1.5.2
Bug Fixes
- Fix potential NULL deref when sending sub-buy (e7273c46)
- Fix _mdns_append_fqdn excessive stack usage (bd23c233)
2025-01-28 09:54:54 +01:00
e7273c46ec fix(mdns): Fix potential NULL deref when sending sub-buy
Closes coverity reported issue: 473829 Dereference null return value
2025-01-28 09:54:16 +01:00
2e7d240abd Merge pull request #744 from andrew-lifx/fix/mdns_append_fqdn
fix(mdns): Fix _mdns_append_fqdn excessive stack usage (IDFGH-14506)
2025-01-28 07:28:00 +01:00
d4a004b5b4 Merge pull request #694 from johanstokking/fix/websocket-esp-tls-errors
fix(websocket): propagate error type (IDFGH-14518)
2025-01-27 15:50:32 +01:00
9046af8f8d fix(websocket): Fix pytest to verify client correctly 2025-01-27 15:34:49 +01:00
eeeb9006eb fix(websocket): propagate error type 2025-01-25 21:52:06 +01:00
b167aa315f Merge pull request #747 from david-cermak/fix/publish_asio_1.32
bump(asio): Publish 1.32.0 to component manager
2025-01-24 15:53:54 +01:00
72ba24470d bump(asio): Publish 1.32.0 to component manager
Related to the bump-commit: ac6a388cdd
but missed to update idf_component.yml
This publishes ASIO-1.32-0~0
2025-01-24 14:32:20 +01:00
7dc87d28b2 Merge pull request #717 from david-cermak/feat/asio_sync_upstream
[asio]: Drop esp-asio patches in favor of sock-utils
2025-01-24 13:49:53 +01:00
ac6a388cdd bump(asio): 1.28.0 -> 1.32.0
1.32.0
Features
- Upgrade asio to 1.32 (9bdd429c)
- Drop esp/asio patches in favor of sock-utils (27435b7f)
Bug Fixes
- Fix chat example to print only the message body (76aaea08)
- Make asio enable if_nametoindex to fix linking (5db32cce)
- Re-applie refs to common comps idf_component.yml (9fe44a45)
- Reference common component from IDF (74fc228c)
- Revert referencing protocol_examples_common from IDF (f9e0281a)
- reference protocol_examples_common from IDF (09abb18b)
- specify override_path in example manifest files (1d8923cf)
Updated
- docs(asio): Updates asio docs (ce9337d3)
2025-01-24 12:40:45 +01:00
76aaea08d2 fix(asio): Fix chat example to print only the message body 2025-01-24 12:37:39 +01:00
5db32cce30 fix(asio): Make asio enable if_nametoindex to fix linking 2025-01-24 12:37:39 +01:00
9bdd429c7c feat(asio): Upgrade asio to 1.32 2025-01-24 10:47:54 +01:00
6f7c52cc3f fix(asio): Add tests for IDF-v5.4 2025-01-24 10:47:54 +01:00
27435b7f34 feat(asio): Drop esp/asio patches in favor of sock-utils 2025-01-24 10:47:54 +01:00
813331f003 Merge pull request #743 from david-cermak/fix/modem_ci
[modem]: Fix deprecated CI download action
2025-01-24 08:27:37 +01:00
bd23c233a4 fix(mdns): Fix _mdns_append_fqdn excessive stack usage
Move "mdns_name_t name" declaration inside loop to move it out of scope and reclain stack space before recursion call.
2025-01-24 17:39:04 +10:30
4eda7d472f fix(modem): Fix deprecated download action 2025-01-23 19:15:28 +01:00
163029c0b6 Merge pull request #742 from david-cermak/bump/mdns_1.5.1
[mdns]: Bump 1.5.0 -> 1.5.1
2025-01-23 15:32:49 +01:00
96eae25096 bump(mdns): 1.5.0 -> 1.5.1
1.5.1
Bug Fixes
- Fix incorrect memory free for mdns browse (4451a8c5)
2025-01-23 09:00:01 +01:00
ebec8eff63 Merge pull request #740 from gytxxsy/fix/fix_wrong_mem_free_of_mdns_browse
[mdns]: fix incorrect memory free for mdns browse
2025-01-23 08:58:05 +01:00
4451a8c5ad fix(mdns): Fix incorrect memory free for mdns browse 2025-01-23 09:08:25 +08:00
6d19aabb02 Merge pull request #721 from david-cermak/feat/mosq_target_tests
[mosq]: Add IDF MQTT stress tests to mosquitto CI
2025-01-21 12:55:49 +01:00
9162de1150 fix(mosq): Remove temp modification of IDF files 2025-01-21 12:27:09 +01:00
dbd164dd91 fix(mosq): Add a note about stack size 2025-01-21 12:27:09 +01:00
90d663ad01 feat(mosq): Add IDF MQTT stress tests to mosquitto CI 2025-01-21 12:27:09 +01:00
a83f1b6787 Merge pull request #736 from david-cermak/bump/mdns_1.4.4
[mdns]: Bump 1.4.3 -> 1.5.0
2025-01-21 10:56:48 +01:00
84caca465d bump(mdns): 1.4.3 -> 1.5.0
1.5.0
Features
- supported removal of subtype when updating service (4ad88e29)
Bug Fixes
- Fix zero-sized VLA clang-tidy warnings (196198ec)
- Remove dead store to arg variable shared (e838bf03)
- Fix name mangling not to use strcpy() (99b54ac3)
- Fix potential null derefernce in _mdns_execute_action() (f5be2f41)
- Fix AFL test mock per espressif/esp-idf@a5bc08fb55 (3d8835cf)
- Fixed potential out-of-bound interface error (24f55ce9)
- Fixed incorrect error conversion (8f8516cc)
- Fixed potential overflow when allocating txt data (75a8e864)
- Move MDNS_NAME_BUF_LEN to public headers (907087c0, #724)
- Cleanup includes in mdns.c (68a9e148, #725)
- Allow advertizing service with port==0 (827ea65f)
- Fixed complier warning if MDNS_MAX_SERVICES==0 (95377216, #611)
2025-01-20 17:57:07 +01:00
8f81478fff Merge pull request #735 from david-cermak/fix/docs_build
[common]: Fix esp-docs dependencies
2025-01-20 14:50:32 +01:00
ae5a8ceeda fix(mosq): Run mosquitto version check only on labeled job or push 2025-01-20 12:56:02 +01:00
774bab22ea fix(common): Update esp-docs dependencies to fix docs-build job 2025-01-20 12:52:52 +01:00
265e38d684 Merge pull request #731 from tanyanquan/feat/update_subtype_removal
feat(mdns): supported removal of subtype when updating service (IDFGH-14413)
2025-01-20 09:48:27 +01:00
93f772171c Merge pull request #732 from david-cermak/fix/mdns_clangtiy_warns
[mdns]: Fixes clang-tidy warnings
2025-01-20 09:47:56 +01:00
4ad88e297f feat(mdns): supported removal of subtype when updating service 2025-01-16 10:56:54 +08:00
196198ecc9 fix(mdns): Fix zero-sized VLA clang-tidy warnings
and some minor leaks in creation of browse results
2025-01-15 14:23:34 +01:00
e838bf03f4 fix(mdns): Remove dead store to arg variable shared
Fixing: warning: Value stored to 'shared' is never read [clang-analyzer-deadcode.DeadStores]
2025-01-15 14:23:34 +01:00
99b54ac384 fix(mdns): Fix name mangling not to use strcpy()
Since it was flagged by clang-tidy as insecture API
2025-01-15 14:23:34 +01:00
f5be2f4115 fix(mdns): Fix potential null derefernce in _mdns_execute_action()
We did check for null-deref before checking 'a->type', but contol
continues and passes potential null-ptr to the processing function
_mdns_execute_action()
Fixed by asserting action != NULL, since it is an invalid state which
should never happen.
2025-01-15 14:21:59 +01:00
9b74256b51 Merge pull request #730 from david-cermak/fix/mdns_minor
[mdns]: Fixed some minor bugs
2025-01-15 12:27:05 +01:00
3d8835cfb9 fix(mdns): Fix AFL test mock per espressif/esp-idf@a5bc08fb55 2025-01-15 11:42:34 +01:00
24f55ce9b4 fix(mdns): Fixed potential out-of-bound interface error
invalid mdns_if was handled for enabling/announcing pcbs,
but not for the consequent browsing

Closes coverity isssue: 470162 Out-of-bounds access
2025-01-15 10:46:15 +01:00
8f8516cc3f fix(mdns): Fixed incorrect error conversion
Mixing esp_err_t (int) with err_t (uint8_t) from lwip.

Closes coverity isssue: 470139 Overflowed return value
2025-01-15 10:46:15 +01:00
75a8e8640a fix(mdns): Fixed potential overflow when allocating txt data
Closes coverity warning: 470092 Overflowed integer argument
2025-01-15 10:46:15 +01:00
907087c09b fix(mdns): Move MDNS_NAME_BUF_LEN to public headers
Since it's used by public API as maximum length of user buffer

Closes https://github.com/espressif/esp-protocols/issues/724
2025-01-15 10:46:10 +01:00
68a9e14898 fix(mdns): Cleanup includes in mdns.c
Closes https://github.com/espressif/esp-protocols/issues/725
2025-01-15 10:44:12 +01:00
827ea65fd5 fix(mdns): Allow advertizing service with port==0
Closes https://github.com/espressif/esp-idf/issues/14335
2025-01-15 10:44:12 +01:00
9537721600 fix(mdns): Fixed complier warning if MDNS_MAX_SERVICES==0
Closes https://github.com/espressif/esp-protocols/issues/611
2025-01-15 10:44:07 +01:00
4394f845fc Merge pull request #728 from david-cermak/bump/mdns_1.4.3
[mdns]: bump: 1.4.2 -> 1.4.3
2025-01-08 10:24:01 +01:00
9b0ba6060f bump(mdns): 1.4.2 -> 1.4.3
1.4.3
Features
- support zero item when update subtype (5bd82c01)
2025-01-08 09:42:38 +01:00
7c6a3098af Merge pull request #726 from zwx1995esp/feature/support_update_zero_item_of_subtype_for_mdns
feat(mdns): support zero item when update subtype (IDFGH-14369)
2025-01-07 18:40:25 +01:00
f3f3e23bec Merge pull request #727 from david-cermak/fix/sockutls_gai_error
fix(sockutls): Fix gai_strerror() impl to return const string
2025-01-07 15:59:24 +01:00
9ed835ba3f bump(sockutls): 0.2.1 -> 0.2.2
0.2.2
Bug Fixes
- Fix gai_strerror() impl to return const string (f12a2056)
2025-01-07 14:06:42 +01:00
f12a205657 fix(sockutls): Fix gai_strerror() impl to return const string
Previous gai_strerror() returned numeric representation of the code,
but used TLS storage, which might cause issues with stack sizes on all
tasks in the system. Alternatively we can leave the storage to static
only (which wouldn't be thread-safe) or we could one-time allocate and
never free (which is wrong).
This option uses hardcoded strings for common error codes used in lwip.
The disadvantage is that we might need to update the impl in the future
when lwip adds more codes.
2025-01-07 14:06:00 +01:00
zwx
5bd82c01a5 feat(mdns): support zero item when update subtype 2025-01-07 10:28:02 +08:00
b4cb8f8a66 Merge pull request #720 from david-cermak/fix/sock_utils_define_conflict
[sock-utils] Fix potential macro conflict
2024-12-20 16:36:08 +01:00
0499ed93df bump(sockutls): 0.2.0 -> 0.2.1
0.2.1
Bug Fixes
- Fix potential macro colission including standard headers (ade9448c)
2024-12-20 15:41:10 +01:00
ade9448c01 fix(sockutls): Fix potential macro colission including standard headers 2024-12-20 15:40:54 +01:00
4745fc8fe1 Merge pull request #719 from david-cermak/bump/mosq_2.2.0_1
[mosq]:  Bump: v2.0.20 -> 2.0.20~1
2024-12-20 15:31:13 +01:00
73e523e736 bump(mosq): 2.0.20 -> 2.0.20~1
2.0.20~1
Bug Fixes
- Use sock_utils instead of func stubs (3cd0ed37)
- Update API docs adding on-message callback (5dcc3330)
2024-12-20 15:09:41 +01:00
3cd0ed377b fix(mosq): Use sock_utils instead of func stubs 2024-12-20 15:07:42 +01:00
95294f5f89 fix(mosq): Add consistency check for api docs and versions 2024-12-20 15:07:37 +01:00
5dcc33300f fix(mosq): Update API docs adding on-message callback 2024-12-20 12:09:48 +01:00
840a561de4 Merge pull request #710 from david-cermak/feat/mosq_p2p_example
[mosq]: Add serverless broker example
2024-12-19 17:11:13 +01:00
e6fb8aa078 bump(mosq): 2.0.18~0 -> 2.0.20~0
2.0.20~0
Features
- Upgrade to mosquitto v2.0.20 (3b2c614d)
- Add support for on-message callback (cdeab8f5)
- Add example with two brokers synced on P2P (d57b8c5b)
Bug Fixes
- Fix dependency issues moving esp-tls to public deps (6cce87e4)
2024-12-19 16:51:49 +01:00
3b2c614d86 feat(mosq): Upgrade to mosquitto v2.0.20
Used tagged version v2.0.20
2024-12-19 16:41:37 +01:00
cdeab8f517 feat(mosq): Add support for on-message callback 2024-12-19 16:41:37 +01:00
6cce87e465 fix(mosq): Fix dependency issues moving esp-tls to public deps
Since esp-tls structs are using in public header files
2024-12-19 16:41:37 +01:00
d57b8c5b29 feat(mosq): Add example with two brokers synced on P2P
Broker-less two chip example which virtual private IoT
networks on MQTT protocol.
2024-12-19 16:40:02 +01:00
9c11003449 Merge pull request #713 from david-cermak/feat/sock_utiis_0.2
[sock-utils]: Bump 0.1 -> 0.2
2024-12-19 11:13:59 +01:00
85a7fc772c bump(sockutls): 0.1.0 -> 0.2.0
0.2.0
Features
- Declare socketpair and gai_strerror via standard headers (b090a3cb)
- Add support for gethostname() (f7c0b756)
2024-12-19 10:57:36 +01:00
b090a3cb69 feat(sockutls): Declare socketpair and gai_strerror via standard headers
Adding a reverse dependency to lwip and define macros, which
enable declarations of socketpair() and gai_strerror() in standard
heders (sys/socket.h and netdb.h)
2024-12-19 10:54:56 +01:00
42cde46c97 Merge pull request #714 from bryghtlabs-richard/chore/websocketTidying
Chore(websocket) tidy up two small issues
2024-12-17 11:35:57 +01:00
beb6e57e5e chore(websocket): align structure members 2024-12-16 16:20:21 -06:00
15d3a01e11 chore(websocket): remove unused client variable 2024-12-16 15:46:43 -06:00
e12ecb8e89 Merge pull request #707 from david-cermak/bugfix/sckutls_gethostname
[sock-utils]: Add support for gethostname()
2024-12-16 16:36:32 +01:00
54271a1b96 Merge pull request #709 from david-cermak/bump/modem_1.3
[modem]: bump 1.2.1 -> 1.3.0
2024-12-12 09:46:27 +01:00
886215032f bump(modem): 1.2.1 -> 1.3.0
1.3.0
Features
- Add mode detection to the example (18f196fa)
- Support for pausing network in C-API (1db83cd1)
- Add support for pausing netif (247f1681, #699)
Bug Fixes
- Minor cleanup of pppos example (5e929902)
- Fix PPP mode detection to accept LCP/conf (c989c6ad)
- Refine mode switch data->command (8b6ea331, #692)
- Detect serial ports properly (0cb59ff8)
- Fix CMUX enter to ignore URC before transition (1284f66d, #669)
2024-12-10 12:48:09 +01:00
269351f41c Merge pull request #700 from david-cermak/feat/modem_pause_network
[modem]: Add support for pausing netif
2024-12-10 12:14:10 +01:00
5e929902c7 fix(modem): Minor cleanup of pppos example
* Use CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT to demonstrate mode
detect
* Use disconnection flag to indicate conneciton issue and gracefully
degrade to command mode
* Remove IDF-verion < v5.0 code
2024-12-10 11:36:10 +01:00
f7c0b7564a feat(sockutls): Add support for gethostname()
Closes https://github.com/espressif/esp-idf/issues/14849
2024-12-09 17:16:59 +01:00
c989c6adae fix(modem): Fix PPP mode detection to accept LCP/conf 2024-12-06 10:12:25 +01:00
18f196fa1e feat(modem): Add mode detection to the example 2024-12-06 09:48:00 +01:00
1db83cd1ca feat(modem): Support for pausing network in C-API
also adds a demo of this feature to pppos client example
2024-12-05 20:16:45 +01:00
247f1681e8 feat(modem): Add support for pausing netif
Closes https://github.com/espressif/esp-protocols/issues/699
2024-12-05 20:16:41 +01:00
32387f7e39 Merge pull request #702 from david-cermak/fix/modem_data_cmd_mode_refine
[modem]: Refine data -> command transition
2024-12-04 15:42:43 +01:00
dbc3ea6809 fix(common): Export IDF environment in bash shell
To avoid issues with IDF_PATH/export with the latest tools
2024-12-04 14:44:43 +01:00
8b6ea3311a fix(modem): Refine mode switch data->command
* netif.stop() moved after setting the transition callback
* send PPP escape sequence if enabled before waiting for transition to
complete
* add newline character before sync() command (after the "+++")

Closes https://github.com/espressif/esp-protocols/issues/692
2024-12-04 14:39:01 +01:00
8e55b93b59 Merge pull request #703 from david-cermak/fix/modem_cmux_data_before_switch
[modem]: CMUX: ignore URC before entering CMUX
2024-12-04 08:18:54 +01:00
0cb59ff80d fix(modem): Detect serial ports properly 2024-11-29 18:27:26 +01:00
1284f66d58 fix(modem): Fix CMUX enter to ignore URC before transition
Closes https://github.com/espressif/esp-protocols/issues/669
2024-11-29 17:32:35 +01:00
c5b49de2db Merge pull request #502 from espressif-abhikroy/component/console_cmd_mqtt
feat(console): Added component with mqtt command
2024-11-29 21:53:01 +11:00
2e9bb6ee45 bump(console): First version [1.0.0]
1.0.0
Features
- Added component with mqtt command (1fcc5b1d)
2024-11-29 21:48:50 +11:00
1fcc5b1d56 feat(console): Added component with mqtt command 2024-11-29 00:59:27 +11:00
849fe7b6cb Merge pull request #698 from david-cermak/fix/modem_minor_fixes_on_1.2
[modem]: Support for URC handler in C-API -> v1.2.1
2024-11-20 16:28:39 +01:00
5eadf1edee bump(modem): 1.2.0 -> 1.2.1
1.2.1
Bug Fixes
- Use higher GPIO range to support new chips (428fdbbd, #558)
- Remove tests and support for IDFv4.4, added IDFv5.4 (433a033f)
- Fix typo GENETIC -> GENERIC in mode types (090b1ff8, #667)
- Add support for URC handler into C-API (295d99df, #180)
2024-11-20 15:47:03 +01:00
428fdbbd80 fix(modem): Use higher GPIO range to support new chips
We can use IDF's Kconfig.env_caps in future, but that's not available on v5.0.

Closes https://github.com/espressif/esp-protocols/issues/558
2024-11-20 11:36:00 +01:00
433a033fcc fix(modem): Remove tests and support for IDFv4.4, added IDFv5.4 2024-11-20 11:35:33 +01:00
090b1ff845 fix(modem): Fix typo GENETIC -> GENERIC in mode types
Closes https://github.com/espressif/esp-protocols/issues/667
2024-11-20 10:25:20 +01:00
295d99df96 fix(modem): Add support for URC handler into C-API
Closes https://github.com/espressif/esp-protocols/issues/180
2024-11-20 10:15:22 +01:00
b65cff3a0b Merge pull request #696 from david-cermak/bump/mdsn_1.4.2
[mdns]: Bump to v1.4.2
2024-11-14 15:31:45 +01:00
e711f26670 bump(mdns): 1.4.1 -> 1.4.2
1.4.2
Features
- support update subtype (062b8dca)
Updated
- chore(mdns): Add more info to idf_component.yml (4a1cb65c)
2024-11-14 12:04:25 +01:00
4a1cb65c67 chore(mdns): Add more info to idf_component.yml
Fixing component manager warnings:
WARNING: A component description has not been provided in the manifest file. Please update your  file and include a brief description of the component.This will help other developers understand the purpose and functionality of your component. Documentation: https://***/projects/idf-component-manager/en/latest/guides/packaging_components.html#create-idf-component-yml
WARNING: A homepage URL has not been provided in the manifest file. Please update your  file and include the URL to the component's homepage. Documentation: https://***/projects/idf-component-manager/en/latest/guides/packaging_components.html#create-idf-component-yml
2024-11-14 12:03:56 +01:00
6c61dd39cf Merge pull request #693 from zwx1995esp/feat/support_add_remove_subtype
feat(mdns): support update subtype (IDFGH-14068)
2024-11-14 10:36:22 +01:00
8821ea3a99 Merge pull request #695 from david-cermak/bump/mdns_1.4.1
bump(mdns): 1.4.0 -> 1.4.1
2024-11-14 10:32:22 +01:00
zwx
062b8dcacc feat(mdns): support update subtype 2024-11-14 11:07:43 +08:00
7de57bb412 bump(mdns): 1.4.0 -> 1.4.1
1.4.1
Features
- Send PTR query for mdns browse when interface is ready (010a404a)
Bug Fixes
- Prevent deadlock when deleting a browse request (3f48f9ea)
- Fix use after free reported by coverity (25b3d5fd)
- Fixed dead-code reported by coverity (11846c7d)
2024-11-13 17:47:19 +01:00
67191f3bb5 Merge pull request #671 from david-cermak/feat/sock_utils
[sock-utils]: Add initial support for socket helpers
2024-11-11 18:29:45 +01:00
6d94ad646d bump(sockutls): Initial version 0.1.0
0.1.0
Features
- Add initial support for socket helpers (31f57ad0)
2024-11-11 16:47:57 +01:00
685d47cd2f fix(common): Disable clang-check for unsecure/deprecated APIs 2024-11-11 16:47:18 +01:00
31f57ad067 feat(sockutls): Add initial support for socket helpers 2024-11-11 16:47:13 +01:00
32ac21b03c Merge pull request #688 from david-cermak/fix/modem_docs_limitations
[modem]: bump: 1.1.0 -> 1.2.0
2024-11-11 16:26:41 +01:00
5b06a3b319 bump(modem): 1.1.0 -> 1.2.0
1.2.0
Features
- Add support for guessing mode (52598e5f)
- Delete CMUX internal implementation even if terminal exit fails (0e0cbd6b)
- Add support for handling URC (1b6a3b3b, #180)
- add ability to change ESP_MODEM_C_API_STR_MAX from Kconfig (17909892)
- Added target test config with CHAP authentication (f8ae7def)
- example add esp32p4 usb support (adafeae5)
- Publish mbedtls component (0140455f)
- host test support of the latest ESP-IDF release (3f74b4e8)
Bug Fixes
- Fix console example to use urc/detect features (1a9eaf3e)
- Update target test builds to use external Catch2 (554f022c)
- Fix arguments names when spawn esp_modem_xxx declarations (b6792c52)
- Remove catch dependency (c3480768)
- Examples: use local configs for MQTT topic/data (f5c13b92)
- Fixed clang-tidy warnings (70fa3af7)
- Fix CI build per IDFv5.3 (d0c17ef0)
- Fixed UART task to check for buffered data periodically (4bdd90cc, #536)
- Cleanup unused configs from PPPoS example (08a62ccc)
- Update CMUX example with SIM7070_gnss cleaned-up (56fe5327)
- Update console example with SIM7070_gnss format comments (5baaf542)
- Fix remaining print format warnings (3b80181d)
Updated
- docs(modem): Fix esp_modem_at_raw() description (C-API) (492a6a00)
- ci(common): updated github actions(checkout, upload, download) v3 to 4, Ubuntu 20.04 to v22.04 (a23a0027)
2024-11-07 12:42:11 +01:00
cc2741d4ad fix(modem): Document CMUX compatibility issue with CAVLI C16QS
Closes https://github.com/espressif/esp-protocols/issues/507
2024-11-07 12:41:47 +01:00
c5653ff204 Merge pull request #677 from david-cermak/fix/docs_links
[asio]: Fix links in documentation
2024-11-06 14:40:23 +01:00
77731c9b36 Merge pull request #678 from david-cermak/fix/modem_docs
[modem]: Fix docs to link tests correctly
2024-11-06 14:40:05 +01:00
2442f6b553 Merge pull request #612 from david-cermak/feat/modem_mode_detect
[modem]: Add support for guessing mode
2024-11-06 14:39:37 +01:00
0b5e362a7b Merge pull request #686 from bryghtlabs-richard/fix/wsExampleLeak
fix(websocket): fix example buffer leak
2024-11-05 17:11:35 +01:00
5219c39d09 fix(websocket): fix example buffer leak 2024-11-04 08:39:13 -06:00
1a9eaf3e98 fix(modem): Fix console example to use urc/detect features 2024-11-01 15:31:39 +01:00
52598e5f03 feat(modem): Add support for guessing mode 2024-11-01 13:49:52 +01:00
2d9759265b fix(modem): Fix docs to link tests correctly
Closes https://github.com/espressif/esp-protocols/issues/664
2024-10-25 12:56:08 +02:00
8f1f935858 fix(asio): Fix docs to link examples correctly 2024-10-25 12:43:19 +02:00
311 changed files with 15833 additions and 1596 deletions

View File

@ -13,7 +13,7 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
idf_target: ["esp32", "esp32s2"]
example: ["asio_chat", "async_request", "socks4", "ssl_client_server", "tcp_echo_server", "udp_echo_server"]
runs-on: ubuntu-22.04
@ -64,7 +64,7 @@ jobs:
name: Target tests
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
idf_target: ["esp32"]
example: ["asio_chat", "tcp_echo_server", "udp_echo_server", "ssl_client_server"]
needs: build_asio

View File

@ -9,12 +9,12 @@ on:
jobs:
build:
name: Run clang-tidy
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
container: espressif/idf:latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: 'true'
submodules: recursive
- name: Install esp-clang
run: |
${IDF_PATH}/tools/idf_tools.py --non-interactive install esp-clang
@ -24,6 +24,7 @@ jobs:
chmod +x clang-tidy-sarif
curl -sSL https://raw.githubusercontent.com/espressif/idf-extra-components/master/.github/filter_sarif.py -o filter_sarif.py
- name: Install pyclang
shell: bash
run: |
. ${IDF_PATH}/export.sh
pip install pyclang~=0.2.0
@ -35,7 +36,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: |
@ -49,7 +50,7 @@ jobs:
results.sarif
results.sarif.raw
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
category: clang-tidy

View File

@ -0,0 +1,32 @@
name: "console_cmd_mqtt: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_console_cmd_mqtt:
if: contains(github.event.pull_request.labels.*.name, 'console') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_target: ["esp32"]
test: [ { app: mqtt_ssl_auth_console, path: "components/console_cmd_mqtt/examples" }]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
working-directory: ${{matrix.test.path}}
run: |
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app

View File

@ -15,7 +15,7 @@ jobs:
matrix:
idf_ver: ["latest", "release-v5.3"]
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }]
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols

38
.github/workflows/esp_dns__build.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: "esp_dns: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_esp_dns:
if: contains(github.event.pull_request.labels.*.name, 'dns') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
idf_target: ["esp32"]
test: [ { app: esp_dns_basic, path: "components/esp_dns/examples"}]
include:
- idf_ver: "latest"
warning: "the choice symbol ETHERNET_PHY_LAN867X\nis deprecated: Please use smi_gpio instead"
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
EXPECTED_WARNING: ${{ matrix.warning }}
shell: bash
working-directory: ${{matrix.test.path}}
run: |
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app

View File

@ -13,12 +13,15 @@ jobs:
name: Build examples
strategy:
matrix:
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
include:
- idf_ver: "latest"
warning: "Warning: The smallest app partition is nearly full"
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TARGET_TEST: examples/esp_netif/slip_custom_netif/
TARGET_TEST_DIR: build_esp32c3_target
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
@ -31,6 +34,16 @@ jobs:
python -m pip install idf-build-apps
# Build default configs for all targets
python ./ci/build_apps.py examples -m examples/.build-test-rules.yml -d -c
# Build target tests
python ./ci/build_apps.py ${TARGET_TEST} -r sdkconfig.ci=target
cd ${TARGET_TEST}
${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: slip_target_${{ matrix.idf_ver }}
path: ${{ env.TARGET_TEST }}/artifacts.zip
if-no-files-found: error
build_and_run_on_host:
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
@ -51,3 +64,38 @@ jobs:
python ./ci/build_apps.py examples/mqtt -l -t linux
timeout 5 ./examples/mqtt/build_linux_default/esp_mqtt_demo.elf | tee test.log || true
grep 'MQTT_EVENT_DATA' test.log
run_on_target:
# 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, 'examples') || github.event_name == 'push' )
name: Slip example target test
needs: build_all_examples
strategy:
matrix:
idf_ver: ["release-v5.4", "latest"]
runs-on:
- self-hosted
- modem
env:
TARGET_TEST: examples/esp_netif/slip_custom_netif/
TARGET_TEST_DIR: build_esp32c3_target
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: slip_target_${{ matrix.idf_ver }}
path: ${{ env.TARGET_TEST }}/ci/
- name: Run Test
working-directory: ${{ env.TARGET_TEST }}
run: |
python -m venv .venv
source .venv/bin/activate
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool netifaces
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 --target=esp32c3
done

86
.github/workflows/lws_build.yml vendored Normal file
View File

@ -0,0 +1,86 @@
name: "lws: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_lws:
if: contains(github.event.pull_request.labels.*.name, 'lws') || github.event_name == 'push'
name: Libwebsockets build
strategy:
matrix:
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
test: [ { app: example, path: "examples/client" }]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TEST_DIR: components/libwebsockets/${{ matrix.test.path }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
python ./ci/build_apps.py ${TEST_DIR}
cd ${TEST_DIR}
for dir in `ls -d build_esp32_*`; do
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
zip -qur artifacts.zip $dir
done
- uses: actions/upload-artifact@v4
with:
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/artifacts.zip
if-no-files-found: error
run-target-lws:
# 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, 'lws') || github.event_name == 'push' )
name: Target test
needs: build_lws
strategy:
fail-fast: false
matrix:
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
idf_target: ["esp32"]
test: [ { app: example, path: "examples/client" }]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
env:
TEST_DIR: components/libwebsockets/${{ matrix.test.path }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/ci/
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
- name: Run Example Test on target
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_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }}
done
- uses: actions/upload-artifact@v4
if: always()
with:
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
path: components/libwebsockets/${{ matrix.test.path }}/*.xml

View File

@ -35,30 +35,36 @@ jobs:
# Next we run the pytest (using the console app)
pytest
build_afl_host_test_mdns:
host_compat_checks:
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
name: Build AFL host test
name: Set of compatibility checks
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
with:
path: esp-protocols
- name: Install Necessary Libs
run: |
apt-get update -y
apt-get install -y libbsd-dev
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
- name: Test AFL compat build
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/tests/test_afl_fuzz_host/
cd components/mdns/tests/test_afl_fuzz_host/
make INSTR=off
- name: Test no malloc functions
shell: bash
run: |
cd components/mdns
for file in $(ls *.c); do
cp $file /tmp
echo -n "Checking that $file does not call any std allocations directly..."
python mem_prefix_script.py $file
diff -q $file /tmp/$file || exit 1
echo "OK"
done

View File

@ -13,11 +13,8 @@ jobs:
name: Build examples
strategy:
matrix:
idf_ver: ["latest", "release-v4.4", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
exclude:
- idf_ver: "release-v4.4"
example: modem_tcp_client
include:
- idf_ver: "release-v5.0"
example: "simple_cmux_client"
@ -26,13 +23,7 @@ jobs:
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Check out code (v3) # @v4 failed due to Node 20's requirement, incompatible with older IDF versions
if: matrix.idf_ver != 'latest' && matrix.idf_ver < 'release-v5.0'
uses: actions/checkout@v3
with:
path: protocols
- name: Check out code (v4)
if: matrix.idf_ver == 'latest' || matrix.idf_ver >= 'release-v5.0'
- name: Check out code
uses: actions/checkout@v4
with:
path: protocols
@ -53,7 +44,7 @@ jobs:
name: Build tests
strategy:
matrix:
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "latest"]
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "latest"]
test: ["target", "target_ota", "target_iperf"]
runs-on: ubuntu-22.04
@ -100,3 +91,21 @@ jobs:
run_executable: false
run_coverage: false
pre_run_script: "esp-protocols/components/esp_modem/test/host_test/env.sh"
esp_modem_generated_commands:
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
name: Generated commands compatibility
runs-on: ubuntu-latest
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
- name: Compat check
shell: bash
run: |
sudo apt-get update -y
sudo apt-get install -y astyle
cd components/esp_modem
find examples/ -type f -regex '.*/generate/.*\.\(hpp\|cpp\)' -exec ./scripts/generate.sh {} \;
./scripts/generate.sh
git diff --name-only
git diff --quiet

View File

@ -42,7 +42,7 @@ jobs:
idf.py set-target ${{ matrix.idf_target }}
idf.py build
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh ${GITHUB_WORKSPACE}/${TEST_DIR}/build
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/build
@ -75,12 +75,17 @@ jobs:
run: |
sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/build
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
env:
PIP_EXTRA_INDEX_URL: "https://dl.espressif.com/pypi/"
run: |
python -m pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
python -m venv .venv
source .venv/bin/activate
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
cd ${{ env.TEST_DIR }}
python -m pytest --log-cli-level DEBUG --target=${{ matrix.idf_target }}

View File

@ -17,7 +17,8 @@ jobs:
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TEST_DIR: components/mosquitto/examples/broker
TEST_DIR: components/mosquitto/examples
TARGET_TEST: broker
TARGET_TEST_DIR: build_esp32_default
steps:
- name: Checkout esp-protocols
@ -29,14 +30,15 @@ jobs:
run: |
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
python ci/build_apps.py ${TEST_DIR}
cd ${TEST_DIR}
python ci/build_apps.py -c ${TEST_DIR} -m components/mosquitto/.build-test-rules.yml
# upload only the target test artifacts
cd ${TEST_DIR}/${TARGET_TEST}
${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: mosq_target_esp32_${{ matrix.idf_ver }}
path: ${{ env.TEST_DIR }}/artifacts.zip
path: ${{ env.TEST_DIR }}/${{ env.TARGET_TEST }}/artifacts.zip
if-no-files-found: error
test_mosq:
@ -71,3 +73,108 @@ jobs:
mv $dir build
python -m pytest --log-cli-level DEBUG --junit-xml=./results_esp32_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=esp32
done
check_consistency:
if: contains(github.event.pull_request.labels.*.name, 'mosquitto') || github.event_name == 'push'
name: Checks that API docs and versions are consistent
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checks API Docs and versions
run: |
sudo apt-get update
sudo apt-get -y install doxygen
pip install esp-doxybook
cd components/mosquitto
cp api.md api_orig.md
./generate_api_docs.sh
diff -wB api.md api_orig.md
# check version consistency
CONFIG_VERSION=$(grep -Po '(?<=#define VERSION ")[^"]*' port/priv_include/config.h)
CZ_VERSION=$(grep -Po '(?<=version: )[^"]*' .cz.yaml)
COMP_VERSION=$(grep -Po '(?<=version: ")[^"]*' idf_component.yml)
if [ "$CONFIG_VERSION" != "v$CZ_VERSION" ] || [ "$CONFIG_VERSION" != "v$COMP_VERSION" ]; then
echo "Version mismatch detected:"
echo "config.h: $CONFIG_VERSION"
echo ".cz.yaml: $CZ_VERSION"
echo "idf_component.yml: $COMP_VERSION"
exit 1
fi
echo "Versions are consistent: $CONFIG_VERSION"
build_idf_tests_with_mosq:
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'mosquitto') || github.event_name == 'push' )
name: Build IDF tests
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
test: [ { app: publish, path: "tools/test_apps/protocols/mqtt/publish_connect_test" }]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TARGET_TEST_DIR: build_esp32_local_broker
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
export MOSQUITTO_PATH=`pwd`/components/mosquitto
# to use the actual version of mosquitto
sed -i '/espressif\/mosquitto:/a \ \ \ \ override_path: "${MOSQUITTO_PATH}"' ${IDF_PATH}/${{matrix.test.path}}/main/idf_component.yml
export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function"
export EXTRA_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes"
export EXTRA_CXXFLAGS="${PEDANTIC_FLAGS}"
cd ${IDF_PATH}/${{matrix.test.path}}
idf-build-apps find --config sdkconfig.ci.local_broker -vv --target ${{ matrix.idf_target }} --build-dir=${TARGET_TEST_DIR}
idf-build-apps build --config sdkconfig.ci.local_broker -vv --target ${{ matrix.idf_target }} --build-dir=${TARGET_TEST_DIR}
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
# to replace mqtt test configs with specific mosquitto markers
python ${MOSQUITTO_PATH}/test/replace_decorators.py pytest_mqtt_publish_app.py ${TARGET_TEST_DIR}/pytest_mosquitto.py
zip -qur ${GITHUB_WORKSPACE}/artifacts.zip ${TARGET_TEST_DIR}
- uses: actions/upload-artifact@v4
with:
name: mosq_publish_esp32_${{ matrix.idf_ver }}
path: artifacts.zip
if-no-files-found: error
test_idf_ci_with_mosq:
# 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, 'mosquitto') || github.event_name == 'push' )
name: Mosquitto IDF target tests
needs: build_idf_tests_with_mosq
strategy:
matrix:
idf_ver: ["latest"]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
env:
TEST_DIR: examples
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: mosq_publish_esp32_${{ matrix.idf_ver }}
path: ${{ env.TEST_DIR }}/ci/
- name: Run Test
working-directory: ${{ env.TEST_DIR }}
run: |
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results "paho-mqtt<2" --upgrade
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults
mv $dir build
mv build/*.py .
# Run only "test_mosquitto" marked tests
python -m pytest --log-cli-level DEBUG --junit-xml=./results_esp32_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=esp32 -m test_mosquitto
done

View File

@ -98,7 +98,11 @@ jobs:
components/console_cmd_ping;
components/console_cmd_ifconfig;
components/console_cmd_wifi;
components/console_cmd_mqtt;
components/mbedtls_cxx;
components/mosquitto;
components/sock_utils;
components/libwebsockets;
components/esp_dns;
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

95
.github/workflows/sockutls_build.yml vendored Normal file
View 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

View File

@ -15,7 +15,7 @@ jobs:
matrix:
idf_ver: ["latest", "release-v5.3", "release-v5.2", "release-v5.1"]
test: [ { app: client, path: "examples/tls_client" }, { app: udp, path: "examples/udp_mutual_auth" }, { app: test, path: "tests/uart_mutual_auth" } ]
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols

5
.gitmodules vendored
View File

@ -1,6 +1,9 @@
[submodule "components/asio/asio"]
path = components/asio/asio
url = https://github.com/espressif/asio
url = https://github.com/chriskohlhoff/asio
[submodule "components/mosquitto/mosquitto"]
path = components/mosquitto/mosquitto
url = https://github.com/eclipse/mosquitto
[submodule "components/libwebsockets/libwebsockets"]
path = components/libwebsockets/libwebsockets
url = https://github.com/warmcat/libwebsockets.git

View File

@ -51,7 +51,7 @@ repos:
rev: v1.0.5
hooks:
- id: astyle_py
args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-pointer=name', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper', '--exclude-list=ci/ignore_astyle.txt']
args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper', '--unpad-paren', '--max-continuation-indent=120', '--exclude-list=ci/ignore_astyle.txt']
- repo: https://github.com/commitizen-tools/commitizen
rev: v2.42.1
hooks:
@ -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, dns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls, lws"
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|dns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls|lws)\)\:)'
language: pygrep
args: [--multiline]
stages: [commit-msg]

View File

@ -62,3 +62,19 @@ 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)
### libwebsockets
* Brief introduction [README](components/libwebsockets/README.md)
### console_cmd_mqtt
* Brief introduction [README](components/console_cmd_mqtt/README.md)
### esp_dns
* Brief introduction [README](components/esp_dns/README.md)

View File

@ -0,0 +1 @@
components/mosquitto/examples/serverless_mqtt/components/libjuice/port/juice_random.c

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -64,7 +64,7 @@ static struct generic_queue_handle *create_generic_queue(queue_type_t type, uint
return h;
}
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize )
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize)
{
return (QueueHandle_t)create_generic_queue(QUEUE, uxQueueLength, uxItemSize);
}
@ -75,7 +75,7 @@ uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t
return osal_queue_send(h->q, (uint8_t *)pvItemToQueue, h->item_size) ? pdTRUE : pdFAIL;
}
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait )
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait)
{
return xQueueSend(xQueue, pvItemToQueue, xTicksToWait);
}
@ -86,7 +86,7 @@ uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksTo
return osal_queue_recv(h->q, (uint8_t *)pvBuffer, h->item_size, xTicksToWait) ? pdTRUE : pdFAIL;
}
BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
BaseType_t xSemaphoreGive(QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX) {
@ -96,7 +96,7 @@ BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
return xQueueSend(xQueue, &s_semaphore_data, portMAX_DELAY);
}
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
BaseType_t xSemaphoreGiveRecursive(QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX_REC) {
@ -106,7 +106,7 @@ BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
return pdFALSE;
}
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
BaseType_t xSemaphoreTake(QueueHandle_t xQueue, TickType_t pvTask)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX) {
@ -116,7 +116,7 @@ BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
return xQueueReceive(xQueue, &s_semaphore_data, portMAX_DELAY);
}
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
BaseType_t xSemaphoreTakeRecursive(QueueHandle_t xQueue, TickType_t pvTask)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX_REC) {
@ -126,7 +126,7 @@ BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
return pdFALSE;
}
void vQueueDelete( QueueHandle_t xQueue )
void vQueueDelete(QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->q) {
@ -166,6 +166,8 @@ void vTaskDelete(TaskHandle_t *task)
if (task == NULL) {
pthread_exit(0);
} else {
pthread_cancel((pthread_t)task);
}
void *thread_rval = NULL;
pthread_join((pthread_t)task, &thread_rval);
@ -176,14 +178,14 @@ void vTaskSuspend(void *task)
vTaskDelete(task);
}
TickType_t xTaskGetTickCount( void )
TickType_t xTaskGetTickCount(void)
{
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
return spec.tv_nsec / 1000000 + spec.tv_sec * 1000;
}
void vTaskDelay( const TickType_t xTicksToDelay )
void vTaskDelay(const TickType_t xTicksToDelay)
{
usleep(xTicksToDelay * 1000);
}
@ -212,13 +214,27 @@ void *pthread_task(void *params)
return NULL;
}
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID)
TaskHandle_t xTaskCreateStaticPinnedToCore(TaskFunction_t pxTaskCode,
const char *const pcName,
const uint32_t ulStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
StackType_t *const puxStackBuffer,
StaticTask_t *const pxTaskBuffer,
const BaseType_t xCoreID)
{
static TaskHandle_t pvCreatedTask;
xTaskCreate(pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &pvCreatedTask);
return pvCreatedTask;
}
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID)
{
xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask);
return pdTRUE;
@ -266,7 +282,7 @@ void xTaskNotifyGive(TaskHandle_t task)
}
}
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time )
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time)
{
return true;
}
@ -276,32 +292,32 @@ TaskHandle_t xTaskGetCurrentTaskHandle(void)
return (TaskHandle_t)pthread_self();
}
EventGroupHandle_t xEventGroupCreate( void )
EventGroupHandle_t xEventGroupCreate(void)
{
return osal_signal_create();
}
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
void vEventGroupDelete(EventGroupHandle_t xEventGroup)
{
osal_signal_delete(xEventGroup);
}
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear)
{
return osal_signal_clear(xEventGroup, uxBitsToClear);
}
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup)
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup)
{
return osal_signal_get(xEventGroup);
}
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet)
{
return osal_signal_set(xEventGroup, uxBitsToSet);
}
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait)
{
return osal_signal_wait(xEventGroup, uxBitsToWaitFor, xWaitForAllBits, xTicksToWait);
}

View File

@ -1,11 +1,12 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "esp_heap_caps.h"
#ifdef __cplusplus
extern "C" {
@ -15,7 +16,10 @@ extern "C" {
#define TaskHandle_t TaskHandle_t
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
void vTaskDelay( const TickType_t xTicksToDelay );
typedef void *StackType_t;
typedef void *StaticTask_t;
void vTaskDelay(const TickType_t xTicksToDelay);
void xTaskNotifyGive(TaskHandle_t task);
@ -23,39 +27,48 @@ void ulTaskNotifyTake(bool stuff, uint32_t timeout);
TaskHandle_t xTaskGetCurrentTaskHandle(void);
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time );
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time);
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID);
TaskHandle_t xTaskCreateStaticPinnedToCore(TaskFunction_t pxTaskCode,
const char *const pcName,
const uint32_t ulStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
StackType_t *const puxStackBuffer,
StaticTask_t *const pxTaskBuffer,
const BaseType_t xCoreID);
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID);
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask);
TickType_t xTaskGetTickCount( void );
TickType_t xTaskGetTickCount(void);
void vQueueDelete( QueueHandle_t xQueue );
void vQueueDelete(QueueHandle_t xQueue);
QueueHandle_t xSemaphoreCreateBinary(void);
QueueHandle_t xSemaphoreCreateMutex(void);
QueueHandle_t xSemaphoreCreateRecursiveMutex(void);
BaseType_t xSemaphoreGive( QueueHandle_t xQueue);
BaseType_t xSemaphoreGive(QueueHandle_t xQueue);
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask );
BaseType_t xSemaphoreTake(QueueHandle_t xQueue, TickType_t pvTask);
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue);
BaseType_t xSemaphoreGiveRecursive(QueueHandle_t xQueue);
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask );
BaseType_t xSemaphoreTakeRecursive(QueueHandle_t xQueue, TickType_t pvTask);
void vTaskDelete(TaskHandle_t *task);
QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
uint32_t uxItemSize );
QueueHandle_t xQueueCreate(uint32_t uxQueueLength,
uint32_t uxItemSize);
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
@ -63,23 +76,26 @@ uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksTo
void vTaskSuspend(void *task);
EventGroupHandle_t xEventGroupCreate( void );
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear );
EventGroupHandle_t xEventGroupCreate(void);
void vEventGroupDelete(EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear);
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait );
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait);
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet);
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
void *heap_caps_malloc(size_t size, uint32_t caps);
void heap_caps_free(void *ptr);
#ifdef __cplusplus
}

View File

@ -1,7 +1,8 @@
---
commitizen:
bump_message: 'bump(asio): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py asio
tag_format: asio-v$version
version: 1.28.0~0
version: 1.32.0
version_files:
- idf_component.yml

View File

@ -1,5 +1,26 @@
# Changelog
## [1.32.0](https://github.com/espressif/esp-protocols/commits/asio-v1.32.0)
### Features
- Upgrade asio to 1.32 ([9bdd429c](https://github.com/espressif/esp-protocols/commit/9bdd429c))
- Drop esp/asio patches in favor of sock-utils ([27435b7f](https://github.com/espressif/esp-protocols/commit/27435b7f))
### Bug Fixes
- Fix chat example to print only the message body ([76aaea08](https://github.com/espressif/esp-protocols/commit/76aaea08))
- Make asio enable if_nametoindex to fix linking ([5db32cce](https://github.com/espressif/esp-protocols/commit/5db32cce))
- Re-applie refs to common comps idf_component.yml ([9fe44a45](https://github.com/espressif/esp-protocols/commit/9fe44a45))
- Reference common component from IDF ([74fc228c](https://github.com/espressif/esp-protocols/commit/74fc228c))
- Revert referencing protocol_examples_common from IDF ([f9e0281a](https://github.com/espressif/esp-protocols/commit/f9e0281a))
- reference protocol_examples_common from IDF ([09abb18b](https://github.com/espressif/esp-protocols/commit/09abb18b))
- specify override_path in example manifest files ([1d8923cf](https://github.com/espressif/esp-protocols/commit/1d8923cf))
### Updated
- docs(asio): Updates asio docs ([ce9337d3](https://github.com/espressif/esp-protocols/commit/ce9337d3))
## [1.28.2~0](https://github.com/espressif/esp-protocols/commits/asio-1.28.2_0)
### Bug Fixes

View File

@ -6,8 +6,8 @@ if(NOT CONFIG_LWIP_IPV6 AND NOT CMAKE_BUILD_EARLY_EXPANSION)
return()
endif()
set(asio_sources "asio/asio/src/asio.cpp")
set(asio_requires lwip)
set(asio_sources "asio/asio/src/asio.cpp" "port/src/asio_stub.cpp")
set(asio_requires lwip sock_utils)
if(CONFIG_ASIO_SSL_SUPPORT)
list(APPEND asio_sources
@ -18,7 +18,7 @@ if(CONFIG_ASIO_SSL_SUPPORT)
endif()
idf_component_register(SRCS ${asio_sources}
INCLUDE_DIRS "asio/asio/include" "port/include"
INCLUDE_DIRS "port/include" "asio/asio/include"
PRIV_INCLUDE_DIRS ${asio_priv_includes}
PRIV_REQUIRES ${asio_requires})
@ -30,6 +30,7 @@ target_compile_definitions(${COMPONENT_LIB} PUBLIC SA_RESTART=0x01
ASIO_STANDALONE
ASIO_HAS_PTHREADS
OPENSSL_NO_ENGINE
ASIO_DETAIL_IMPL_POSIX_EVENT_IPP # this replaces asio's posix_event constructor
)
if(NOT CONFIG_COMPILER_CXX_EXCEPTIONS)

View File

@ -1,6 +1,15 @@
menu "ESP-ASIO"
visible if LWIP_IPV6
config ASIO_IS_ENABLED
# Invisible option that is enabled if ASIO is added to the IDF components.
# This is used to "select" LWIP_NETIF_API option
# which enables if_indextoname() and if_nametoindex() functions
# (these are optionally used in asio)
bool
default "y"
select LWIP_NETIF_API
config ASIO_SSL_SUPPORT
bool "Enable SSL/TLS support of ASIO"
default n

View File

@ -120,7 +120,7 @@ private:
asio::buffer(read_msg_.body(), read_msg_.body_length()),
[this, self](std::error_code ec, std::size_t /*length*/) {
if (!ec) {
ESP_LOGD("asio-chat:", "%s", read_msg_.body());
ESP_LOGD("asio-chat", "%.*s", read_msg_.body_length(), read_msg_.body());
room_.deliver(read_msg_);
do_read_header();
} else {

View File

@ -17,6 +17,8 @@
#include "asio/ssl.hpp"
#include "asio/buffer.hpp"
#include "esp_pthread.h"
// allows for direct access to mbedtls specifics
#include "asio/ssl/mbedtls_specific.hpp"
extern const unsigned char server_pem_start[] asm("_binary_srv_crt_start");
extern const unsigned char server_pem_end[] asm("_binary_srv_crt_end");
@ -217,6 +219,7 @@ void ssl_server_thread()
io_context.run();
}
void ssl_client_thread()
{
asio::io_context io_context;
@ -229,6 +232,11 @@ void ssl_client_thread()
asio::ssl::context ctx(asio::ssl::context::tls_client);
#if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
ctx.add_certificate_authority(cert_chain);
// mbedtls (from 3.6.3) requires hostname to be set when performing TLS handshake with verify-peer option
// asio::ssl allows for name verification using verification callback, i.e. socket_.set_verify_callback(asio::ssl::host_name_verification()),
// - which is not supported in Espressif ASIO port yet.
// Therefore we provide a way to directly use mbedtls API and here we just configure the expected hostname to verify
asio::ssl::mbedtls::set_hostname(ctx.native_handle(), server_ip);
#endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
Client c(io_context, ctx, endpoints);

View File

@ -1,22 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIUNI5wldYysh6rtCzYmda6H414aRswDQYJKoZIhvcNAQEL
MIIDkzCCAnugAwIBAgIUb25LYOLubieEbKPQDiM+8T5p4yUwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJRXNwcmVzc2lmMB4X
DTIwMDEyMTA5MDk0NloXDTI1MDEyMDA5MDk0NlowWTELMAkGA1UEBhMCQVUxEzAR
DTI1MDQwNzA5NDkzOFoXDTQ1MDQwMjA5NDkzOFowWTELMAkGA1UEBhMCQVUxEzAR
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
IEx0ZDESMBAGA1UEAwwJRXNwcmVzc2lmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAyadSpRnIQBVbEAsbpkrKrOMlBOMIUmA8AfNyOYPLfv0Oa5lBiMAV
3OQDu5tYyFYKwkCUqq65iAm50fPbSH71w1tkja6nZ1yAIM+TvpMlM/WiFGrhY+Tc
kAcLcKUJyPxrv/glzoVslbqUgIhuhCSKA8uk1+ILcn3nWzPcbcowLx31+AHeZj8h
bIAdj6vjqxMCFStp4IcA+ikmCk75LCN4vkkifdkebb/ZDNYCZZhpCBnCHyFAjPc4
7C+FDVGT3/UUeeTy+Mtn+MqUAhB+W0sPDm1n2h59D4Z/MFm0hl6GQCAKeMJPzssU
BBsRm6zoyPQ4VTqG0uwfNNbORyIfKONMUwIDAQABo1MwUTAdBgNVHQ4EFgQUGYLV
EkgWzxjpltE6texha7zZVxowHwYDVR0jBBgwFoAUGYLVEkgWzxjpltE6texha7zZ
VxowDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAb2EF4Zg2XWNb
eZHnzupCDd9jAhwPqkt7F1OXvxJa/RFUSB9+2izGvikGGhuKY4f0iLuqF+bhExD9
sapDcdFO2Suh4J3onbwEvmKvsv56K3xhapYg8WwPofpkVirnkwFjpQXGzrYxPujg
BPmSy3psQrhvOr/WH7SefJv2qr4ikaugfE+3enY4PL+C1dSQAuNo1QGgWsZIu0c8
TZybNZ13vNVMA+tgj2CM8FR3Etaabwtu3TTcAnO7aoBTix/bLBTuZoczhN8/MhG3
GylmDzFI8a6aKxQL3Fi4PsM82hRKWu3gfs39sR1Ci4V22v8uO5EWBPK0QZvDSc1a
KwwxI4zA0w==
MIIBCgKCAQEArJsjwSNjPOBpTCRW+pIag9gJgRaNIjscea/ilRYRwAnqWKLNssNw
Kye79KmQ5TxnOEvBIYjesArst1l7MghPLaELscCKo96jzCkSmgPLbxPs+5/E4daO
9ItxOSH2mjOgG5yFQLEb8xOvsvWWrJAUBj6RBjhzgSYLYRbesWKAyVi9fxSuzfZm
ROV0B2NsO1PlUDzweo9RYSuvpyNR3kddNnc6lJLXtZhf6IHczjFDFd5/PQuzLIO/
Dbg+5AMpQykbMFhcQI/Y49GlMMXFDIaWjP+XfE/yUJ4GyYd2EzpDFDFMisnkuR9d
LQgSXZNwygO8SIfYnnm1pwcGuG/fCQZYpQIDAQABo1MwUTAdBgNVHQ4EFgQUMTUG
OZ7ujyz7oXSuhDgbpoPRo1cwHwYDVR0jBBgwFoAUMTUGOZ7ujyz7oXSuhDgbpoPR
o1cwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAD5JwzRVEvnTK
R2bfMNy60FmFNTOEEYP+XYoNGBiXVY3MRrWlfbY5Pbs4Nq7sCfzEWMj2UsjFmjZE
DU6FdsaL6rhnps03MR5yiuE5w2aPiH/ijgzVfZtdLe6nKcnrv1YInjEKk+Y3qGu6
2ZC+MEINPBfRiuN6gCAdxGiK81J4FPLlZImLO/g/0fSrIXCzBUzjoYRYjsy5AP60
0kbaoGA/SshP0aeNvWB0wUab40idGXBFJ3vnEfMbLIMdc/uCqnzRpqK0m1DacwrI
nTUMl0bI302Oa/gym+Ma0nJ1nVADcLKoZ1syWjyzIcl6zr+ITY5S+pbeO/geQgKh
NSUkahX6MQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,22 @@
#!/bin/bash
# This script generates self-signed certificates for testing purposes only.
# DO NOT use these certificates in production environments.
# These certificates are meant for development and testing of SSL/TLS functionality.
# 1. Generate CA private key
openssl genrsa -out ca.key 2048
# 2. Generate CA certificate (validity: 20 years, CN=Espressif)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 7300 -out ca.crt -subj "/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=Espressif"
# 3. Generate server private key
openssl genrsa -out server.key 2048
# 4. Generate server Certificate Signing Request (CSR)
openssl req -new -key server.key -out server.csr -subj "/CN=localhost"
# 5. Generate server certificate signed by CA (validity: 20 years)
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out srv.crt -days 7300 -sha256
# 6. Clean up intermediate files
rm server.csr ca.srl

View File

@ -1,27 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAlUCywNhVv4RO2y9h/XGKZ1azzk3jzHpSBzIGO9LoiA8trC/p
1ykGaUfYPJllYK4HMhC4fUyE3J7tVL2Eskzl26LNPLbEoaBWZM9NhV3iA1/1EtOu
p6umLx+y3sDfvK35YAOUbjdAlBfhnJ4r8h7oTsxl3J5jZ18zgjJnJi2NEFq/yTpO
MiwHLWPjy25fDFixfV9UzSvbgt1JaGPmC7c4QkhHzjyp0+ikuvRIw0p9BBNeqBV2
da3qBMB5FtodUJTAz6o6OKWbTalLjQi6C1H6z9TnY7IrJBUOy/FWkQH/sEsLdscD
hHa1Dz2oT203QjhzyOSfnNF95D/1MdNcMt6l0wIDAQABAoIBAC1JJTOoMFRc48RT
myrYQYNbZlEphv3q+2qdfhC2zMFDwbrmCtCy7PQSzYSNkpoEE8DYG/JAvmtmeWJl
4pZrCK9ctWM/nWfhC3WpBL97nfEiM20T94F+bn0L5Cz8XqaULv839th+QUTt/hGU
WIctY5VNJXcMQ+MAmtNdUbjex1d3iuxiKHUo4nDoZ8digKFNdtdP5B5nlMq5chCL
mxNRcsGsx2dDAxbGUapdTVPWHPJKpLOBoSkluDsfd2KZADFU2R1SJpAX9+RYh3HM
5FTUdHTUaISxbKkgeDKlEM0lqk2TtGUwCyEj098ewi7Wzsu9w60IplPPUJx5FRG6
jp3wzLkCgYEAxKp5T20rf/7ysX7x053I7VCjDXUxAaWOEj1uS3AhOkl0NaZg7Di+
y53fWNkcHdkt2n2LqMt/43UgMYq3TVVcq2eunPNF11e1bJw8CjDafwDs4omwwyVn
lYhPuB4dK2OAib+vU5Zqpp0kZMoxk2MZVgon8z+s8DW/zmB6aFqAWeUCgYEAwkhC
OgmXKMdjOCVy5t2f5UbY8Y9rV3w8eUATuJ47MMwLr4pGYnKoEn9JB4ltWrHv/u5S
fOv3tIrrCEvnCoCbOILwCsY5LqTNXgqova8FB6RpMUQCzhDd8LHuvdHv0WMnMzX1
3PKuqwh8JS55m4WqZRhzr5BFKG4fHPVs4IcaJVcCgYAzzCaJSdqUKqTnJOUydDNQ
ddWMHNqccWs62J0tF0pZHLGT089hSAzQejMyJnSmU+Ykzr4y5e44DUg+ZCelIZ93
saYmxlgVwI8THQ8fLADQRIEfpV4996MRmkZM2vmZzOo03Zyi6lIKsga82Rg3lnk8
1Q3ynknBNpbfF0AGLhfyFQKBgBYlxJ73HutAJ5hr9HhLBYJOnEaVUehMOlycKGNg
bmD2sdJWEgYBChXpurqIORYguLo4EuE4ySkkuPxeIr14wbkkfBbOWBBwKxUwY+IT
xKAFZxR9q1AwbgyVTCEJgKw/AGX/HcMNS0omEnjunmBTUYRq0C1QZgHg490aQUor
PJjLAoGAevzdTpFlVeuKeYh1oDubGO1LinyXpBv7fPFjl+zu4AVbjojcU6yC4OO6
QvqopE6SyAECKy8kAOFcESPsGc9Lta2XUvI203z7pIVlNVEcJ0+90mQh3Mn1U46l
sZ49PdRvNwNb5wvkh1UqNsMlGFbRlzMbIk45ou4311kCobowZek=
MIIEpAIBAAKCAQEA1zQFgwUxyIteLl5SiciuRBZbn/3KWRmsVpyo5JiYCK9NM3Q8
dDur/dyQ4y2Mq0RVuM7uZL7VraRA4F9+Tm8D2BJ8q4Ai3nRDiGQkFEfbvX0Wic6P
P6q7lKm65fnYMvbWHqhMOglr8e4b7dMOFpZdNLRZVcoP4/43+/9dOgOj7TXczTFs
jnlLJBijOnwABf/xlTpTR7ZbQ3uOlckc0TK/Lqxex+NUq4dXQKa96a/wpA6smW38
0XU5hvVBmhA9YNK86CIpAVOny9gNM1Wxv+aAdVZigNNi6Hht75neC0DhFqGbfpMi
nLhW8qGwVtY3T1pM8HrpdAu5plkdvmcDm8tUAwIDAQABAoIBADHwOHc29V58ONa5
vJ2MnCPgrFJsKlCSzJMst2SUpHMfeuK9zmmKj1bRoC2XnFUB/oJsQpXOUveAbi2i
+0RoLpQtdhC2I2FLyYAU/OpX4n4OUPSZolQ74luVJ3HGkI0DCp0CoO220f3KK2D8
4QAM1IQudayavyVBEOzBTXjw71FUCXmg0viBtd7JRSug9FR69PPLWP14uzo1viBA
dtRsLePDyTatW+bfKkPwDG8hhJoummw+fYRPZbM05aWLmVvTiZ/eQtRq1jkwUNSH
zXsJiCQbHLqwoXISiEsdagQM8hNW2bw5B+ijCQhWNj2ZZBZPJIRtI75sXiXGy2eC
kDaP0cECgYEA8GpoGOyVZ+KMm7PVNcNbdLJrjpoNhdVXdGwR+yd3u6EoQ3MMD5tr
4F7pFtN3IxwLoCSM/dnobysNFdKdQRnOB+o3uLADiPn9REM7ebuGUwUP3BWwkb7S
orDVPUMA82A1p0T4Dwi+OAPpS5bUoI7S/6ZwheWTdoGZYjCrtsqkeEUCgYEA5Sc5
sgLFWIqvYUQF/+aLAwjHR/bBD9NkvHgdW8GmgXdnEd0DIKHDpJ2yEK3BbQxVp/Kk
O2KG1NGyJ9UqA9QQ3q5UgpZNHQBWxz2GUl0jHsy8enhgsZr4K2+wvuw5F5bCdXPe
m/dyFIGnUJ7ic+DtvDGjXdcwAR8Cgc97m5Pg06cCgYEAgyjqBb78e6KDJ2biyOP9
fxrfxvqQqhUMEz3qSWTs03ZGaxXW3KTkI5JkA8n2Uzc3uHR4Xv2E6zFHgEJY/G1B
k9vZ7m5IX3BTFezAA9eknqJCVsWWgMzkSVHD5Bor6JryaoEb+8e/TvwDSPPOqJGC
12pMNSBcZOirb4AyDhVbySkCgYB3Lu2dHj/SC1+oMR8Ft7y5eUlcroQ/XO1Z8Qck
ABY/5ABhlBfaUwhUiAhjEFw4AWBTl6m/kUEbU21btkzB7PxRNU6TFOVKnjCENAW2
tOZdUJL/B7kS5s0ImnDM/EO9dxXwzLENYaed7sk870ZMisJbTV3wosk+7Af7yBQ8
GK+opQKBgQCdZy3KX2FT8S/K2SjDuRM8uDzJ+IcaqScDhgbJFMlrbWmTSML69oRD
Ic6xVe5hWkkPIs521gwrQSD5E3dbb2dFmjhUZpZkHdv0u/AUupFN0EaCFb/I2A0P
fRebd9oKoZjlUrEPeID0kjzbmnPGbtG+gFZYmkRb5iLdcCVAn0O1AQ==
-----END RSA PRIVATE KEY-----

View File

@ -1,18 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC9DCCAdwCFA1lSIcHwYKdB2UqOrZxZnVgPObTMA0GCSqGSIb3DQEBCwUAMFkx
MIIC9DCCAdwCFHNjaiCN2RT7W7NHXho8HlgxdAygMA0GCSqGSIb3DQEBCwUAMFkx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yMDA2
MTIwNjA0MTNaFw0yMjA2MDIwNjA0MTNaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJVAssDYVb+ETtsvYf1ximdW
s85N48x6UgcyBjvS6IgPLawv6dcpBmlH2DyZZWCuBzIQuH1MhNye7VS9hLJM5dui
zTy2xKGgVmTPTYVd4gNf9RLTrqerpi8fst7A37yt+WADlG43QJQX4ZyeK/Ie6E7M
ZdyeY2dfM4IyZyYtjRBav8k6TjIsBy1j48tuXwxYsX1fVM0r24LdSWhj5gu3OEJI
R848qdPopLr0SMNKfQQTXqgVdnWt6gTAeRbaHVCUwM+qOjilm02pS40IugtR+s/U
52OyKyQVDsvxVpEB/7BLC3bHA4R2tQ89qE9tN0I4c8jkn5zRfeQ/9THTXDLepdMC
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAnMYGW+idt37bEE4WPgrRorKWuplR+zHD
wJFz53DQzyIZJHmJ2hR5U0jNcHy/nMq7tbdz9LZPrVF4lZJ3TJhnmkOKjMFPCQE8
YcmsP3il6eXgtGqg53InOi/uJqEQ9TfM54cbpp6xKbnmpwk4uprISBRQt7u2ZLk2
40ED6zgjFPDTYmSjSpb2AN6KUB6PflgVs+4p9ViHNq4U3AlYV/BM0+3G4aMX2wNl
ZIpQfOyuaYD5MU50mY+O+gDiiypkpYf6a6S4YJ1sMbavDsP7bW5UMnP0jKYR549q
5hF1fdkXq52DfJ9ya2kl3mANFkKssQV+1KCBMxGoeqfakmJfa03xXA==
cm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yNTA0
MDcwOTQ5MzhaFw00NTA0MDIwOTQ5MzhaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANc0BYMFMciLXi5eUonIrkQW
W5/9ylkZrFacqOSYmAivTTN0PHQ7q/3ckOMtjKtEVbjO7mS+1a2kQOBffk5vA9gS
fKuAIt50Q4hkJBRH2719FonOjz+qu5SpuuX52DL21h6oTDoJa/HuG+3TDhaWXTS0
WVXKD+P+N/v/XToDo+013M0xbI55SyQYozp8AAX/8ZU6U0e2W0N7jpXJHNEyvy6s
XsfjVKuHV0Cmvemv8KQOrJlt/NF1OYb1QZoQPWDSvOgiKQFTp8vYDTNVsb/mgHVW
YoDTYuh4be+Z3gtA4Rahm36TIpy4VvKhsFbWN09aTPB66XQLuaZZHb5nA5vLVAMC
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAoiuycWVVjmS9IMS1n9ll8UlIqq8dl7vs
Y5ckGMrdSGR8BPgloTrB6ual4vRPgbn2rGBGAvGusdcmhc1vkVbYsI3JRpTTDDoE
PvQqHWXwV2RDRU5kG6ZOsU+o01Ir4b3w3qfP2LT20FCuuAMIMh23PsSmoc7ziFZ8
76+ox6FjhJMPMF2aftiDmP44/fFg16C1t2PFH/Bk4sm4qRdpXVcWeiaHaSF9JkHa
XwW3TuDSxJwlFFU7ffTRgYYkQ61q8B0LjWV4FF1dBBqflAiXEhWcVhljqfsWn7rq
NBJ/QzZ3GhgQO9GOCokh/ckcp/ZMOm9tv9lV2huGz8Akk8/UYhMUEg==
-----END CERTIFICATE-----

View File

@ -1,4 +1,4 @@
version: "1.28.2~0"
version: "1.32.0"
description: Cross-platform C++ library for network and I/O programming
url: https://github.com/espressif/esp-protocols/tree/master/components/asio
issues: https://github.com/espressif/esp-protocols/issues
@ -7,3 +7,5 @@ repository: https://github.com/espressif/esp-protocols.git
dependencies:
idf:
version: ">=5.0"
espressif/sock_utils:
version: "^0.1"

View File

@ -0,0 +1,11 @@
//
// SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
//
// SPDX-License-Identifier: BSL-1.0
//
#pragma once
#include "sys/socket.h"
#include "socketpair.h"
#include_next "asio/detail/config.hpp"

View File

@ -0,0 +1,29 @@
//
// SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
//
// SPDX-License-Identifier: BSL-1.0
//
#pragma once
#include "asio/ssl/context_base.hpp"
#include "asio/ssl/context.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
namespace asio {
namespace ssl {
namespace mbedtls {
/**
* @brief Configures specific hostname to be used in peer verification
*
* @param handle asio::ssl context handle type
* @param name hostname to be verified (std::string ownership will be moved to ssl::context)
*
* @return true on success
*/
bool set_hostname(asio::ssl::context::native_handle_type handle, std::string name);
};
};
} // namespace asio::ssl::mbedtls

View File

@ -1,12 +0,0 @@
/*
* SPDX-FileCopyrightText: 2018-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_ASIO_CONFIG_H_
#define _ESP_ASIO_CONFIG_H_
#define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP
#include "openssl_stub.hpp"
#endif // _ESP_ASIO_CONFIG_H_

View File

@ -1,5 +1,5 @@
//
// SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
// SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
//
// SPDX-License-Identifier: BSL-1.0
//
@ -52,6 +52,12 @@ public:
return nullptr;
}
bool set_hostname(std::string hostname)
{
hostname_ = std::move(hostname);
return true;
}
std::size_t size(container c) const
{
switch (c) {
@ -70,6 +76,7 @@ public:
const_buffer cert_chain_;
const_buffer private_key_;
const_buffer ca_cert_;
std::string hostname_;
};
/**

View File

@ -1,5 +1,5 @@
//
// SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
// SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
//
// SPDX-License-Identifier: BSL-1.0
//
@ -16,6 +16,11 @@ namespace asio {
namespace ssl {
namespace mbedtls {
bool set_hostname(asio::ssl::context::native_handle_type handle, std::string name)
{
return handle->get()->set_hostname(std::move(name));
}
const char *error_message(int error_code)
{
static char error_buf[100];
@ -25,7 +30,7 @@ const char *error_message(int error_code)
void throw_alloc_failure(const char *location)
{
asio::error_code ec( MBEDTLS_ERR_SSL_ALLOC_FAILED, asio::error::get_mbedtls_category());
asio::error_code ec(MBEDTLS_ERR_SSL_ALLOC_FAILED, asio::error::get_mbedtls_category());
asio::detail::throw_error(ec, location);
}
@ -269,6 +274,16 @@ private:
} else {
mbedtls_ssl_conf_ca_chain(&conf_, nullptr, nullptr);
}
// Configure hostname before handshake if users pre-configured any
// use NULL if not set (to preserve the default behaviour of mbedtls < v3.6.3)
const char* hostname = !ctx->hostname_.empty() ? ctx->hostname_.c_str() : NULL;
ret = mbedtls_ssl_set_hostname(&ssl_, hostname);
if (ret < 0) {
print_error("mbedtls_ssl_set_hostname", ret);
return false;
}
ret = mbedtls_ssl_setup(&ssl_, &conf_);
if (ret) {
print_error("mbedtls_ssl_setup", ret);

View File

@ -8,7 +8,7 @@
//
#include "asio/detail/config.hpp"
#include "openssl_stub.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
#include <cstring>
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"

View File

@ -7,7 +7,7 @@
//
#include "asio/detail/config.hpp"
#include "openssl_stub.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/ssl/detail/engine.hpp"

View File

@ -0,0 +1,36 @@
//
// SPDX-FileCopyrightText: 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// SPDX-License-Identifier: BSL-1.0
//
// SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD
//
#include "asio/detail/posix_event.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/detail/push_options.hpp"
#include <unistd.h>
#include <climits>
namespace asio::detail {
// This replaces asio's posix_event constructor
// since the default POSIX version uses pthread_condattr_t operations (init, setclock, destroy),
// which are not available on all IDF versions (some are defined in compilers' headers, others in
// pthread library, but they typically return `ENOSYS` which causes trouble in the event wrapper)
// IMPORTANT: Check implementation of posix_event() when upgrading upstream asio in order not to
// miss any initialization step.
posix_event::posix_event()
: state_(0)
{
int error = ::pthread_cond_init(&cond_, nullptr);
asio::error_code ec(error, asio::error::get_system_category());
asio::detail::throw_error(ec, "event");
}
} // namespace asio::detail
extern "C" int pause (void)
{
while (true) {
::sleep(UINT_MAX);
}
}

View File

@ -0,0 +1,8 @@
---
commitizen:
bump_message: 'bump(console): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py console_cmd_mqtt
tag_format: console_cmd_mqtt-v$version
version: 1.0.0
version_files:
- idf_component.yml

View File

@ -0,0 +1,7 @@
# Changelog
## [1.0.0](https://github.com/espressif/esp-protocols/commits/console_cmd_mqtt-v1.0.0)
### Features
- Added component with mqtt command ([1fcc5b1d](https://github.com/espressif/esp-protocols/commit/1fcc5b1d))

View File

@ -0,0 +1,7 @@
idf_component_register(SRCS "console_mqtt.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_netif console mqtt)
if(CONFIG_MQTT_CMD_AUTO_REGISTRATION)
target_link_libraries(${COMPONENT_LIB} PRIVATE "-u console_cmd_mqtt_register")
endif()

View File

@ -0,0 +1,15 @@
menu "MQTT Configuration"
config MQTT_CMD_AUTO_REGISTRATION
bool "Enable Console command mqtt Auto-registration"
default y
help
Enabling this allows for the autoregistration of the wifi command.
config MQTT_BROKER_URL
string "Broker URL or IP address"
default "mqtt://mqtt.eclipseprojects.io"
help
URL or IP address of the broker to connect to
endmenu

View File

@ -0,0 +1,87 @@
# Console command mqtt
The component provides a console where mqtt commands can be executed.
## MQTT Configuration:
1. Broker: Use menuconfig **"MQTT Configuration"** to configure the broker url.
## API
### Steps to enable console in an example code:
1. Add this component to your project using ```idf.py add-dependency``` command.
2. In the main file of the example, add the following line:
```c
#include "console_mqtt.h"
```
3. Ensure esp-netif is initialized and default event loop is created in your app_main():
```c
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
```
4. In your app_main() function, add the following line as the last line:
```c
ESP_ERROR_CHECK(console_cmd_init()); // Initialize console
// Register all plugin command added to your project
ESP_ERROR_CHECK(console_cmd_all_register());
// To register only mqtt command skip calling console_cmd_all_register()
ESP_ERROR_CHECK(console_cmd_mqtt_register());
ESP_ERROR_CHECK(console_cmd_start()); // Start console
```
Note: Auto-registration of a specific plugin command can be disabled from menuconfig.
### Certificate Integration for Mutual Authentication
To enhance security and enable secure communication over MQTT, three functions have been added to the API, allowing users to set client certificates, client keys, and broker certificates separately.
Setting the client certificate:
```c
set_mqtt_client_cert(client_cert_pem_start, client_cert_pem_end);
```
Setting the client key:
```c
set_mqtt_client_key(client_key_pem_start, client_key_pem_end);
```
Setting the broker certificate:
```c
set_mqtt_broker_certs(broker_cert_pem_start, broker_cert_pem_end);
```
Each function takes pointers to the start and end of the respective PEM-encoded data, allowing users to specify the necessary certificate and key information independently. For a complete secure MQTT setup, users should call all three functions in their application code.
To utilize these certificates, users need to include additional arguments when establishing MQTT connections using the library. Specifically, users should provide the `--cert`, `--key`, and `--cafile` options along with the MQTT connection command.
### Adding a plugin command or component:
To add a plugin command or any component from IDF component manager into your project, simply include an entry within the `idf_component.yml` file.
For more details refer [IDF Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html)
## Suported command:
### mqtt:
```
mqtt [-CsD] [-h <host>] [-u <username>] [-P <password>] [--cert] [--key] [--cafile]
mqtt command
-C, --connect Connect to a broker (flag, no argument)
-h, --host=<host> Specify the host uri to connect to
-s, --status Displays the status of the mqtt client (flag, no argument)
-u, --username=<username> Provide a username to be used for authenticating with the broker
-P, --password=<password> Provide a password to be used for authenticating with the broker
--cert Define the PEM encoded certificate for this client, if required by the broker (flag, no argument)
--key Define the PEM encoded private key for this client, if required by the broker (flag, no argument)
--cafile Define the PEM encoded CA certificates that are trusted (flag, no argument)
--use-internal-bundle Use the internal certificate bundle for TLS (flag, no argument)
-D, --disconnect Disconnect from the broker (flag, no argument)
mqtt_pub [-t <topic>] [-m <message>]
mqtt publish command
-t, --topic=<topic> Topic to Subscribe/Publish
-m, --message=<message> Message to Publish
mqtt_sub [-U] [-t <topic>]
mqtt subscribe command
-t, --topic=<topic> Topic to Subscribe/Publish
-U, --unsubscribe Unsubscribe from a topic
```

View File

@ -0,0 +1,475 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_console.h"
#include "esp_event.h"
#include "esp_log.h"
#include "argtable3/argtable3.h"
#include "console_mqtt.h"
#include "mqtt_client.h"
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
#include "esp_crt_bundle.h"
#endif
static const char *TAG = "console_mqtt";
#define CONNECT_HELP_MSG "mqtt -C -h <host uri> -u <username> -P <password> --cert --key --cafile\n"
#define PUBLISH_HELP_MSG "Usage: mqtt -P -t <topic> -d <data>\n"
#define SUBSCRIBE_HELP_MSG "Usage: mqtt -S -t <topic>\n"
#define UNSUBSCRIBE_HELP_MSG "Usage: mqtt -U\n"
#define DISCONNECT_HELP_MSG "Usage: mqtt -D\n"
#if CONFIG_MQTT_CMD_AUTO_REGISTRATION
/**
* Static registration of this plugin is achieved by defining the plugin description
* structure and placing it into .console_cmd_desc section.
* The name of the section and its placement is determined by linker.lf file in 'plugins' component.
*/
static const console_cmd_plugin_desc_t __attribute__((section(".console_cmd_desc"), used)) PLUGIN = {
.name = "console_cmd_mqtt",
.plugin_regd_fn = &console_cmd_mqtt_register
};
#endif
static struct {
struct arg_lit *connect;
struct arg_str *uri;
struct arg_lit *status;
struct arg_str *username;
struct arg_str *password;
struct arg_lit *cert;
struct arg_lit *key;
struct arg_lit *cafile;
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
struct arg_lit *use_internal_bundle;
#endif
struct arg_lit *disconnect;
struct arg_end *end;
} mqtt_args;
static struct {
struct arg_str *topic;
struct arg_lit *unsubscribe;
struct arg_end *end;
} mqtt_sub_args;
static struct {
struct arg_str *topic;
struct arg_str *message;
struct arg_end *end;
} mqtt_pub_args;
typedef enum {
MQTT_STATE_INIT = 0,
MQTT_STATE_DISCONNECTED,
MQTT_STATE_CONNECTED,
MQTT_STATE_ERROR,
MQTT_STATE_STOPPED,
} mqtt_client_state_t;
mqtt_client_state_t client_status = MQTT_STATE_INIT;
static esp_mqtt_client_handle_t client_handle = NULL;
static const uint8_t *s_own_cert_pem_start = NULL;
static const uint8_t *s_own_cert_pem_end = NULL;
static const uint8_t *s_own_key_pem_start = NULL;
static const uint8_t *s_own_key_pem_end = NULL;
static const uint8_t *s_ca_cert_pem_start = NULL;
static const uint8_t *s_ca_cert_pem_end = NULL;
static void log_error_if_nonzero(const char *message, int error_code)
{
if (error_code != 0) {
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
}
}
/*
* @brief Event handler registered to receive MQTT events
*
* This function is called by the MQTT client event loop.
*
* @param handler_args user data registered to the event.
* @param base Event base for the handler(always MQTT Base in this example).
* @param event_id The id for the received event.
* @param event_data The data for the event, esp_mqtt_event_handle_t.
*/
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32, base, event_id);
esp_mqtt_event_handle_t event = event_data;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_BEFORE_CONNECT:
ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT");
break;
case MQTT_EVENT_CONNECTED:
client_status = MQTT_STATE_CONNECTED;
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
break;
case MQTT_EVENT_DISCONNECTED:
client_status = MQTT_STATE_DISCONNECTED;
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
ESP_LOGI(TAG, "TOPIC=%.*s\r\n", event->topic_len, event->topic);
ESP_LOGI(TAG, "DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
client_status = MQTT_STATE_ERROR;
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
}
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
static const char *mqtt_state_to_string(mqtt_client_state_t state)
{
switch (state) {
case MQTT_STATE_INIT:
return "Initializing";
case MQTT_STATE_DISCONNECTED:
return "Disconnected";
case MQTT_STATE_CONNECTED:
return "Connected";
case MQTT_STATE_ERROR:
return "Error";
case MQTT_STATE_STOPPED:
return "Disconnected and Stopped";
default:
return "Unknown State";
}
}
static int do_mqtt_cmd(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&mqtt_args);
if (nerrors != 0) {
arg_print_errors(stderr, mqtt_args.end, argv[0]);
return 1;
}
if (mqtt_args.status->count > 0) {
ESP_LOGI(TAG, "MQTT Client Status: %s\n", mqtt_state_to_string(client_status));
return 0;
}
if (mqtt_args.connect->count > 0) {
if (client_handle != NULL) {
ESP_LOGW(TAG, "mqtt client already connected");
ESP_LOGI(TAG, "Try: %s", DISCONNECT_HELP_MSG);
return 0;
}
char *uri = CONFIG_MQTT_BROKER_URL;
if (mqtt_args.uri->count > 0) {
uri = (char *)mqtt_args.uri->sval[0];
}
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = uri,
};
if ((mqtt_args.username->count > 0) && (mqtt_args.password->count > 0)) {
mqtt_cfg.credentials.username = mqtt_args.username->sval[0];
mqtt_cfg.credentials.authentication.password = mqtt_args.password->sval[0];
}
ESP_LOGI(TAG, "broker: %s", mqtt_cfg.broker.address.uri);
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
/* Ensure --use_internal_bundle and --cafile are mutually exclusive */
if ((mqtt_args.use_internal_bundle->count > 0) && (mqtt_args.cafile->count > 0)) {
ESP_LOGE(TAG, "Error: Options can't be used together. Use either --use-internal-bundle or --cafile. \n");
return 1;
}
if (mqtt_args.use_internal_bundle->count > 0) {
mqtt_cfg.broker.verification.crt_bundle_attach = esp_crt_bundle_attach;
}
#endif
if (mqtt_args.cafile->count > 0) {
if (s_ca_cert_pem_start && s_ca_cert_pem_end) {
mqtt_cfg.broker.verification.certificate = (const char *)s_ca_cert_pem_start;
} else {
ESP_LOGW(TAG, "cafile not provided");
}
}
if (mqtt_args.cert->count > 0) {
if (s_own_cert_pem_start && s_own_cert_pem_end) {
mqtt_cfg.credentials.authentication.certificate = (const char *)s_own_cert_pem_start;
} else {
ESP_LOGW(TAG, "cert not provided");
}
if (mqtt_args.key->count > 0) {
if (s_own_key_pem_start && s_own_key_pem_end) {
mqtt_cfg.credentials.authentication.key = (const char *)s_own_key_pem_start;
} else {
ESP_LOGW(TAG, "key not provided");
}
} else {
mqtt_cfg.credentials.authentication.key = NULL;
}
}
client_handle = esp_mqtt_client_init(&mqtt_cfg);
if (client_handle == NULL) {
ESP_LOGE(TAG, "ERROR: Client init");
ESP_LOGI(TAG, "Try: %s", DISCONNECT_HELP_MSG);
ESP_LOGE(TAG, CONNECT_HELP_MSG);
return 1;
}
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
esp_mqtt_client_register_event(client_handle, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(client_handle);
} else if (mqtt_args.disconnect->count > 0) {
ESP_LOGD(TAG, "Disconnect command received:");
if (client_handle == NULL) {
ESP_LOGE(TAG, "mqtt client not connected");
return 0;
}
if (esp_mqtt_client_stop(client_handle) != ESP_OK) {
ESP_LOGE(TAG, "Failed to stop mqtt client task");
return 1;
}
client_handle = NULL;
client_status = MQTT_STATE_STOPPED;
ESP_LOGI(TAG, "mqtt client disconnected and stopped");
}
return 0;
}
esp_err_t set_mqtt_client_cert(const uint8_t *client_cert_pem_start_i, const uint8_t *client_cert_pem_end_i)
{
if (!client_cert_pem_start_i || !client_cert_pem_end_i ||
(client_cert_pem_start_i > client_cert_pem_end_i)) {
ESP_LOGE(TAG, "Invalid mqtt Client certs(%d)\n", __LINE__);
return ESP_ERR_INVALID_ARG;
}
s_own_cert_pem_start = client_cert_pem_start_i;
s_own_cert_pem_end = client_cert_pem_end_i;
return ESP_OK;
}
esp_err_t set_mqtt_client_key(const uint8_t *client_key_pem_start_i, const uint8_t *client_key_pem_end_i)
{
if (client_key_pem_start_i && client_key_pem_end_i &&
(client_key_pem_start_i >= client_key_pem_end_i)) {
ESP_LOGE(TAG, "Invalid mqtt Client key(%d)\n", __LINE__);
return ESP_ERR_INVALID_ARG;
}
s_own_key_pem_start = client_key_pem_start_i;
s_own_key_pem_end = client_key_pem_end_i;
return ESP_OK;
}
esp_err_t set_mqtt_broker_certs(const uint8_t *ca_cert_pem_start_i, const uint8_t *ca_cert_pem_end_i)
{
if (!ca_cert_pem_start_i || !ca_cert_pem_end_i ||
(ca_cert_pem_start_i > ca_cert_pem_end_i)) {
ESP_LOGE(TAG, "Invalid mqtt ca cert(%d)\n", __LINE__);
return ESP_ERR_INVALID_ARG;
}
s_ca_cert_pem_start = ca_cert_pem_start_i;
s_ca_cert_pem_end = ca_cert_pem_end_i;
return ESP_OK;
}
static int do_mqtt_sub_cmd(int argc, char **argv)
{
int msg_id;
int nerrors = arg_parse(argc, argv, (void **)&mqtt_sub_args);
if (nerrors != 0) {
arg_print_errors(stderr, mqtt_sub_args.end, argv[0]);
return 1;
}
if (client_handle == NULL) {
ESP_LOGE(TAG, "mqtt client not connected");
return 0;
}
if (mqtt_sub_args.unsubscribe->count > 0) {
if (mqtt_sub_args.topic->count <= 0) {
ESP_LOGE(TAG, UNSUBSCRIBE_HELP_MSG);
return 0;
}
char *topic = (char *)mqtt_sub_args.topic->sval[0];
msg_id = esp_mqtt_client_unsubscribe(client_handle, mqtt_sub_args.topic->sval[0]);
ESP_LOGI(TAG, "Unsubscribe successful, msg_id=%d, topic=%s", msg_id, topic);
} else {
if (mqtt_sub_args.topic->count <= 0) {
ESP_LOGE(TAG, SUBSCRIBE_HELP_MSG);
return 0;
}
char *topic = (char *)mqtt_sub_args.topic->sval[0];
msg_id = esp_mqtt_client_subscribe(client_handle, topic, 0);
ESP_LOGI(TAG, "Subscribe successful, msg_id=%d, topic=%s", msg_id, topic);
}
return 0;
}
static int do_mqtt_pub_cmd(int argc, char **argv)
{
int msg_id;
int nerrors = arg_parse(argc, argv, (void **)&mqtt_pub_args);
if (nerrors != 0) {
arg_print_errors(stderr, mqtt_pub_args.end, argv[0]);
return 1;
}
if (client_handle == NULL) {
ESP_LOGE(TAG, "mqtt client not connected");
return 0;
}
if ((mqtt_pub_args.topic->count <= 0) || (mqtt_pub_args.message->count <= 0)) {
ESP_LOGE(TAG, PUBLISH_HELP_MSG);
}
msg_id = esp_mqtt_client_publish(client_handle,
mqtt_pub_args.topic->sval[0],
mqtt_pub_args.message->sval[0],
0, 1, 0);
if (msg_id == -1) {
ESP_LOGE(TAG, "mqtt client not connected");
return 0;
}
ESP_LOGI(TAG, "Publish successful, msg_id=%d, topic=%s, data=%s",
msg_id, mqtt_pub_args.topic->sval[0], mqtt_pub_args.message->sval[0]);
return 0;
}
/**
* @brief Registers the mqtt commands.
*
* @return
* - esp_err_t
*/
esp_err_t console_cmd_mqtt_register(void)
{
esp_err_t ret = ESP_OK;
/* Register mqtt */
mqtt_args.connect = arg_lit0("C", "connect", "Connect to a broker (flag, no argument)");
mqtt_args.uri = arg_str0("h", "host", "<host>", "Specify the host uri to connect to");
mqtt_args.status = arg_lit0("s", "status", "Displays the status of the mqtt client (flag, no argument)");
mqtt_args.username = arg_str0("u", "username", "<username>", "Provide a username to be used for authenticating with the broker");
mqtt_args.password = arg_str0("P", "password", "<password>", "Provide a password to be used for authenticating with the broker");
mqtt_args.cert = arg_lit0(NULL, "cert", "Define the PEM encoded certificate for this client, if required by the broker (flag, no argument)");
mqtt_args.key = arg_lit0(NULL, "key", "Define the PEM encoded private key for this client, if required by the broker (flag, no argument)");
mqtt_args.cafile = arg_lit0(NULL, "cafile", "Define the PEM encoded CA certificates that are trusted (flag, no argument)");
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
mqtt_args.use_internal_bundle = arg_lit0(NULL, "use-internal-bundle", "Use the internal certificate bundle for TLS (flag, no argument)");
#endif
mqtt_args.disconnect = arg_lit0("D", "disconnect", "Disconnect from the broker (flag, no argument)");
mqtt_args.end = arg_end(1);
const esp_console_cmd_t mqtt_cmd = {
.command = "mqtt",
.help = "mqtt command",
.hint = NULL,
.func = &do_mqtt_cmd,
.argtable = &mqtt_args
};
ret = esp_console_cmd_register(&mqtt_cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Unable to register mqtt");
return ret;
}
/* Register mqtt_pub */
mqtt_pub_args.topic = arg_str0("t", "topic", "<topic>", "Topic to Subscribe/Publish");
mqtt_pub_args.message = arg_str0("m", "message", "<message>", "Message to Publish");
mqtt_pub_args.end = arg_end(1);
const esp_console_cmd_t mqtt_pub_cmd = {
.command = "mqtt_pub",
.help = "mqtt publish command",
.hint = NULL,
.func = &do_mqtt_pub_cmd,
.argtable = &mqtt_pub_args
};
ret = esp_console_cmd_register(&mqtt_pub_cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Unable to register mqtt_pub");
return ret;
}
/* Register mqtt_sub */
mqtt_sub_args.topic = arg_str0("t", "topic", "<topic>", "Topic to Subscribe/Publish");
mqtt_sub_args.unsubscribe = arg_lit0("U", "unsubscribe", "Unsubscribe from a topic");
mqtt_sub_args.end = arg_end(1);
const esp_console_cmd_t mqtt_sub_cmd = {
.command = "mqtt_sub",
.help = "mqtt subscribe command",
.hint = NULL,
.func = &do_mqtt_sub_cmd,
.argtable = &mqtt_sub_args
};
ret = esp_console_cmd_register(&mqtt_sub_cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Unable to register mqtt_sub");
}
return ret;
}

View File

@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "console_simple_init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Registers the mqtt command.
*
* @return
* - esp_err_t
*/
esp_err_t console_cmd_mqtt_register(void);
/**
* @brief Set MQTT client certificate
*
* This function sets the MQTT client certificate for secure communication.
* The function takes the PEM(Privacy Enhanced Mail) encoded certificate arguments.
*
* @param client_cert_pem_start_i Pointer to the beginning of the client certificate PEM data.
* @param client_cert_pem_end_i Pointer to the end of the client certificate PEM data.
*
* @return
* ESP_OK on success
* ESP_ERR_INVALID_ARG on invalid arguments
*/
esp_err_t set_mqtt_client_cert(const uint8_t *client_cert_pem_start_i, const uint8_t *client_cert_pem_end_i);
/**
* @brief Set MQTT client key
*
* This function sets the MQTT client key for secure communication.
* The function takes the PEM(Privacy Enhanced Mail) encoded key arguments.
*
* @param client_key_pem_start_i Pointer to the beginning of the client key PEM data.
* @param client_key_pem_end_i Pointer to the end of the client key PEM data.
*
* @return
* ESP_OK on success
* ESP_ERR_INVALID_ARG on invalid arguments
*/
esp_err_t set_mqtt_client_key(const uint8_t *client_key_pem_start_i, const uint8_t *client_key_pem_end_i);
/**
* @brief Set MQTT broker certificate
*
* This function sets the MQTT broker certificate for secure communication.
* The function takes the PEM(Privacy Enhanced Mail) encoded broker certificate arguments.
*
* @param broker_cert_pem_start_i Pointer to the beginning of the broker certificate PEM data.
* @param broker_cert_pem_end_i Pointer to the end of the broker certificate PEM data.
*
* @return
* ESP_OK on success
* ESP_ERR_INVALID_ARG on invalid arguments
*/
esp_err_t set_mqtt_broker_certs(const uint8_t *broker_cert_pem_start_i, const uint8_t *broker_cert_pem_end_i);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,11 @@
# 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(mqtt_ssl_auth_console)
# Certs for mqtts://test.mosquitto.org:8884
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "certs/client.crt" TEXT)
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "certs/client.key" TEXT)
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "certs/mosquitto.org.pem" TEXT)

View File

@ -0,0 +1,174 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
# ESP-MQTT SSL Authentication Console
This example demonstrates the use of the MQTT command-line component to connect to both secured and unsecured MQTT brokers. It provides multiple modes of connection, including:
* Unsecured transport: Connect to a broker without encryption.
* SSL/TLS transport: Securely connect using SSL/TLS with options for:
* Validating the broker using a provided CA certificate.
* Validating the broker using the internal certificate bundle.
* Performing SSL mutual authentication using client and broker certificates.
Additionally, the example allows subscribing to topics, unsubscribing from topics, and publishing messages to a specified topic through commands. Connections to the broker at test.mosquitto.org are used to demonstrate these features.
(Please note that the public broker is maintained by the community so may not be always available, for details please visit http://test.mosquitto.org)
It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker.
## How to use example
### Hardware Required
This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet.
### Configure the project
* Open the project configuration menu (`idf.py menuconfig`)
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
* Generate your client keys and certificate (specific to testing with Mosquitto broker)
Note: The following steps are for testing with the Mosquitto broker. If you're using a different broker, you may need to adapt the steps to meet your broker's certificate and key requirements.
#### Steps for SSL Mutual authentication:
Navigate to the certs directory
```
cd certs
```
Generate a client key and a CSR. When you are generating the CSR, do not use the default values. At a minimum, the CSR must include the Country, Organisation and Common Name fields.
```
openssl genrsa -out client.key
openssl req -out client.csr -key client.key -new
```
Paste the generated CSR in the [Mosquitto test certificate signer](https://test.mosquitto.org/ssl/index.php), click Submit and copy the downloaded `client.crt` in the `main` directory.
Please note, that the supplied files `client.crt` and `client.key` in the `main` directory are only placeholders for your client certificate and key (i.e. the example "as is" would compile but would not connect to the broker)
The broker certificate `mosquitto.org.pem` can be downloaded in pem format from [mosquitto.org.crt](https://test.mosquitto.org/ssl/mosquitto.org.crt). Convert it to `mosquitto.org.pem` simply by renaming it.
Note: If your certificate and key file names differ, update the root `CMakeLists.txt` file and main/`mqtt_ssl_auth_console.c` accordingly.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
Warning: This example might need a bigger app partition size if you're compiling it for debug. To ensere this issue doesn't happen "optimize for size is enabled in menuconfig.
### Command Usage:
```
esp> help
help [<string>]
Print the summary of all registered commands if no arguments are given,
otherwise print summary of given command.
<string> Name of command
mqtt [-CsD] [-h <host>] [-u <username>] [-P <password>] [--cert] [--key] [--cafile]
mqtt command
-C, --connect Connect to a broker (flag, no argument)
-h, --host=<host> Specify the host uri to connect to
-s, --status Displays the status of the mqtt client (flag, no argument)
-u, --username=<username> Provide a username to be used for authenticating with the broker
-P, --password=<password> Provide a password to be used for authenticating with the broker
--cert Define the PEM encoded certificate for this client, if required by the broker (flag, no argument)
--key Define the PEM encoded private key for this client, if required by the broker (flag, no argument)
--cafile Define the PEM encoded CA certificates that are trusted (flag, no argument)
--use-internal-bundle Use the internal certificate bundle for TLS (flag, no argument)
-D, --disconnect Disconnect from the broker (flag, no argument)
mqtt_pub [-t <topic>] [-m <message>]
mqtt publish command
-t, --topic=<topic> Topic to Subscribe/Publish
-m, --message=<message> Message to Publish
mqtt_sub [-U] [-t <topic>]
mqtt subscribe command
-t, --topic=<topic> Topic to Subscribe/Publish
-U, --unsubscribe Unsubscribe from a topic
```
### Connection:
#### Connect without Validating the Broker:
This option connects to the broker without validating its certificate. It is not secure.
```
mqtt -h mqtts://test.mosquitto.org -C
```
or
```
mqtt -h mqtts://mqtt.eclipseprojects.io -C
```
#### Validate the Broker using the Internal Certificate Bundle:
This option uses the ESP-IDF's built-in certificate bundle to verify the broker's identity.
```
mqtt -h mqtts://mqtt.eclipseprojects.io -C --use-internal-bundle
```
or
```
mqtt -h mqtts://test.mosquitto.org -C --use-internal-bundle
```
#### Validate the Broker using a Provided CA Certificate:
This option requires you to provide the broker's CA certificate for validation.
```
mqtt -h mqtts://test.mosquitto.org -C --cafile
```
#### SSL Mutual Authentication(encrypted, client certificate required):
This option performs client authentication in addition to broker validation. It requires the client certificate, private key, and broker CA certificate.
```
mqtt -h mqtts://test.mosquitto.org:8884 -C --cert --key --cafile
```
or
```
mqtt -h mqtts://test.mosquitto.org:8884 -C --cert --key --use-internal-bundle
```
Note: In this example, the broker's certificate is included in the certificate bundle (refer to sdkconfig.default).
### Disconnect:
```
esp> mqtt -D
I (1189949) console_mqtt: mqtt client disconnected
```
### Subscribe/Unsubscribe:
```
esp> mqtt_sub -t test0
I (897289) console_mqtt: Subscribe successful, msg_id=57425, topic=test0
esp> I (897799) console_mqtt: MQTT_EVENT_SUBSCRIBED, msg_id=57425
esp>
esp> mqtt_sub -U -t test0
I (902009) console_mqtt: Unsubscribe successful, msg_id=27663, topic=test0
esp> I (902509) console_mqtt: MQTT_EVENT_UNSUBSCRIBED, msg_id=27663
```
### Publish:
```
esp> mqtt_pub -t test0 -m "Hello, Testing 123"
I (999469) console_mqtt: Publish successful, msg_id=55776, topic=test0, data=Hello, Testing 123
I (1000009) console_mqtt: MQTT_EVENT_PUBLISHED, msg_id=55776
esp>
```
### Receiving data event:
```
esp> I (999999) console_mqtt: MQTT_EVENT_DATA
I (999999) console_mqtt: TOPIC=test0
I (999999) console_mqtt: DATA=Hello, Testing 123
```

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG
A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU
BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv
by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE
BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES
MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp
dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg
UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW
Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA
s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH
3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo
E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT
MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV
6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC
6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf
+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK
sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839
LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE
m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
-----END CERTIFICATE-----

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "mqtt_ssl_auth_console.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,8 @@
dependencies:
idf:
version: ">=5.0"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
console_cmd_mqtt:
version: "*"
override_path: '../../../'

View File

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include <netdb.h>
#include "console_mqtt.h"
#include "protocol_examples_common.h"
// Certs for mqtts://test.mosquitto.org:8884
extern const uint8_t g_client_cert_pem_start[] asm("_binary_client_crt_start");
extern const uint8_t g_client_cert_pem_end[] asm("_binary_client_crt_end");
extern const uint8_t g_client_key_pem_start[] asm("_binary_client_key_start");
extern const uint8_t g_client_key_pem_end[] asm("_binary_client_key_end");
extern const uint8_t g_broker_cert_pem_start[] asm("_binary_mosquitto_org_pem_start");
extern const uint8_t g_broker_cert_pem_end[] asm("_binary_mosquitto_org_pem_end");
void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init(); //Initialize NVS
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* ${IDF_PATH}/examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
// Initialize console REPL
ESP_ERROR_CHECK(console_cmd_init());
ESP_ERROR_CHECK(console_cmd_all_register());
set_mqtt_client_cert(g_client_cert_pem_start, g_client_cert_pem_end);
set_mqtt_client_key(g_client_key_pem_start, g_client_key_pem_end);
set_mqtt_broker_certs(g_broker_cert_pem_start, g_broker_cert_pem_end);
// start console REPL
ESP_ERROR_CHECK(console_cmd_start());
}

View File

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
# -*- coding: utf-8 -*-
import pytest
@pytest.mark.esp32
def test_examples_ifconfig_command(dut):
dut.expect('esp>', timeout=30)
dut.write('help mqtt')
dut.expect(r'mqtt \[-CsD\] \[-h <host>\] \[-u <username>\] \[-P <password>\] \[--cert\] \[--key\] \[--cafile\]', timeout=30)
dut.write('help mqtt_pub')
dut.expect(r'mqtt_pub \[-t <topic>\] \[-m <message>\]', timeout=30)
dut.write('help mqtt_sub')
dut.expect(r'mqtt_sub \[-U\] \[-t <topic>\]', timeout=30)

View 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.5.0 Project Minimal Configuration
#
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="certs/mosquitto.org.pem"

View File

@ -0,0 +1,11 @@
version: 1.0.0
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_mqtt
description: The component provides a console where the 'mqtt' command can be executed.
license: Apache-2.0
dependencies:
idf:
version: '>=5.0'
espressif/console_simple_init:
version: '>=1.1.0'
override_path: '../console_simple_init'
public: true

View File

@ -1,3 +1,22 @@
idf_component_register(SRCS eppp_link.c eppp_sdio_slave.c eppp_sdio_host.c
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
set(driver_deps esp_driver_gpio esp_driver_spi)
else()
set(driver_deps driver)
endif()
if(CONFIG_EPPP_LINK_DEVICE_ETH)
set(transport_src eppp_eth.c)
endif()
if(CONFIG_EPPP_LINK_DEVICE_SDIO)
set(transport_src eppp_sdio_slave.c eppp_sdio_host.c)
endif()
idf_component_register(SRCS eppp_link.c ${transport_src}
INCLUDE_DIRS "include"
PRIV_REQUIRES esp_netif esp_driver_spi esp_driver_gpio esp_timer driver)
PRIV_REQUIRES esp_netif esp_timer esp_eth ${driver_deps})
if(CONFIG_EPPP_LINK_DEVICE_ETH)
idf_component_get_property(ethernet_init espressif__ethernet_init COMPONENT_LIB)
target_link_libraries(${COMPONENT_LIB} PRIVATE ${ethernet_init})
endif()

View File

@ -28,6 +28,16 @@ menu "eppp_link"
help
Use SDIO.
config EPPP_LINK_DEVICE_ETH
bool "Ethernet"
depends on SOC_EMAC_SUPPORTED
help
Use Ethernet.
This transport could employ a full fledged Ethernet connection
between two EPPP nodes via standard Ethernet cable.
It could be also effectively connected directly on PCB, EMAC to EMAC,
without any Ethernet PHY chips (using eth_dummy_phy driver).
endchoice
config EPPP_LINK_CONN_MAX_RETRY
@ -67,4 +77,14 @@ menu "eppp_link"
endchoice
config EPPP_LINK_ETHERNET_OUR_ADDRESS
string "MAC address our local node"
default "06:00:00:00:00:01"
depends on EPPP_LINK_DEVICE_ETH
config EPPP_LINK_ETHERNET_THEIR_ADDRESS
string "MAC address the remote node"
default "06:00:00:00:00:02"
depends on EPPP_LINK_DEVICE_ETH
endmenu

View File

@ -11,12 +11,12 @@ We usually call this node a SLAVE microcontroller. The "HOST" microcontroller ru
brings in the WiFi connectivity from the "SLAVE" microcontroller.
```
SLAVE micro HOST micro
\|/ +----------------+ +----------------+
| | | serial line | |
+---+ WiFi NAT PPPoS |=== UART / SPI / SDIO =====| PPPoS client |
| (server)| | |
+----------------+ +----------------+
SLAVE micro HOST micro
\|/ +----------------+ +----------------+
| | | (serial) line | |
+---+ WiFi NAT PPPoS |=== UART / SPI / SDIO / ETH ===| PPPoS client |
| (server)| | |
+----------------+ +----------------+
```
## API
@ -55,3 +55,9 @@ Tested with WiFi-NAPT example
* TCP - 9Mbits/s
* UDP - 11Mbits/s
### Ethernet
- Internal EMAC with real PHY chip
* TCP - 5Mbits/s
* UDP - 8Mbits/s

View File

@ -0,0 +1,119 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_check.h"
#include "esp_event.h"
#include "esp_mac.h"
#include "eppp_link.h"
#include "eppp_transport.h"
#include "esp_eth_driver.h"
#include "ethernet_init.h"
#include "esp_eth_spec.h"
typedef struct header {
uint8_t dst[ETH_ADDR_LEN];
uint8_t src[ETH_ADDR_LEN];
uint16_t len;
} __attribute__((packed)) header_t;
static const char *TAG = "eppp_ethernet";
static bool s_is_connected = false;
static esp_eth_handle_t *s_eth_handles = NULL;
static uint8_t s_out_buffer[ETH_MAX_PACKET_SIZE];
static uint8_t s_their_mac[ETH_ADDR_LEN];
static uint8_t s_our_mac[ETH_ADDR_LEN];
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
s_is_connected = true;
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
s_is_connected = false;
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}
static esp_err_t receive(esp_eth_handle_t h, uint8_t *buffer, uint32_t len, void *netif)
{
header_t *head = (header_t *)buffer;
size_t packet_len = head->len;
if (len >= packet_len) {
esp_err_t ret = esp_netif_receive(netif, buffer + ETH_HEADER_LEN, packet_len, NULL);
free(buffer);
return ret;
}
return ESP_FAIL;
}
__attribute__((weak)) esp_err_t eppp_transport_ethernet_init(esp_eth_handle_t *handle_array[])
{
uint8_t eth_port_cnt = 0;
ESP_RETURN_ON_ERROR(ethernet_init_all(handle_array, &eth_port_cnt), TAG, "Failed to init common eth drivers");
ESP_RETURN_ON_FALSE(eth_port_cnt == 1, ESP_ERR_INVALID_ARG, TAG, "multiple Ethernet devices detected, please init only one");
return ESP_OK;
}
__attribute__((weak)) void eppp_transport_ethernet_deinit(esp_eth_handle_t *handle_array)
{
ethernet_deinit_all(s_eth_handles);
}
esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif)
{
ESP_RETURN_ON_ERROR(eppp_transport_ethernet_init(&s_eth_handles), TAG, "Failed to initialize Ethernet driver");
ESP_RETURN_ON_ERROR(esp_eth_update_input_path(s_eth_handles[0], receive, esp_netif), TAG, "Failed to set Ethernet Rx callback");
sscanf(CONFIG_EPPP_LINK_ETHERNET_OUR_ADDRESS, "%2" PRIu8 ":%2" PRIu8 ":%2" PRIi8 ":%2" PRIu8 ":%2" PRIu8 ":%2" PRIu8,
&s_our_mac[0], &s_our_mac[1], &s_our_mac[2], &s_our_mac[3], &s_our_mac[4], &s_our_mac[5]);
sscanf(CONFIG_EPPP_LINK_ETHERNET_THEIR_ADDRESS, "%2" PRIu8 ":%2" PRIu8 ":%2" PRIi8 ":%2" PRIu8 ":%2" PRIu8 ":%2" PRIu8,
&s_their_mac[0], &s_their_mac[1], &s_their_mac[2], &s_their_mac[3], &s_their_mac[4], &s_their_mac[5]);
esp_eth_ioctl(s_eth_handles[0], ETH_CMD_S_MAC_ADDR, s_our_mac);
ESP_RETURN_ON_ERROR(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL), TAG, "Failed to register Ethernet handlers");
ESP_RETURN_ON_ERROR(esp_eth_start(s_eth_handles[0]), TAG, "Failed to start Ethernet driver");
return ESP_OK;
}
void eppp_transport_deinit(void)
{
esp_eth_stop(s_eth_handles[0]);
eppp_transport_ethernet_deinit(s_eth_handles);
}
esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len)
{
if (!s_is_connected) {
return ESP_FAIL;
}
// setup Ethernet header
header_t *head = (header_t *)s_out_buffer;
memcpy(head->dst, s_their_mac, ETH_ADDR_LEN);
memcpy(head->src, s_our_mac, ETH_ADDR_LEN);
head->len = len;
// support only payloads with len <= ETH_MAX_PAYLOAD_LEN
if (len > ETH_MAX_PAYLOAD_LEN) {
return ESP_FAIL;
}
memcpy(s_out_buffer + ETH_HEADER_LEN, buffer, len);
return esp_eth_transmit(s_eth_handles[0], s_out_buffer, len + ETH_HEADER_LEN);
}

View File

@ -12,7 +12,7 @@
#include "esp_event.h"
#include "esp_netif_ppp.h"
#include "eppp_link.h"
#include "esp_serial_slave_link/essl_sdio.h"
#include "eppp_transport.h"
#if CONFIG_EPPP_LINK_DEVICE_SPI
#include "driver/spi_master.h"
@ -24,6 +24,12 @@
#include "driver/uart.h"
#endif
#if CONFIG_EPPP_LINK_DEVICE_ETH
#define EPPP_NEEDS_TASK 0
#else
#define EPPP_NEEDS_TASK 1
#endif
static const int GOT_IPV4 = BIT0;
static const int CONNECTION_FAILED = BIT1;
#define CONNECT_BITS (GOT_IPV4|CONNECTION_FAILED)
@ -98,6 +104,8 @@ esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *config);
esp_err_t eppp_sdio_slave_init(void);
void eppp_sdio_slave_deinit(void);
void eppp_sdio_host_deinit(void);
#elif CONFIG_EPPP_LINK_DEVICE_ETH
#else
static esp_err_t transmit(void *h, void *buffer, size_t len)
{
@ -224,6 +232,8 @@ static esp_netif_t *netif_init(eppp_type_t role, eppp_config_t *eppp_config)
.handle = h,
#if CONFIG_EPPP_LINK_DEVICE_SDIO
.transmit = role == EPPP_CLIENT ? eppp_sdio_host_tx : eppp_sdio_slave_tx,
#elif CONFIG_EPPP_LINK_DEVICE_ETH
.transmit = eppp_transport_tx,
#else
.transmit = transmit,
#endif
@ -691,6 +701,7 @@ esp_err_t eppp_perform(esp_netif_t *netif)
#endif // CONFIG_EPPP_LINK_DEVICE_SPI / UART
#if EPPP_NEEDS_TASK
static void ppp_task(void *args)
{
esp_netif_t *netif = args;
@ -699,6 +710,7 @@ static void ppp_task(void *args)
h->exited = true;
vTaskDelete(NULL);
}
#endif
static bool have_some_eppp_netif(esp_netif_t *netif, void *ctx)
{
@ -738,6 +750,8 @@ void eppp_deinit(esp_netif_t *netif)
} else {
eppp_sdio_slave_deinit();
}
#elif CONFIG_EPPP_LINK_DEVICE_ETH
eppp_transport_deinit();
#endif
netif_deinit(netif);
}
@ -781,6 +795,13 @@ esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config)
ESP_LOGE(TAG, "Failed to initialize SDIO %d", ret);
return NULL;
}
#elif CONFIG_EPPP_LINK_DEVICE_ETH
esp_err_t ret = eppp_transport_init(config, netif);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize SDIO %d", ret);
return NULL;
}
#endif
return netif;
}
@ -834,12 +855,13 @@ esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_time
}
eppp_netif_start(netif);
#if EPPP_NEEDS_TASK
if (xTaskCreate(ppp_task, "ppp connect", config->task.stack_size, netif, config->task.priority, NULL) != pdTRUE) {
ESP_LOGE(TAG, "Failed to create a ppp connection task");
eppp_deinit(netif);
return NULL;
}
#endif
int netif_cnt = get_netif_num(netif);
if (netif_cnt < 0) {
eppp_close(netif);

View File

@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif);
esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len);
esp_err_t eppp_transport_rx(esp_netif_t *netif);
void eppp_transport_deinit(void);

View File

@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EPPP_LINK_DEVICE_ETH=y

View File

@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32p4"
CONFIG_EPPP_LINK_DEVICE_SDIO=y

View File

@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EPPP_LINK_DEVICE_ETH=y

View File

@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32c6"
CONFIG_EPPP_LINK_DEVICE_SDIO=y

View File

@ -4,3 +4,4 @@ description: The component provides a general purpose PPP connectivity, typicall
dependencies:
idf: '>=5.2'
espressif/esp_serial_slave_link: "^1.1.0"
espressif/ethernet_init: '>=0.0.7'

View File

@ -63,6 +63,7 @@ typedef enum eppp_transport {
EPPP_TRANSPORT_UART,
EPPP_TRANSPORT_SPI,
EPPP_TRANSPORT_SDIO,
EPPP_TRANSPORT_ETHERNET,
} eppp_transport_t;

View File

@ -0,0 +1,8 @@
---
commitizen:
bump_message: 'bump(dns): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py esp_dns
tag_format: esp_dns-v$version
version: 1.0.0
version_files:
- idf_component.yml

View File

@ -0,0 +1,7 @@
# Changelog
## [1.0.0](https://github.com/espressif/esp-protocols/commits/esp_dns-v1.0.0)
### Features
- Add ESP DNS module with support for UDP, TCP, DoT, and DoH protocols ([57cd6080](https://github.com/espressif/esp-protocols/commit/57cd6080))

View File

@ -0,0 +1,15 @@
idf_component_register(SRCS
"esp_dns_udp.c"
"esp_dns_tcp.c"
"esp_dns_dot.c"
"esp_dns_doh.c"
"esp_dns.c"
"esp_dns_lwip.c"
"esp_dns_utils.c"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES nvs_flash lwip esp_event esp-tls esp_http_client esp-tls tcp_transport)
if(CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM)
target_link_libraries(${COMPONENT_LIB} "-u lwip_hook_netconn_external_resolve")
endif()

201
components/esp_dns/LICENSE Normal file
View 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.

View File

@ -0,0 +1,223 @@
# ESP DNS Component
This component provides a flexible DNS resolution system for ESP32 devices with support for multiple DNS protocols. It allows applications to resolve domain names using various transport methods, including standard UDP/TCP DNS, and securely resolve them using DNS over TLS (DoT) and DNS over HTTPS (DoH).
## Table of Contents
- [Features](#features)
- [Requirements](#requirements)
- [How to Use](#how-to-use)
- [Configuration](#configuration)
- [Certificate Options](#certificate-options)
- [Limitations](#limitations)
- [Performance Considerations](#performance-considerations)
- [How It Works](#how-it-works)
- [Troubleshooting](#troubleshooting)
## Features
- **Multiple Protocol Support** Choose from various DNS protocols:
- Standard UDP DNS (Port 53)
- TCP DNS (Port 53)
- DNS over TLS (DoT) (Port 853)
- DNS over HTTPS (DoH) (Port 443)
- **Secure DNS Resolution**: Supports encrypted DNS queries using TLS and HTTPS to protect privacy and prevent DNS spoofing.
- **Flexible Configuration**: Easily configure DNS servers, ports, timeouts, and protocol-specific options.
- **LWIP Integration**: Seamlessly integrates with the ESP-IDF networking stack through LWIP hooks.
- **Standard getaddrinfo() Interface**: Use the standard `getaddrinfo()` function to resolve domain names.
## Requirements
- ESP-IDF v5.0 or newer
- Network connectivity (Wi-Fi or Ethernet)
- For DoT/DoH: Sufficient RAM for TLS operations
## How to Use
### 1. Enable custom DNS resolution
To enable custom DNS resolution, configure the `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM` setting either through menuconfig or by adding `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM=y` to your `sdkconfig.defaults` file to pre-set the configuration during the build process.
### 2. Configure DNS Settings
Initialize the DNS component with your preferred configuration:
```C
#include "esp_dns.h"
/* Configure DNS over HTTPS */
esp_dns_config_t dns_config = {
.dns_server = "dns.google", /* DNS server hostname or IP address */
.port = ESP_DNS_DEFAULT_DOH_PORT, /* Optional: Server port (443 is default for HTTPS) */
.timeout_ms = ESP_DNS_DEFAULT_TIMEOUT_MS, /* Optional: Request timeout in milliseconds (10000ms default) */
.tls_config = {
/* Optional: Use ESP-IDF certificate bundle for validating popular DNS providers */
.crt_bundle_attach = esp_crt_bundle_attach,
/* Or provide a custom certificate in PEM format (string) for your DNS server */
/* Note: Only PEM format is supported; DER format certificates are not supported yet */
.cert_pem = server_root_cert_pem_start,
/* Note: If both crt_bundle_attach and cert_pem are provided,
crt_bundle_attach is preferred over cert_pem */
},
.protocol_config.doh_config = {
.url_path = "/dns-query", /* Optional: DoH endpoint path on the server ("/dns-query" default) */
}
};
/* Initialize DNS component based on protocol */
esp_dns_handle_t dns_handle = NULL;
/* Call esp_dns_init_doh() to use DNS over HTTPS */
dns_handle = esp_dns_init_doh(&dns_config);
/* or Call esp_dns_init_dot() to use DNS over TLS */
dns_handle = esp_dns_init_dot(&dns_config);
/* or Call esp_dns_init_tcp() to use DNS over TCP */
dns_handle = esp_dns_init_tcp(&dns_config);
/* or Call esp_dns_init_udp() to use DNS over UDP */
dns_handle = esp_dns_init_udp(&dns_config);
if (dns_handle == NULL) {
ESP_LOGE(TAG, "Failed to initialize DNS");
return;
}
```
### 3. Resolve Domain Names
Once initialized, the component automatically handles DNS resolution through the standard `getaddrinfo()` function:
```C
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo res;
int err = getaddrinfo("www.example.com", "80", &hints, &res);
if (err != 0) {
ESP_LOGE(TAG, "DNS lookup failed: %d", err);
return;
}
/* Use the resolved addresses */
/* ... */
/* Free the address info when done */
freeaddrinfo(res);
```
### 4. Cleanup
When you're done using the DNS component, clean up resources based on the protocol used:
```C
int ret = 0;
/* Call esp_dns_cleanup_doh() to cleanup DNS over HTTPS */
ret = esp_dns_cleanup_doh(dns_handle);
/* or Call esp_dns_cleanup_dot() to cleanup DNS over TLS */
ret = esp_dns_cleanup_dot(dns_handle);
/* or Call esp_dns_cleanup_tcp() to cleanup DNS over TCP */
ret = esp_dns_cleanup_tcp(dns_handle);
/* or Call esp_dns_cleanup_udp() to cleanup DNS over UDP */
ret = esp_dns_cleanup_udp(dns_handle);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to cleanup DNS");
}
```
## Configuration
### Setting Up the ESP DNS Component
1. Navigate to your project directory.
2. Execute `idf.py menuconfig`.
3. Locate the **Component config -> LWIP -> Hooks -> Netconn external resolve Hook** section.
4. Change the setting to `Custom implementation`.
### Common Settings
| Parameter | Description | Default Value |
|-----------|-------------|---------------|
| `dns_server` | IP address or hostname of DNS server | `"8.8.8.8"` (Google DNS) |
| `port` | Server port number | Protocol-dependent (53, 853, or 443) |
| `timeout_ms` | Query timeout in milliseconds | `10000` (10 seconds) |
### TLS Configuration (for DoT and DoH)
| Parameter | Description |
|-----------|-------------|
| `crt_bundle_attach` | Function pointer to attach certificate bundle |
| `server_cert` | SSL server certificate in PEM format |
| `alpn_protos` | ALPN protocols for DoH (typically `"h2"`) |
### Protocol-Specific Options
#### DoH Options
- **URL Path**: URL path for DoH service (e.g., "/dns-query")
## Certificate Options
When using secure DNS protocols (DoT and DoH), you have two certificate options:
1. **Certificate Bundle**: Use ESP-IDF's certificate bundle for validating connections to popular DNS providers.
2. **Custom Certificate**: Provide your own certificate in PEM format for custom DNS servers.
## Limitations
- The UDP DNS protocol implementation relies on the native LWIP DNS resolver.
- Transport protocol selection must be configured through `esp_dns_init_xxx()` rather than `getaddrinfo()` parameters due to LWIP resolver hook limitations.
- Maximum response size is limited by the buffer size (default: 512 bytes) for DNS over TLS (DOT) and TCP protocols.
- Only one DNS protocol can be active at a time.
- **Resolution Speed**:
- UDP DNS is fastest but least secure
- DoH typically has the highest latency but offers the best security
## Performance Considerations
- **Memory Usage**: DoH and DoT require more memory due to TLS overhead:
TBD: Fill in the memory usage for each protocol
## How It Works
This component utilizes the `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM` hook to override the core DNS functionality of LWIP and implement custom DNS over HTTPS resolution. To enable this, ensure that the configuration option `Component config → LWIP → Hooks → Netconn external resolve Hook` is set to `Custom implementation`.
Once you add this component to your project, it will replace the default LWIP DNS resolution automatically.
**⚠️ Warning:** This component cannot work alongside other components that use the `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM` hook.
## Troubleshooting
- **Connection Issues**:
- Ensure network connectivity and correct DNS server configuration
- Verify that your network allows the required ports (53, 853, or 443)
- **Certificate Errors**:
- Verify that the correct certificate is provided for secure protocols
- For public DNS servers, use the certificate bundle approach
- **Timeout Errors**:
- Increase the timeout value for slow network connections
- Try a different DNS server that might be geographically closer
- **Memory Issues**:
- If you encounter memory errors, consider increasing the task stack size
- For memory-constrained devices, prefer UDP DNS.

View File

@ -0,0 +1,157 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file esp_dns.c
* @brief Custom DNS module for ESP32 with multiple protocol support
*
* This module provides DNS resolution capabilities with support for various protocols:
* - Standard UDP/TCP DNS (Port 53)
* - DNS over TLS (DoT) (Port 853)
* - DNS over HTTPS (DoH) (Port 443)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "esp_log.h"
#include "esp_dns_priv.h"
#include "esp_dns.h"
#define TAG "ESP_DNS"
/* Global DNS handle instance */
esp_dns_handle_t g_dns_handle = NULL;
/* Mutex for protecting global handle access */
static SemaphoreHandle_t s_dns_global_mutex = NULL;
/**
* @brief Creates or returns a singleton DNS handle instance
*
* This function implements a singleton pattern for the DNS handle. It creates
* a static instance of the dns_handle structure on first call and initializes
* it to zeros. On subsequent calls, it returns a pointer to the same instance.
*
* The function ensures that only one DNS handle exists throughout the application
* lifecycle, which helps manage resources efficiently.
*
* @return Pointer to the singleton DNS handle instance
*/
static esp_dns_handle_t esp_dns_create_handle(void)
{
static struct esp_dns_handle instance;
static bool initialized = false;
if (!initialized) {
memset(&instance, 0, sizeof(instance));
initialized = true;
}
return &instance;
}
/**
* @brief Initialize the DNS module with provided configuration
*
* @param config DNS configuration parameters
*
* @return On success, returns a handle to the initialized DNS module
* On failure, returns NULL
*/
esp_dns_handle_t esp_dns_init(const esp_dns_config_t *config)
{
/* Create global mutex if it doesn't exist */
if (s_dns_global_mutex == NULL) {
s_dns_global_mutex = xSemaphoreCreateMutex();
if (s_dns_global_mutex == NULL) {
ESP_LOGE(TAG, "Failed to create global mutex");
return NULL;
}
}
/* Take the global mutex */
if (xSemaphoreTake(s_dns_global_mutex, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Failed to take global mutex");
return NULL;
}
/* Check if we need to clean up an existing handle */
if (g_dns_handle != NULL) {
ESP_LOGE(TAG, "DNS handle already initialized. Call esp_dns_cleanup() before reinitializing");
xSemaphoreGive(s_dns_global_mutex);
return NULL;
}
/* Allocate memory for the new handle */
esp_dns_handle_t handle = esp_dns_create_handle();
if (handle == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for DNS handle");
xSemaphoreGive(s_dns_global_mutex);
return NULL;
}
/* Copy configuration */
memcpy(&handle->config, config, sizeof(esp_dns_config_t));
/* Create mutex for this handle */
handle->lock = xSemaphoreCreateMutex();
if (handle->lock == NULL) {
ESP_LOGE(TAG, "Failed to create handle mutex");
free(handle);
xSemaphoreGive(s_dns_global_mutex);
return NULL;
}
/* Set global handle */
g_dns_handle = handle;
handle->initialized = true;
/* Release global mutex */
xSemaphoreGive(s_dns_global_mutex);
return handle;
}
/**
* @brief Cleanup and release resources associated with a DNS module handle
*
* @param handle DNS module handle previously obtained from esp_dns_init()
*
* @return 0 on success, non-zero error code on failure
*/
int esp_dns_cleanup(esp_dns_handle_t handle)
{
/* Take the handle mutex */
if (xSemaphoreTake(handle->lock, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Failed to take handle mutex during cleanup");
return -1;
}
/* Release and delete mutex */
xSemaphoreGive(handle->lock);
vSemaphoreDelete(handle->lock);
/* Take global mutex before modifying global handle */
if (s_dns_global_mutex != NULL && xSemaphoreTake(s_dns_global_mutex, portMAX_DELAY) == pdTRUE) {
/* Clear global handle if it matches this one */
if (g_dns_handle == handle) {
g_dns_handle = NULL;
}
xSemaphoreGive(s_dns_global_mutex);
}
/* Mark as uninitialized */
handle->initialized = false;
return 0;
}

View File

@ -0,0 +1,309 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_event.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_http_client.h"
#include "esp_dns_utils.h"
#include "esp_dns_priv.h"
#include "esp_dns.h"
#define TAG "ESP_DNS_DOH"
#define SERVER_URL_MAX_SZ 256
/**
* @brief Initializes the DNS over HTTPS (DoH) module
*
* Sets up the DoH service using the provided configuration. Validates the parameters,
* sets the protocol, and initializes the DNS module. Returns a handle for further use.
*
* @param config Pointer to the DNS configuration structure, which must be initialized
*
* @return On success, returns a handle to the initialized DoH module; returns NULL on failure
*/
esp_dns_handle_t esp_dns_init_doh(esp_dns_config_t *config)
{
ESP_LOGD(TAG, "Initializing DNS over HTTPS");
/* Validate parameters */
if (config == NULL) {
ESP_LOGE(TAG, "Invalid configuration (NULL)");
return NULL;
}
config->protocol = ESP_DNS_PROTOCOL_DOH;
esp_dns_handle_t handle = esp_dns_init(config);
if (handle == NULL) {
ESP_LOGE(TAG, "Failed to initialize DNS");
return NULL;
}
ESP_LOGD(TAG, "DNS module initialized successfully with protocol DNS Over HTTPS(%d)", config->protocol);
return handle;
}
/**
* @brief Cleans up the DNS over HTTPS (DoH) module
*
* Releases resources allocated for the DoH service. Validates the parameters,
* checks the protocol, and cleans up the DNS module.
*
* @param handle Pointer to the DNS handle to be cleaned up
*
* @return 0 on success, or -1 on failure
*/
int esp_dns_cleanup_doh(esp_dns_handle_t handle)
{
ESP_LOGD(TAG, "Cleaning up DNS over HTTPS");
/* Validate parameters */
if (handle == NULL) {
ESP_LOGE(TAG, "Invalid handle (NULL)");
return -1;
}
if (handle->config.protocol != ESP_DNS_PROTOCOL_DOH) {
ESP_LOGW(TAG, "Unknown protocol during cleanup: %d", handle->config.protocol);
return -1;
}
int ret = esp_dns_cleanup(handle);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to cleanup DNS");
return ret;
}
/* Empty the handle */
memset(handle, 0, sizeof(*handle));
ESP_LOGD(TAG, "DNS module cleaned up DNS Over HTTPS successfully");
return 0;
}
/**
* @brief HTTP event handler for DNS over HTTPS requests
*
* Handles HTTP events during DNS over HTTPS communication, including data reception,
* connection status, and error conditions.
*
* @param evt Pointer to the HTTP client event structure
*
* @return ESP_OK on success, or an error code on failure
*/
esp_err_t esp_dns_http_event_handler(esp_http_client_event_t *evt)
{
char *temp_buff = NULL;
size_t temp_buff_len = 0;
esp_dns_handle_t handle = (esp_dns_handle_t)evt->user_data;
switch (evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
/* Check if buffer is null, if yes, initialize it */
if (handle->response_buffer.buffer == NULL) {
if (evt->data_len == 0) {
ESP_LOGW(TAG, "Received empty HTTP data");
return ESP_ERR_INVALID_ARG;
}
temp_buff = malloc(evt->data_len);
if (temp_buff) {
handle->response_buffer.buffer = temp_buff;
handle->response_buffer.length = evt->data_len;
memcpy(handle->response_buffer.buffer, evt->data, evt->data_len);
} else {
ESP_LOGE(TAG, "Buffer allocation error");
return ESP_ERR_NO_MEM;
}
} else {
/* Reallocate buffer to hold the new data chunk */
int new_len = handle->response_buffer.length + evt->data_len;
if (new_len == 0) {
ESP_LOGW(TAG, "New data length is zero after receiving HTTP data");
return ESP_ERR_INVALID_ARG;
}
temp_buff = realloc(handle->response_buffer.buffer, new_len);
if (temp_buff) {
handle->response_buffer.buffer = temp_buff;
memcpy(handle->response_buffer.buffer + handle->response_buffer.length, evt->data, evt->data_len);
handle->response_buffer.length = new_len;
} else {
ESP_LOGE(TAG, "Buffer allocation error");
return ESP_ERR_NO_MEM;
}
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
/* Entire response received, process it here */
ESP_LOGD(TAG, "Received full response, length: %d", handle->response_buffer.length);
/* Check if the buffer indicates an HTTP error response */
if (HttpStatus_Ok == esp_http_client_get_status_code(evt->client)) {
/* Parse the DNS response */
esp_dns_parse_response((uint8_t *)handle->response_buffer.buffer,
handle->response_buffer.length,
&handle->response_buffer.dns_response);
} else {
ESP_LOGE(TAG, "HTTP Error: %d", esp_http_client_get_status_code(evt->client));
temp_buff_len = handle->response_buffer.length > ESP_DNS_BUFFER_SIZE ? ESP_DNS_BUFFER_SIZE : handle->response_buffer.length;
ESP_LOG_BUFFER_HEXDUMP(TAG, handle->response_buffer.buffer, temp_buff_len, ESP_LOG_ERROR);
handle->response_buffer.dns_response.status_code = ERR_VAL; /* TBD: Not handled properly yet */
}
free(handle->response_buffer.buffer);
handle->response_buffer.buffer = NULL;
handle->response_buffer.length = 0;
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
break;
case HTTP_EVENT_REDIRECT:
ESP_LOGE(TAG, "HTTP_EVENT_REDIRECT: Not supported(%d)", esp_http_client_get_status_code(evt->client));
break;
}
return ESP_OK;
}
/**
* @brief Resolves a hostname using DNS over HTTPS
*
* This function generates a DNS request, sends it via HTTPS, and processes
* the response to extract IP addresses.
*
* @param handle Pointer to the DNS handle
* @param name The hostname to resolve
* @param addr Pointer to store the resolved IP addresses
* @param rrtype The address RR type (A or AAAA)
*
* @return ERR_OK on success, or an error code on failure
*/
err_t dns_resolve_doh(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype)
{
uint8_t buffer_qry[ESP_DNS_BUFFER_SIZE];
/* Initialize error status */
err_t err = ERR_OK;
const char *prefix = "https://";
/* Set default values for DoH configuration if not specified */
const char *url_path = handle->config.protocol_config.doh_config.url_path ?
handle->config.protocol_config.doh_config.url_path : "dns-query";
int port = handle->config.port ?
handle->config.port : ESP_DNS_DEFAULT_DOH_PORT;
/* Calculate required URL length: https:// + server + / + path + null terminator */
size_t url_len = strlen(prefix) + \
strlen(handle->config.dns_server) + 1 + \
strlen(url_path) + 1; /* 1 for '/' and 1 for '\0' */
/* Allocate memory for the full server URL */
char *dns_server_url = malloc(url_len);
if (dns_server_url == NULL) {
ESP_LOGE(TAG, "Memory allocation failed");
return ERR_MEM;
}
/* Construct the complete server URL by combining prefix, server and path */
snprintf(dns_server_url, url_len, "%s%s/%s", prefix,
handle->config.dns_server,
url_path);
/* Configure the HTTP client with base settings */
esp_http_client_config_t config = {
.url = dns_server_url,
.event_handler = esp_dns_http_event_handler,
.method = HTTP_METHOD_POST,
.user_data = handle,
.port = port,
};
/* Configure TLS certificate settings - either using bundle or PEM cert */
if (handle->config.tls_config.crt_bundle_attach) {
config.crt_bundle_attach = handle->config.tls_config.crt_bundle_attach;
} else {
config.cert_pem = handle->config.tls_config.cert_pem; /* Use the root certificate for dns.google if needed */
}
/* Clear the response buffer to ensure no residual data remains */
memset(&handle->response_buffer, 0, sizeof(response_buffer_t));
/* Create DNS query in wire format */
size_t query_size = esp_dns_create_query(buffer_qry, sizeof(buffer_qry), name, rrtype, &handle->response_buffer.dns_response.id);
if (query_size == -1) {
ESP_LOGE(TAG, "Error: Hostname too big");
err = ERR_MEM;
goto cleanup;
}
/* Initialize HTTP client with the configuration */
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Error initializing HTTP client");
err = ERR_VAL;
goto cleanup;
}
/* Set Content-Type header for DNS-over-HTTPS */
esp_err_t ret = esp_http_client_set_header(client, "Content-Type", "application/dns-message");
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error setting HTTP header: %s", esp_err_to_name(ret));
err = ERR_VAL;
goto client_cleanup;
}
/* Set the DNS query as POST data */
ret = esp_http_client_set_post_field(client, (const char *)buffer_qry, query_size);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error setting POST field: %s", esp_err_to_name(ret));
err = ERR_VAL;
goto client_cleanup;
}
/* Execute the HTTP request */
ret = esp_http_client_perform(client);
if (ret == ESP_OK) {
ESP_LOGD(TAG, "HTTP POST Status = %d, content_length = %lld",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
/* Verify HTTP status code and DNS response status */
if ((HttpStatus_Ok != esp_http_client_get_status_code(client)) ||
(handle->response_buffer.dns_response.status_code != ERR_OK)) {
err = ERR_ARG;
goto client_cleanup;
}
/* Extract IP addresses from DNS response */
err = esp_dns_extract_ip_addresses_from_response(&handle->response_buffer.dns_response, addr);
} else {
ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(ret));
err = ERR_VAL;
}
/* Clean up HTTP client */
client_cleanup:
esp_http_client_cleanup(client);
/* Free allocated memory */
cleanup:
free(dns_server_url);
return err;
}

View File

@ -0,0 +1,205 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_transport.h"
#include "esp_transport_ssl.h"
#include "esp_dns_priv.h"
#include "esp_dns.h"
#define TAG "ESP_DNS_DOT"
/**
* @brief Initializes the DNS over TLS (DoT) module
*
* Sets up the DoT service using the provided configuration. Validates the parameters,
* sets the protocol, and initializes the DNS module. Returns a handle for further use.
*
* @param config Pointer to the DNS configuration structure, which must be initialized
*
* @return On success, returns a handle to the initialized DoT module; returns NULL on failure
*/
esp_dns_handle_t esp_dns_init_dot(esp_dns_config_t *config)
{
ESP_LOGD(TAG, "Initializing DNS over TLS");
/* Validate parameters */
if (config == NULL) {
ESP_LOGE(TAG, "Invalid configuration (NULL)");
return NULL;
}
config->protocol = ESP_DNS_PROTOCOL_DOT;
esp_dns_handle_t handle = esp_dns_init(config);
if (handle == NULL) {
ESP_LOGE(TAG, "Failed to initialize DNS");
return NULL;
}
ESP_LOGD(TAG, "DNS module initialized successfully with protocol DNS Over TLS(%d)", config->protocol);
return handle;
}
/**
* @brief Cleans up the DNS over TLS (DoT) module
*
* Releases resources allocated for the DoT service. Validates the parameters,
* checks the protocol, and cleans up the DNS module.
*
* @param handle Pointer to the DNS handle to be cleaned up
*
* @return 0 on success, or -1 on failure
*/
int esp_dns_cleanup_dot(esp_dns_handle_t handle)
{
ESP_LOGD(TAG, "Cleaning up DNS over TLS");
/* Validate parameters */
if (handle == NULL) {
ESP_LOGE(TAG, "Invalid handle (NULL)");
return -1;
}
if (handle->config.protocol != ESP_DNS_PROTOCOL_DOT) {
ESP_LOGW(TAG, "Unknown protocol during cleanup: %d", handle->config.protocol);
return -1;
}
int ret = esp_dns_cleanup(handle);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to cleanup DNS");
return ret;
}
/* Empty the handle */
memset(handle, 0, sizeof(*handle));
ESP_LOGD(TAG, "DNS module cleaned up DNS Over TLS successfully");
return 0;
}
/**
* @brief Resolves a hostname using DNS over TLS (DoT)
*
* Performs DNS resolution over a TLS-encrypted connection. Creates a DNS query,
* establishes a TLS connection, sends the query, and processes the response.
*
* @param handle Pointer to the DNS handle
* @param name Hostname to resolve
* @param addr Pointer to store the resolved IP address
* @param rrtype DNS record type to query
*
* @return ERR_OK on success, or an error code on failure
*/
err_t dns_resolve_dot(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype)
{
int err = ERR_OK;
esp_transport_handle_t transport = NULL;
int len = 0;
char dot_buffer[ESP_DNS_BUFFER_SIZE];
size_t query_size;
int timeout_ms;
int dot_port;
if (addr == NULL) {
return ERR_ARG;
}
/* Set timeout and port values, using defaults if not specified in config */
timeout_ms = handle->config.timeout_ms ? : ESP_DNS_DEFAULT_TIMEOUT_MS;
dot_port = handle->config.port ? : ESP_DNS_DEFAULT_DOT_PORT;
/* Clear the response buffer to ensure no residual data remains */
memset(&handle->response_buffer, 0, sizeof(response_buffer_t));
/* Create DNS query in wire format, leaving 2 bytes at start for length prefix as required by RFC 7858 */
memset(dot_buffer, 0, ESP_DNS_BUFFER_SIZE);
query_size = esp_dns_create_query((uint8_t *)(dot_buffer + 2), sizeof(dot_buffer) - 2,
name, rrtype, &handle->response_buffer.dns_response.id);
if (query_size == -1) {
ESP_LOGE(TAG, "Error: Hostname too big");
return ERR_MEM;
}
/* Prepends the 2-byte length field to DNS messages as required by RFC 7858 */
dot_buffer[0] = (query_size >> 8) & 0xFF;
dot_buffer[1] = query_size & 0xFF;
transport = esp_transport_ssl_init();
if (!transport) {
ESP_LOGE(TAG, "Failed to initialize transport");
return ERR_MEM;
}
/* Configure TLS certificate settings - either using bundle or PEM cert */
if (handle->config.tls_config.crt_bundle_attach) {
esp_transport_ssl_crt_bundle_attach(transport, handle->config.tls_config.crt_bundle_attach);
} else {
if (handle->config.tls_config.cert_pem == NULL) {
ESP_LOGE(TAG, "Certificate PEM data is null");
err = ERR_VAL;
goto cleanup;
}
esp_transport_ssl_set_cert_data(transport,
handle->config.tls_config.cert_pem,
strlen(handle->config.tls_config.cert_pem));
}
if (esp_transport_connect(transport, handle->config.dns_server, dot_port, timeout_ms) < 0) {
ESP_LOGE(TAG, "TLS connection failed");
err = ERR_CONN;
goto cleanup;
}
/* Send DNS query */
len = esp_transport_write(transport,
dot_buffer,
query_size + 2,
timeout_ms);
if (len < 0) {
ESP_LOGE(TAG, "Failed to send DNS query");
err = ERR_ABRT;
goto cleanup;
}
/* Read response */
memset(dot_buffer, 0, ESP_DNS_BUFFER_SIZE);
len = esp_transport_read(transport,
dot_buffer,
sizeof(dot_buffer),
timeout_ms);
if (len > 0) {
/* Skip the 2-byte length field that prepends DNS messages as required by RFC 7858 */
handle->response_buffer.buffer = dot_buffer + 2;
handle->response_buffer.length = len - 2;
/* Parse the DNS response */
esp_dns_parse_response((uint8_t *)handle->response_buffer.buffer,
handle->response_buffer.length,
&handle->response_buffer.dns_response);
/* Extract IP addresses from DNS response */
err = esp_dns_extract_ip_addresses_from_response(&handle->response_buffer.dns_response, addr);
if (err != ERR_OK) {
ESP_LOGE(TAG, "Failed to extract IP address from DNS response");
goto cleanup;
}
} else {
ESP_LOGE(TAG, "Failed to receive response");
err = ERR_ABRT;
goto cleanup;
}
cleanup:
if (transport) {
esp_transport_close(transport);
esp_transport_destroy(transport);
}
return err;
}

View File

@ -0,0 +1,111 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file esp_dns_lwip.c
* @brief Custom DNS module for ESP32 with multiple protocol support
*
* Provides DNS resolution capabilities with support for various protocols:
* - Standard UDP/TCP DNS (Port 53)
* - DNS over TLS (DoT) (Port 853)
* - DNS over HTTPS (DoH) (Port 443)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "esp_log.h"
#include "esp_dns.h"
#include "esp_dns_priv.h"
#define TAG "ESP_DNS_LWIP"
/* Global DNS handle instance */
extern esp_dns_handle_t g_dns_handle;
/* ========================= LWIP HOOK FUNCTIONS ========================= */
#if defined(CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM)
/**
* @brief Custom DNS resolution hook for lwIP network connections
*
* @param name Hostname to resolve
* @param addr Pointer to store resolved IP address
* @param addrtype Type of address to resolve (IPv4/IPv6)
* @param err Pointer to store error code
*
* @return int 0 if resolution should be handled by lwIP, 1 if handled by this module
*/
int lwip_hook_netconn_external_resolve(const char *name, ip_addr_t *addr, u8_t addrtype, err_t *err)
{
if (g_dns_handle == NULL) {
ESP_LOGD(TAG, "ESP_DNS module not initialized, resolving through native DNS");
*err = ERR_OK;
return 0;
}
if (name == NULL || addr == NULL || err == NULL) {
if (err) {
*err = ERR_ARG;
}
return 1;
}
/* Check if name is already an IP address */
if (ipaddr_aton(name, addr)) {
*err = ERR_OK;
return 0;
}
/* Check if DNS server name matches or if it's localhost */
if ((strcmp(name, g_dns_handle->config.dns_server) == 0) ||
#if LWIP_HAVE_LOOPIF
(strcmp(name, "localhost") == 0) ||
#endif
ipaddr_aton(name, addr)) {
return 0;
}
u8_t rrtype;
if ((addrtype == NETCONN_DNS_IPV4) || (addrtype == NETCONN_DNS_IPV4_IPV6)) {
rrtype = DNS_RRTYPE_A;
} else if ((addrtype == NETCONN_DNS_IPV6) || (addrtype == NETCONN_DNS_IPV6_IPV4)) {
rrtype = DNS_RRTYPE_AAAA;
} else {
ESP_LOGE(TAG, "Invalid address type");
*err = ERR_VAL;
return 1;
}
/* Resolve based on configured transport type */
switch (g_dns_handle->config.protocol) {
case ESP_DNS_PROTOCOL_UDP:
/* Return zero as lwIP DNS can handle UDP DNS */
return 0;
case ESP_DNS_PROTOCOL_TCP:
*err = dns_resolve_tcp(g_dns_handle, name, addr, rrtype);
break;
case ESP_DNS_PROTOCOL_DOT:
*err = dns_resolve_dot(g_dns_handle, name, addr, rrtype);
break;
case ESP_DNS_PROTOCOL_DOH:
*err = dns_resolve_doh(g_dns_handle, name, addr, rrtype);
break;
default:
ESP_LOGE(TAG, "Invalid transport type");
*err = ERR_VAL;
}
return 1;
}
#else
#error "CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not defined. Please enable it in your menuconfig"
#endif /* CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM */

View File

@ -0,0 +1,116 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @file esp_dns_priv.h
* @brief Private header for ESP DNS module
*
* This module provides DNS resolution capabilities with support for various protocols:
* - Standard UDP/TCP DNS (Port 53)
* - DNS over TLS (DoT)
* - DNS over HTTPS (DoH)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "lwip/prot/dns.h"
#include "lwip/ip_addr.h"
#include "lwip/err.h"
#include "esp_log.h"
#include "esp_dns.h"
#include "esp_dns_utils.h"
/**
* @brief Opaque handle type for DNS module instances
*/
struct esp_dns_handle {
/* Configuration */
esp_dns_config_t config; /* Copy of user configuration */
/* Connection state */
bool initialized; /* Flag indicating successful initialization */
response_buffer_t response_buffer; /* Buffer for storing DNS response data during processing */
/* Thread safety */
SemaphoreHandle_t lock; /* Mutex for synchronization */
};
/**
* @brief Initialize DNS module with configuration
*
* @param config DNS configuration parameters
*
* @return esp_dns_handle_t Handle to DNS module instance
*/
esp_dns_handle_t esp_dns_init(const esp_dns_config_t *config);
/**
* @brief Clean up DNS module resources
*
* @param handle DNS module handle
*
* @return int 0 on success, negative error code on failure
*/
int esp_dns_cleanup(esp_dns_handle_t handle);
/**
* @brief Resolve hostname using DNS over HTTPS
*
* @param handle DNS module handle
* @param name Hostname to resolve
* @param addr Pointer to store resolved IP address
* @param rrtype Record type (A or AAAA)
*
* @return err_t Error code
*/
err_t dns_resolve_doh(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype);
/**
* @brief Resolve hostname using DNS over TLS
*
* @param handle DNS module handle
* @param name Hostname to resolve
* @param addr Pointer to store resolved IP address
* @param rrtype Record type (A or AAAA)
*
* @return err_t Error code
*/
err_t dns_resolve_dot(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype);
/**
* @brief Resolve hostname using TCP DNS
*
* @param handle DNS module handle
* @param name Hostname to resolve
* @param addr Pointer to store resolved IP address
* @param rrtype Record type (A or AAAA)
*
* @return err_t Error code
*/
err_t dns_resolve_tcp(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype);
/**
* @brief Resolve hostname using UDP DNS
*
* @param handle DNS module handle
* @param name Hostname to resolve
* @param addr Pointer to store resolved IP address
* @param rrtype Record type (A or AAAA)
*
* @return err_t Error code
*/
err_t dns_resolve_udp(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype);

View File

@ -0,0 +1,188 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_transport.h"
#include "esp_transport_tcp.h"
#include "esp_dns_priv.h"
#include "esp_dns.h"
#define TAG "ESP_DNS_TCP"
/**
* @brief Initializes the TCP DNS module
*
* Sets up the TCP DNS service using the provided configuration. Validates the parameters,
* sets the protocol, and initializes the DNS module.
*
* @param config Pointer to the DNS configuration structure
*
* @return Handle to the initialized TCP module on success, NULL on failure
*/
esp_dns_handle_t esp_dns_init_tcp(esp_dns_config_t *config)
{
ESP_LOGD(TAG, "Initializing TCP DNS");
/* Validate parameters */
if (config == NULL) {
ESP_LOGE(TAG, "Invalid configuration (NULL)");
return NULL;
}
config->protocol = ESP_DNS_PROTOCOL_TCP;
esp_dns_handle_t handle = esp_dns_init(config);
if (handle == NULL) {
ESP_LOGE(TAG, "Failed to initialize DNS");
return NULL;
}
ESP_LOGD(TAG, "DNS module initialized successfully with protocol DNS Over TCP(%d)", config->protocol);
return handle;
}
/**
* @brief Cleans up the TCP DNS module
*
* Releases resources allocated for the TCP DNS service. Validates the parameters,
* checks the protocol, and cleans up the DNS module.
*
* @param handle Pointer to the DNS handle to be cleaned up
*
* @return 0 on success, -1 on failure
*/
int esp_dns_cleanup_tcp(esp_dns_handle_t handle)
{
ESP_LOGD(TAG, "Cleaning up TCP DNS");
/* Validate parameters */
if (handle == NULL) {
ESP_LOGE(TAG, "Invalid handle (NULL)");
return -1;
}
if (handle->config.protocol != ESP_DNS_PROTOCOL_TCP) {
ESP_LOGW(TAG, "Unknown protocol during cleanup: %d", handle->config.protocol);
return -1;
}
int ret = esp_dns_cleanup(handle);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to cleanup DNS");
return ret;
}
/* Empty the handle */
memset(handle, 0, sizeof(*handle));
ESP_LOGD(TAG, "DNS module cleaned up DNS Over TCP successfully");
return 0;
}
/**
* @brief Resolves a hostname using TCP DNS
*
* Performs DNS resolution over TCP for the given hostname. Creates a TCP connection,
* sends the DNS query, and processes the response.
*
* @param handle DNS handle
* @param name Hostname to resolve
* @param addr Pointer to store the resolved IP address
* @param rrtype DNS record type
*
* @return ERR_OK on success, error code on failure
*/
err_t dns_resolve_tcp(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype)
{
int err = ERR_OK;
esp_transport_handle_t transport = NULL;
int len = 0;
char tcp_buffer[ESP_DNS_BUFFER_SIZE];
size_t query_size;
int timeout_ms;
int tcp_port;
if (addr == NULL) {
return ERR_ARG;
}
/* Set timeout and port values, using defaults if not specified in config */
timeout_ms = handle->config.timeout_ms ? : ESP_DNS_DEFAULT_TIMEOUT_MS;
tcp_port = handle->config.port ? : ESP_DNS_DEFAULT_TCP_PORT;
/* Clear the response buffer to ensure no residual data remains */
memset(&handle->response_buffer, 0, sizeof(response_buffer_t));
/* Create DNS query in wire format, leaving 2 bytes at start for length prefix as required by RFC 7858 */
memset(tcp_buffer, 0, ESP_DNS_BUFFER_SIZE);
query_size = esp_dns_create_query((uint8_t *)(tcp_buffer + 2), sizeof(tcp_buffer) - 2,
name, rrtype, &handle->response_buffer.dns_response.id);
if (query_size == -1) {
ESP_LOGE(TAG, "Error: Hostname too big");
return ERR_MEM;
}
/* Prepends the 2-byte length field to DNS messages as required by RFC 7858 */
tcp_buffer[0] = (query_size >> 8) & 0xFF;
tcp_buffer[1] = query_size & 0xFF;
transport = esp_transport_tcp_init();
if (!transport) {
ESP_LOGE(TAG, "Failed to initialize transport");
return ERR_MEM;
}
if (esp_transport_connect(transport, handle->config.dns_server, tcp_port, timeout_ms) < 0) {
ESP_LOGE(TAG, "TCP connection failed");
err = ERR_CONN;
goto cleanup;
}
/* Send DNS query */
len = esp_transport_write(transport,
tcp_buffer,
query_size + 2,
timeout_ms);
if (len < 0) {
ESP_LOGE(TAG, "Failed to send DNS query");
err = ERR_ABRT;
goto cleanup;
}
/* Read response */
memset(tcp_buffer, 0, ESP_DNS_BUFFER_SIZE);
len = esp_transport_read(transport,
tcp_buffer,
sizeof(tcp_buffer),
timeout_ms);
if (len > 0) {
/* Skip the 2-byte length field that prepends DNS messages as required by RFC 7858 */
handle->response_buffer.buffer = tcp_buffer + 2;
handle->response_buffer.length = len - 2;
/* Parse the DNS response */
esp_dns_parse_response((uint8_t *)handle->response_buffer.buffer,
handle->response_buffer.length,
&handle->response_buffer.dns_response);
/* Extract IP addresses from DNS response */
err = esp_dns_extract_ip_addresses_from_response(&handle->response_buffer.dns_response, addr);
if (err != ERR_OK) {
ESP_LOGE(TAG, "Failed to extract IP address from DNS response");
goto cleanup;
}
} else {
ESP_LOGE(TAG, "Failed to receive response");
err = ERR_ABRT;
goto cleanup;
}
cleanup:
if (transport) {
esp_transport_close(transport);
esp_transport_destroy(transport);
}
return err;
}

View File

@ -0,0 +1,110 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_dns_priv.h"
#include "esp_dns.h"
#define TAG "ESP_DNS_UDP"
/**
* @brief Initializes the UDP DNS module
*
* Sets up the UDP DNS service using the provided configuration. Validates the parameters,
* sets the protocol, and initializes the DNS module.
*
* @param config Pointer to the DNS configuration structure
*
* @return Handle to the initialized UDP module on success, NULL on failure
*/
esp_dns_handle_t esp_dns_init_udp(esp_dns_config_t *config)
{
ESP_LOGD(TAG, "Initializing UDP DNS");
/* Validate parameters */
if (config == NULL) {
ESP_LOGE(TAG, "Invalid configuration (NULL)");
return NULL;
}
config->protocol = ESP_DNS_PROTOCOL_UDP;
esp_dns_handle_t handle = esp_dns_init(config);
if (handle == NULL) {
ESP_LOGE(TAG, "Failed to initialize DNS");
return NULL;
}
ESP_LOGD(TAG, "DNS module initialized successfully with protocol DNS Over UDP(%d)", config->protocol);
return handle;
}
/**
* @brief Cleans up the UDP DNS module
*
* Releases resources allocated for the UDP DNS service. Validates the parameters,
* checks the protocol, and cleans up the DNS module.
*
* @param handle Pointer to the DNS handle to be cleaned up
*
* @return 0 on success, -1 on failure
*/
int esp_dns_cleanup_udp(esp_dns_handle_t handle)
{
ESP_LOGD(TAG, "Cleaning up UDP DNS");
/* Validate parameters */
if (handle == NULL) {
ESP_LOGE(TAG, "Invalid handle (NULL)");
return -1;
}
if (handle->config.protocol != ESP_DNS_PROTOCOL_UDP) {
ESP_LOGW(TAG, "Unknown protocol during cleanup: %d", handle->config.protocol);
return -1;
}
int ret = esp_dns_cleanup(handle);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to cleanup DNS");
return ret;
}
/* Empty the handle */
memset(handle, 0, sizeof(*handle));
ESP_LOGD(TAG, "DNS module cleaned up DNS Over UDP successfully");
return 0;
}
/**
* @brief Resolves a hostname using UDP DNS
*
* Performs DNS resolution over UDP for the given hostname. Creates a UDP connection,
* sends the DNS query, and processes the response.
*
* @note This function is a placeholder and does not contain the actual implementation
* for UDP DNS resolution. The implementation needs to be added.
* As of now the resolution is performed by lwip dns module.
*
* @param handle DNS handle
* @param name Hostname to resolve
* @param addr Pointer to store the resolved IP address
* @param rrtype DNS record type
*
* @return ERR_OK on success, error code on failure
*/
err_t dns_resolve_udp(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype)
{
// TBD: Implement UDP DNS resolution
if (addr == NULL) {
return ERR_ARG;
}
return ERR_OK;
}

View File

@ -0,0 +1,242 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <arpa/inet.h>
#include "esp_random.h"
#include "esp_dns_utils.h"
/**
* @brief Creates a DNS query packet in the provided buffer
*
* @param buffer Buffer to store the DNS query
* @param buffer_size Size of the buffer
* @param hostname Domain name to query
* @param addrtype Type of address to query (A or AAAA)
* @param id_o Pointer to store the generated query ID
*
* @return size_t Size of the created query packet, or -1 on error
*/
size_t esp_dns_create_query(uint8_t *buffer, size_t buffer_size, const char *hostname, int addrtype, uint16_t *id_o)
{
/*
* Sample DNS Query for example.com (Type A)
* 0x00, 0x00, // Transaction ID
* 0x01, 0x00, // Flags: Standard query
* 0x00, 0x01, // Questions: 1
* 0x00, 0x00, // Answer RRs: 0
* 0x00, 0x00, // Authority RRs: 0
* 0x00, 0x00, // Additional RRs: 0
* 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', // QNAME: example.com
* 0x03, 'c', 'o', 'm',
* 0x00, // End of QNAME
* 0x00, 0x01, // QTYPE: A (host address)
* 0x00, 0x01 // QCLASS: IN (internet)
*/
dns_header_t *header = (dns_header_t *)buffer;
memset(buffer, 0, buffer_size);
/* Set header fields */
*id_o = (uint16_t)(esp_random() & 0xFFFF); /* Return the id for response validation */
header->id = htons(*id_o); /* Random transaction ID */
header->flags = htons(0x0100); /* Standard query with recursion */
header->qdcount = htons(1); /* One question */
/* Add the question name */
uint8_t *qname = buffer + sizeof(dns_header_t);
const char *dot = hostname;
while (*dot) {
const char *next_dot = strchr(dot, '.');
if (!next_dot) {
next_dot = dot + strlen(dot);
}
uint8_t len = next_dot - dot;
*qname++ = len;
/* Check for buffer overflow */
if ((qname - buffer) > buffer_size) {
return -1;
}
memcpy(qname, dot, len);
qname += len;
dot = (*next_dot) ? next_dot + 1 : next_dot;
}
*qname++ = 0; /* Null-terminate the question name */
/* Set question fields */
dns_question_t *question = (dns_question_t *)qname;
question->qtype = htons(addrtype);
question->qclass = htons(DNS_RRCLASS_IN);
/* Return the total query size */
return (qname + sizeof(dns_question_t)) - buffer;
}
/**
* @brief Skips over a DNS name in a DNS reply message and returns the offset to the end of the name.
*
* This function handles both uncompressed labels and compression pointers according to RFC 1035.
* Reference: RFC 1035, sections 3.1 (Name Space Definitions) and 4.1.4 (Message Compression).
*
* @param ptr Pointer to the start of the DNS name in the DNS message
* @param remaining_bytes Number of bytes remaining in the buffer
*
* @return uint8_t* Pointer to the end of the DNS name, or NULL on error
*/
static uint8_t *skip_dns_name(uint8_t *ptr, size_t remaining_bytes)
{
uint8_t offset = 0;
/* Loop through each part of the name, handling labels and compression pointers */
while (ptr[offset] != 0) {
if (offset >= remaining_bytes) {
return NULL;
}
/* Check if this part is a compression pointer, indicated by the two high bits set to 1 (0xC0) */
/* RFC 1035, Section 4.1.4: Compression pointers */
if ((ptr[offset] & 0xC0) == 0xC0) {
/* Compression pointer is 2 bytes; move offset by 2 and stop */
offset += 2;
return ptr + offset; /* End of name processing due to pointer */
} else {
/* Otherwise, it's a label
RFC 1035, Section 3.1: Labels
- The first byte is the length of this label
- Followed by 'length' bytes of label content */
offset += ptr[offset] + 1; /* Move past this label (1 byte for length + label content) */
}
}
/* RFC 1035, Section 3.1: End of a name is indicated by a zero-length byte (0x00) */
offset += 1; /* Move past the terminating zero byte */
return ptr + offset;
}
/**
* @brief Parses a DNS response message
*
* @param buffer Buffer containing the DNS response
* @param response_size Size of the response buffer
*
* @param dns_response Structure to store parsed response
*/
void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_t *dns_response)
{
/* Validate input buffer */
assert(buffer != NULL);
dns_header_t *header = (dns_header_t *)buffer;
dns_response->status_code = ERR_OK; /* Initialize DNS response code */
/* Check if there are answers and Transaction id matches */
int answer_count = ntohs(header->ancount);
if ((ntohs(header->id) != dns_response->id) || (answer_count == 0)) {
dns_response->status_code = ERR_VAL; /* DNS response code */
return;
}
/* Ensure only MAX_ANSWERS are processed */
dns_response->num_answers = (answer_count < MAX_ANSWERS ? answer_count : MAX_ANSWERS);
/* Skip the header and question section */
uint8_t *ptr = buffer + sizeof(dns_header_t);
/* Skip the question name */
ptr = skip_dns_name(ptr, response_size - (ptr - buffer));
if (ptr == NULL) {
dns_response->status_code = ERR_VAL;
return;
}
/* Skip the question type and class */
ptr += sizeof(dns_question_t);
/* Parse each answer record */
for (int i = 0; i < dns_response->num_answers; i++) {
/* Answer fields */
ptr = skip_dns_name(ptr, response_size - (ptr - buffer));
if (ptr == NULL) {
dns_response->status_code = ERR_VAL;
return;
}
dns_answer_t *answer = (dns_answer_t *)ptr;
uint16_t type = ntohs(answer->type);
uint16_t class = ntohs(answer->class);
uint32_t ttl = ntohl(answer->ttl);
uint16_t data_len = ntohs(answer->data_len);
/* Skip fixed parts of answer (type, class, ttl, data_len) */
ptr += SIZEOF_DNS_ANSWER_FIXED;
/* Validate RR class and ttl */
if ((class != DNS_RRCLASS_IN) || (ttl > DNS_MAX_TTL)) {
dns_response->answers[i].status = ERR_VAL;
goto next_answer;
}
/* Initialize status for this answer */
dns_response->answers[i].status = ERR_OK;
/* Check the type of answer */
if (type == DNS_RRTYPE_A && data_len == 4) {
/* IPv4 Address (A record) */
memcpy(&dns_response->answers[i].ip, ptr, sizeof(struct in_addr));
IP_SET_TYPE(&dns_response->answers[i].ip, IPADDR_TYPE_V4);
} else if (type == DNS_RRTYPE_AAAA && data_len == 16) {
/* IPv6 Address (AAAA record) */
memcpy(&dns_response->answers[i].ip, ptr, sizeof(struct in6_addr));
IP_SET_TYPE(&dns_response->answers[i].ip, IPADDR_TYPE_V6);
} else {
dns_response->answers[i].status = ERR_VAL;
}
next_answer:
/* Move pointer to next answer */
ptr += data_len;
}
}
/**
* @brief Converts a dns_response_t to an array of IP addresses.
*
* This function iterates over the DNS response and extracts valid
* IPv4 and IPv6 addresses, storing them in the provided array.
*
* @param response The DNS response to process
* @param ipaddr Array to store the extracted IP addresses
*
* @return err_t Status of DNS response parsing
*/
err_t esp_dns_extract_ip_addresses_from_response(const dns_response_t *response, ip_addr_t ipaddr[])
{
int count = 0;
memset(ipaddr, 0, DNS_MAX_HOST_IP * sizeof(ip_addr_t));
if (response->status_code != ERR_OK) {
return response->status_code;
}
/* Iterate over the DNS answers */
for (int i = 0; i < response->num_answers && count < DNS_MAX_HOST_IP; i++) {
const dns_answer_storage_t *answer = &response->answers[i];
/* Check if the answer is valid */
if (answer->status != ERR_OK) {
continue;
}
ipaddr[count] = answer->ip;
count++;
}
if (count == 0) {
return ERR_VAL;
}
/* Store the number of valid IP addresses */
return ERR_OK;
}

View File

@ -0,0 +1,139 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_tls.h"
#include "sdkconfig.h"
#include "lwip/prot/dns.h"
#include "lwip/api.h"
#include "lwip/opt.h"
#include "lwip/dns.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief DNS header structure
*
* Contains the basic fields of a DNS message header as defined in RFC 1035
*/
typedef struct {
uint16_t id; /* Identification - unique identifier for the query */
uint16_t flags; /* Flags - control bits for the DNS message */
uint16_t qdcount; /* Number of questions in the question section */
uint16_t ancount; /* Number of answers in the answer section */
uint16_t nscount; /* Number of authority records in the authority section */
uint16_t arcount; /* Number of additional records in the additional section */
} dns_header_t;
/**
* @brief DNS question structure
*
* Represents a single question in the question section of a DNS message
*/
typedef struct {
uint16_t qtype; /* Question type (e.g., A, AAAA, MX) */
uint16_t qclass; /* Question class (e.g., IN for internet) */
} dns_question_t;
/**
* @brief DNS answer message structure
*
* Represents a single resource record in the answer section of a DNS message
* No packing needed as it's only used locally on the stack
*/
typedef struct {
uint16_t type; /* Resource record type (e.g., A, AAAA, MX) */
uint16_t class; /* Resource record class (e.g., IN for internet) */
uint32_t ttl; /* Time-to-live in seconds */
uint16_t data_len; /* Length of the resource data */
} dns_answer_t;
#define SIZEOF_DNS_ANSWER_FIXED 10 /* Size of dns_answer_t structure in bytes */
/** Maximum TTL value for DNS resource records (one week) */
#define DNS_MAX_TTL 604800
#ifndef CONFIG_LWIP_DNS_MAX_HOST_IP
#define CONFIG_LWIP_DNS_MAX_HOST_IP 1
#endif
/** Maximum number of answers that can be stored */
#define MAX_ANSWERS (CONFIG_LWIP_DNS_MAX_HOST_IP)
#define ESP_DNS_BUFFER_SIZE 512
/**
* @brief Structure to store a single DNS answer
*/
typedef struct {
err_t status; /* Status of the answer */
ip_addr_t ip; /* IP address from the answer */
} dns_answer_storage_t;
/**
* @brief Structure to store a complete DNS response
*/
typedef struct {
err_t status_code; /* Overall status of the DNS response */
uint16_t id; /* Transaction ID */
int num_answers; /* Number of valid answers */
dns_answer_storage_t answers[MAX_ANSWERS]; /* Array of answers */
} dns_response_t;
/**
* @brief Buffer structure for DNS response processing
*/
typedef struct {
char *buffer; /* Pointer to response data buffer */
int length; /* Current length of data in buffer */
dns_response_t dns_response; /* Parsed DNS response information */
} response_buffer_t;
/**
* @brief Creates a DNS query for A and AAAA records
*
* @param buffer Buffer to store the query
* @param buffer_size Size of the buffer
* @param hostname Hostname to query
* @param addrtype Address type (A or AAAA)
* @param id_o Pointer to store the generated query ID
*
* @return size_t Size of the created query, or -1 on error
*/
size_t esp_dns_create_query(uint8_t *buffer, size_t buffer_size, const char *hostname, int addrtype, uint16_t *id_o);
/**
* @brief Parses a DNS response message
*
* @param buffer Buffer containing the DNS response
* @param response_size Size of the response
* @param dns_response Structure to store parsed response
*/
void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_t *dns_response);
/**
* @brief Converts a dns_response_t to an array of IP addresses.
*
* This function iterates over the DNS response and extracts valid
* IPv4 and IPv6 addresses, storing them in the provided array.
*
* @param response The DNS response to process.
* @param ipaddr An array to store the extracted IP addresses.
*
* @return err Status of dns response parsing
*/
err_t esp_dns_extract_ip_addresses_from_response(const dns_response_t *response, ip_addr_t ipaddr[]);
#ifdef __cplusplus
}
#endif

View 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(esp_dns_example)

View File

@ -0,0 +1,152 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
# ESP DNS Example
This example demonstrates how to use the ESP DNS component in an ESP32 application. The example resolves domain names using various DNS protocols including standard UDP, TCP, DNS over TLS (DoT), and DNS over HTTPS (DoH).
## Features
- **Standard UDP DNS**: Traditional DNS resolution over UDP
- **DNS over TCP**: DNS resolution using TCP transport
- **DNS over TLS (DoT)**: Secure DNS resolution using TLS encryption
- **DNS over HTTPS (DoH)**: Secure DNS resolution using HTTPS
## Certificate Options
This example provides two certificate options for secure DNS protocols (DoT and DoH):
1. **Certificate Bundle (Default)**: Uses the ESP-IDF certificate bundle, making it easy to get started with popular DNS providers like Google.
2. **Custom Certificate**: Uses a specific certificate for the DNS server. The example includes a Google DNS certificate.
## How It Works
1. **Network Initialization**: The application initializes the network interfaces (Wi-Fi or Ethernet) and establishes a connection.
2. **DNS Resolution Tests**: The example performs DNS resolution using different protocols:
- Native UDP DNS (system default)
- ESP DNS with UDP protocol
- ESP DNS with TCP protocol
- ESP DNS with DoT protocol (using server certificate)
- ESP DNS with DoT protocol (using certificate bundle)
- ESP DNS with DoH protocol (using server certificate)
- ESP DNS with DoH protocol (using certificate bundle)
3. **Domain Resolution**: For each protocol, the application resolves several domain names including:
- yahoo.com
- www.google.com
- IP addresses (0.0.0.0 and IPv6 address)
## How to use example
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
### Hardware Required
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
* A USB cable for power supply and programming
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
idf.py -p PORT flash monitor
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Troubleshooting Tips
* **Connectivity**:
Ensure that the network connection details are accurate. For example, verify the Wi-Fi SSID and password or check that the Ethernet connection is secure and not faulty.
* **Memory Issues**:
If you encounter memory-related errors, check the system information output which displays free heap and stack high water mark. You may need to increase task stack sizes for more complex DNS operations.
* **Certificate Issues**:
For DoT and DoH protocols, ensure that the certificates are valid for the DNS server you're using. The example includes Google DNS certificates, but these may need to be updated if they expire.
## Example Output
```
I (4583) example_esp_dns: Executing DNS without initializing ESP_DNS module
I (4603) wifi:<ba-add>idx:1 (ifx:0, a0:36:bc:0e:c4:f0), tid:7, ssn:3, winSize:64
I (4613) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
I (4613) example_esp_dns: Hostname: yahoo.com: 74.6.143.26(IPv4)
I (4613) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
I (4613) wifi:<ba-del>idx:0, tid:6
I (4623) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
I (4623) wifi:<ba-add>idx:0 (ifx:0, a0:36:bc:0e:c4:f0), tid:0, ssn:1, winSize:64
I (4643) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
I (4643) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
I (4643) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
I (4653) example_esp_dns: Free Heap: 215292 bytes, Min Free Heap: 206008 bytes, Stack High Water Mark: 1220 bytes
I (4663) example_esp_dns: Executing UDP DNS
I (4673) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
I (4673) example_esp_dns: Hostname: yahoo.com: 74.6.143.26(IPv4)
I (4683) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
I (4683) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
I (4693) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
I (4703) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
I (4703) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
I (4713) example_esp_dns: Free Heap: 215116 bytes, Min Free Heap: 206008 bytes, Stack High Water Mark: 1220 bytes
I (4723) example_esp_dns: Executing TCP DNS
I (4763) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
I (4763) example_esp_dns: Hostname: yahoo.com: 74.6.143.26(IPv4)
I (4763) example_esp_dns: Hostname: yahoo.com: 98.137.11.164(IPv4)
I (4763) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
I (4793) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
I (4793) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
I (4793) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
I (4803) example_esp_dns: Free Heap: 214580 bytes, Min Free Heap: 206008 bytes, Stack High Water Mark: 1220 bytes
I (4813) example_esp_dns: Executing DNS over TLS
I (5963) example_esp_dns: Hostname: yahoo.com: 74.6.143.25(IPv4)
I (5963) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
I (5963) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
I (5973) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
I (7083) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
I (7083) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
I (7083) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
I (7093) example_esp_dns: Free Heap: 213504 bytes, Min Free Heap: 165308 bytes, Stack High Water Mark: 1220 bytes
I (7103) example_esp_dns: Executing DNS over TLS
I (7413) esp-x509-crt-bundle: Certificate validated
I (8233) example_esp_dns: Hostname: yahoo.com: 98.137.11.164(IPv4)
I (8233) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
I (8233) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
I (8243) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
I (8553) esp-x509-crt-bundle: Certificate validated
I (9363) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
I (9363) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
I (9363) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
I (9373) example_esp_dns: Free Heap: 213120 bytes, Min Free Heap: 165308 bytes, Stack High Water Mark: 1220 bytes
I (9383) example_esp_dns: Executing DNS over HTTPS
I (10563) example_esp_dns: Hostname: yahoo.com: 74.6.143.26(IPv4)
I (10563) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
I (10563) example_esp_dns: Hostname: yahoo.com: 74.6.143.25(IPv4)
I (10573) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
I (11713) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
I (11713) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
I (11723) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
I (11723) example_esp_dns: Free Heap: 212664 bytes, Min Free Heap: 162780 bytes, Stack High Water Mark: 1220 bytes
I (11733) example_esp_dns: Executing DNS over HTTPS
I (12033) esp-x509-crt-bundle: Certificate validated
I (12863) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
I (12863) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
I (12863) example_esp_dns: Hostname: yahoo.com: 98.137.11.164(IPv4)
I (12873) example_esp_dns: Hostname: yahoo.com: 74.6.143.25(IPv4)
I (13153) esp-x509-crt-bundle: Certificate validated
I (13993) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
I (13993) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
I (13993) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
I (14003) example_esp_dns: Free Heap: 212044 bytes, Min Free Heap: 162780 bytes, Stack High Water Mark: 1220 bytes
I (14013) main_task: Returned from app_main()
```

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "esp_dns_example.c"
INCLUDE_DIRS "."
EMBED_TXTFILES "cert_google_root.pem")

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
-----END CERTIFICATE-----

View File

@ -0,0 +1,324 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <unistd.h>
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_timer.h"
#include "lwip/opt.h"
#include "protocol_examples_common.h"
#include "esp_dns.h"
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
#include "esp_crt_bundle.h"
#endif
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN INET_ADDRSTRLEN
#endif
#define TAG "example_esp_dns"
extern const char server_root_cert_pem_start[] asm("_binary_cert_google_root_pem_start");
extern const char server_root_cert_pem_end[] asm("_binary_cert_google_root_pem_end");
/**
* @brief Performs DNS lookup for a given hostname and address family
* @param hostname The hostname to resolve
* @param family The address family (AF_INET, AF_INET6, or AF_UNSPEC)
*/
static void do_getaddrinfo(char *hostname, int family)
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
void *addr = NULL;
char *ipver = NULL;
/* Initialize the hints structure */
memset(&hints, 0, sizeof hints);
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM; /* UDP datagram sockets */
/* Get address information */
if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
ESP_LOGE(TAG, "getaddrinfo error: %d", status);
goto cleanup;
}
/* Loop through all the results */
for (p = res; p != NULL; p = p->ai_next) {
/* Get pointer to the address itself */
#if defined(CONFIG_LWIP_IPV4)
if (p->ai_family == AF_INET) { /* IPv4 */
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
/* Convert the IP to a string and print it */
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
ESP_LOGI(TAG, "Hostname: %s: %s(%s)", hostname, ipstr, ipver);
}
#endif
#if defined(CONFIG_LWIP_IPV6)
if (p->ai_family == AF_INET6) { /* IPv6 */
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
/* Convert the IP to a string and print it */
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
ESP_LOGI(TAG, "Hostname: %s: %s(%s)", hostname, ipstr, ipver);
}
#endif
}
cleanup:
freeaddrinfo(res); /* Free the linked list */
}
/**
* @brief Task that performs DNS lookups for various hostnames
* @param pvParameters Parent task handle for notification
*/
static void addr_info_task(void *pvParameters)
{
TaskHandle_t parent_handle = (TaskHandle_t)pvParameters;
do_getaddrinfo("yahoo.com", AF_UNSPEC);
do_getaddrinfo("www.google.com", AF_INET6);
do_getaddrinfo("0.0.0.0", AF_UNSPEC);
do_getaddrinfo("fe80:0000:0000:0000:5abf:25ff:fee0:4100", AF_UNSPEC);
/* Notify parent task before deleting */
if (parent_handle) {
xTaskNotifyGive(parent_handle);
}
vTaskDelete(NULL);
}
/**
* @brief Prints system information including heap and stack usage
*/
void print_system_info(void)
{
/* Get the free heap size */
uint32_t free_heap = esp_get_free_heap_size();
uint32_t min_free_heap = esp_get_minimum_free_heap_size();
/* Get the stack high water mark for the current task */
UBaseType_t stack_high_water_mark = uxTaskGetStackHighWaterMark(NULL);
ESP_LOGI(TAG, "Free Heap: %lu bytes, Min Free Heap: %lu bytes, Stack High Water Mark: %u bytes\n",
free_heap, min_free_heap, stack_high_water_mark);
}
/**
* @brief Creates and runs the DNS query task
*/
static void run_dns_query_task(void)
{
TaskHandle_t task_handle = NULL;
TaskHandle_t parent_handle = xTaskGetCurrentTaskHandle();
xTaskCreate(addr_info_task, "AddressInfo", 4 * 1024, parent_handle, 5, &task_handle);
/* Wait for task to complete */
if (task_handle != NULL) {
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
}
print_system_info();
}
/**
* @brief Performs DNS queries using UDP protocol
*/
void perform_esp_dns_udp_query(void)
{
esp_dns_handle_t dns_handle;
ESP_LOGI(TAG, "Executing UDP DNS");
/* Initialize with UDP DNS configuration */
esp_dns_config_t udp_config = {
.dns_server = "dns.google", /* Google DNS */
};
/* Initialize UDP DNS module */
dns_handle = esp_dns_init_udp(&udp_config);
if (!dns_handle) {
ESP_LOGE(TAG, "Failed to initialize UDP DNS module");
return;
}
run_dns_query_task();
/* Cleanup */
esp_dns_cleanup_udp(dns_handle);
}
/**
* @brief Performs DNS queries using TCP protocol
*/
void perform_esp_dns_tcp_query(void)
{
esp_dns_handle_t dns_handle;
ESP_LOGI(TAG, "Executing TCP DNS");
/* Initialize with TCP DNS configuration */
esp_dns_config_t tcp_config = {
.dns_server = "dns.google",
.port = ESP_DNS_DEFAULT_TCP_PORT,
.timeout_ms = ESP_DNS_DEFAULT_TIMEOUT_MS,
};
/* Initialize TCP DNS module */
dns_handle = esp_dns_init_tcp(&tcp_config);
if (!dns_handle) {
ESP_LOGE(TAG, "Failed to initialize TCP DNS module");
return;
}
run_dns_query_task();
/* Cleanup */
esp_dns_cleanup_tcp(dns_handle);
}
/**
* @brief Performs DNS queries using DNS over TLS protocol
* @param val_type Type of certificate validation ("cert" or "bndl")
*/
void perform_esp_dns_dot_query(char *val_type)
{
esp_dns_handle_t dns_handle;
ESP_LOGI(TAG, "Executing DNS over TLS");
/* Initialize with DNS over TLS configuration */
esp_dns_config_t dot_config = {
.dns_server = "dns.google",
.port = ESP_DNS_DEFAULT_DOT_PORT,
.timeout_ms = ESP_DNS_DEFAULT_TIMEOUT_MS,
};
if (strcmp(val_type, "cert") == 0) {
dot_config.tls_config.cert_pem = server_root_cert_pem_start;
} else if (strcmp(val_type, "bndl") == 0) {
dot_config.tls_config.crt_bundle_attach = esp_crt_bundle_attach;
}
/* Initialize DoT DNS module */
dns_handle = esp_dns_init_dot(&dot_config);
if (!dns_handle) {
ESP_LOGE(TAG, "Failed to initialize DoT DNS module");
return;
}
run_dns_query_task();
/* Cleanup */
esp_dns_cleanup_dot(dns_handle);
}
/**
* @brief Performs DNS queries using DNS over HTTPS protocol
* @param val_type Type of certificate validation ("cert" or "bndl")
*/
void perform_esp_dns_doh_query(char *val_type)
{
esp_dns_handle_t dns_handle;
ESP_LOGI(TAG, "Executing DNS over HTTPS");
/* Initialize with DNS over HTTPS configuration */
esp_dns_config_t doh_config = {
.dns_server = "dns.google",
.port = ESP_DNS_DEFAULT_DOH_PORT,
.protocol_config.doh_config = {
.url_path = "/dns-query",
},
};
if (strcmp(val_type, "cert") == 0) {
doh_config.tls_config.cert_pem = server_root_cert_pem_start;
} else if (strcmp(val_type, "bndl") == 0) {
doh_config.tls_config.crt_bundle_attach = esp_crt_bundle_attach;
}
/* Initialize DoH DNS module */
dns_handle = esp_dns_init_doh(&doh_config);
if (!dns_handle) {
ESP_LOGE(TAG, "Failed to initialize DoH DNS module");
return;
}
run_dns_query_task();
/* Cleanup */
esp_dns_cleanup_doh(dns_handle);
}
void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init(); /* Initialize NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
/* 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());
/* Test Without ESP_DNS module */
ESP_LOGI(TAG, "Executing DNS without initializing ESP_DNS module");
run_dns_query_task();
/* DNS over UDP Test */
perform_esp_dns_udp_query();
/* DNS over TCP Test */
perform_esp_dns_tcp_query();
/* DNS over TLS Test with cert */
perform_esp_dns_dot_query("cert");
/* DNS over TLS Test with cert bundle */
perform_esp_dns_dot_query("bndl");
/* DNS over HTTPS Test with cert */
perform_esp_dns_doh_query("cert");
/* DNS over HTTPS Test with cert bundle */
perform_esp_dns_doh_query("bndl");
}

View File

@ -0,0 +1,8 @@
dependencies:
idf:
version: ">=5.1"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
esp_dns:
version: "*"
override_path: '../../../'

View File

@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
# -*- coding: utf-8 -*-
import pytest
@pytest.mark.esp32
def test_esp_dns_resolution(dut):
"""
Test DNS resolution for different protocols (UDP, TCP, DoT, DoH).
"""
dut.expect('Executing UDP DNS', timeout=10)
dut.expect('Executing TCP DNS', timeout=10)
dut.expect('Executing DNS over TLS', timeout=10)
dut.expect('Executing DNS over HTTPS', timeout=10)
# Check for successful DNS resolution
dut.expect('Hostname: yahoo.com:', timeout=10)
dut.expect('Hostname: www.google.com:', timeout=10)
dut.expect('Hostname: 0.0.0.0:', timeout=10)
dut.expect('Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100', timeout=10)
# Check for system information logs
dut.expect('Free Heap:', timeout=10)
dut.expect('Min Free Heap:', timeout=10)
dut.expect('Stack High Water Mark:', timeout=10)

View 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.5.0 Project Minimal Configuration
#
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
CONFIG_LWIP_DNS_MAX_HOST_IP=4
CONFIG_LWIP_USE_ESP_GETADDRINFO=y
CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM=y

View File

@ -0,0 +1,5 @@
## IDF Component Manager Manifest File
version: 0.1.0
dependencies:
idf:
version: ">=5.1"

View File

@ -0,0 +1,155 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_DNS_DEFAULT_TCP_PORT 53 /* Default TCP port for DNS */
#define ESP_DNS_DEFAULT_DOT_PORT 853 /* Default TLS port for DNS over TLS */
#define ESP_DNS_DEFAULT_DOH_PORT 443 /* Default HTTPS port for DNS over HTTPS */
#define ESP_DNS_DEFAULT_TIMEOUT_MS 10000 /* Default timeout for DNS queries in milliseconds */
typedef enum {
ESP_DNS_PROTOCOL_UDP, /* Traditional UDP DNS (Port 53) */
ESP_DNS_PROTOCOL_TCP, /* TCP DNS (Port 53) */
ESP_DNS_PROTOCOL_DOT, /* DNS over TLS (Port 853) */
ESP_DNS_PROTOCOL_DOH, /* DNS over HTTPS (Port 443) */
} esp_dns_protocol_type_t;
/**
* @brief DNS configuration structure
*/
typedef struct {
/* Basic protocol selection */
esp_dns_protocol_type_t protocol; /* DNS protocol type */
/* Common settings */
const char *dns_server; /* DNS server IP address or hostname */
uint16_t port; /* Custom port number (if not using default) */
uint32_t timeout_ms; /* Query timeout in milliseconds */
/* Secure protocol options */
struct {
const char *cert_pem; /* SSL server certification in PEM format as string */
esp_err_t (*crt_bundle_attach)(void *conf); /* Function pointer to attach cert bundle */
} tls_config; /* Used for DoT, DoH, DoH3, DNSCrypt, DoQ */
/* Protocol-specific options */
union {
/* DoH options */
struct {
const char *url_path; /* URL path for DoH service (e.g., "/dns-query") */
} doh_config; /* DNS over HTTPS configuration */
} protocol_config; /* Protocol-specific configuration */
} esp_dns_config_t;
typedef struct esp_dns_handle* esp_dns_handle_t;
/**
* @brief Initialize DNS over HTTPS (DoH) module
*
* Sets up the DoH service using the provided configuration. Validates the parameters,
* sets the protocol, and initializes the DNS module.
*
* @param config Pointer to the DNS configuration structure
*
* @return Handle to the initialized DoH module on success, NULL on failure
*/
esp_dns_handle_t esp_dns_init_doh(esp_dns_config_t *config);
/**
* @brief Initialize DNS over TLS (DoT) module
*
* Sets up the DoT service using the provided configuration. Validates the parameters,
* sets the protocol, and initializes the DNS module.
*
* @param config Pointer to the DNS configuration structure
*
* @return Handle to the initialized DoT module on success, NULL on failure
*/
esp_dns_handle_t esp_dns_init_dot(esp_dns_config_t *config);
/**
* @brief Initialize TCP DNS module
*
* Sets up the TCP DNS service using the provided configuration. Validates the parameters,
* sets the protocol, and initializes the DNS module.
*
* @param config Pointer to the DNS configuration structure
*
* @return Handle to the initialized TCP module on success, NULL on failure
*/
esp_dns_handle_t esp_dns_init_tcp(esp_dns_config_t *config);
/**
* @brief Initialize UDP DNS module
*
* Sets up the UDP DNS service using the provided configuration. Validates the parameters,
* sets the protocol, and initializes the DNS module.
*
* @param config Pointer to the DNS configuration structure
*
* @return Handle to the initialized UDP module on success, NULL on failure
*/
esp_dns_handle_t esp_dns_init_udp(esp_dns_config_t *config);
/**
* @brief Clean up DNS over HTTPS (DoH) module
*
* Releases resources allocated for the DoH service. Validates the parameters,
* checks the protocol, and cleans up the DNS module.
*
* @param handle Pointer to the DNS handle to be cleaned up
*
* @return 0 on success, -1 on failure
*/
int esp_dns_cleanup_doh(esp_dns_handle_t handle);
/**
* @brief Clean up DNS over TLS (DoT) module
*
* Releases resources allocated for the DoT service. Validates the parameters,
* checks the protocol, and cleans up the DNS module.
*
* @param handle Pointer to the DNS handle to be cleaned up
*
* @return 0 on success, -1 on failure
*/
int esp_dns_cleanup_dot(esp_dns_handle_t handle);
/**
* @brief Clean up TCP DNS module
*
* Releases resources allocated for the TCP DNS service. Validates the parameters,
* checks the protocol, and cleans up the DNS module.
*
* @param handle Pointer to the DNS handle to be cleaned up
*
* @return 0 on success, -1 on failure
*/
int esp_dns_cleanup_tcp(esp_dns_handle_t handle);
/**
* @brief Clean up UDP DNS module
*
* Releases resources allocated for the UDP DNS service. Validates the parameters,
* checks the protocol, and cleans up the DNS module.
*
* @param handle Pointer to the DNS handle to be cleaned up
*
* @return 0 on success, -1 on failure
*/
int esp_dns_cleanup_udp(esp_dns_handle_t handle);
#ifdef __cplusplus
}
#endif

View File

@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(modem): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py esp_modem
tag_format: modem-v$version
version: 1.1.0
version: 1.4.0
version_files:
- idf_component.yml

View File

@ -1,5 +1,77 @@
# Changelog
## [1.4.0](https://github.com/espressif/esp-protocols/commits/modem-v1.4.0)
### Features
- added config_edrx api function ([74b7d85d](https://github.com/espressif/esp-protocols/commit/74b7d85d))
- added sqn_gm02s connect function ([b97dfc08](https://github.com/espressif/esp-protocols/commit/b97dfc08))
- add support for sequans GM02S modem ([8560f021](https://github.com/espressif/esp-protocols/commit/8560f021))
### Bug Fixes
- Fix cmux log message ([6ed672da](https://github.com/espressif/esp-protocols/commit/6ed672da))
- fixed minor code mistakes. ([317faf89](https://github.com/espressif/esp-protocols/commit/317faf89))
- handle nullptr in DTE constructors to prevent invalid access ([95b56600](https://github.com/espressif/esp-protocols/commit/95b56600))
## [1.3.0](https://github.com/espressif/esp-protocols/commits/modem-v1.3.0)
### Features
- Add mode detection to the example ([18f196fa](https://github.com/espressif/esp-protocols/commit/18f196fa))
- Support for pausing network in C-API ([1db83cd1](https://github.com/espressif/esp-protocols/commit/1db83cd1))
- Add support for pausing netif ([247f1681](https://github.com/espressif/esp-protocols/commit/247f1681), [#699](https://github.com/espressif/esp-protocols/issues/699))
### Bug Fixes
- Minor cleanup of pppos example ([5e929902](https://github.com/espressif/esp-protocols/commit/5e929902))
- Fix PPP mode detection to accept LCP/conf ([c989c6ad](https://github.com/espressif/esp-protocols/commit/c989c6ad))
- Refine mode switch data->command ([8b6ea331](https://github.com/espressif/esp-protocols/commit/8b6ea331), [#692](https://github.com/espressif/esp-protocols/issues/692))
- Detect serial ports properly ([0cb59ff8](https://github.com/espressif/esp-protocols/commit/0cb59ff8))
- Fix CMUX enter to ignore URC before transition ([1284f66d](https://github.com/espressif/esp-protocols/commit/1284f66d), [#669](https://github.com/espressif/esp-protocols/issues/669))
## [1.2.1](https://github.com/espressif/esp-protocols/commits/modem-v1.2.1)
### Bug Fixes
- Use higher GPIO range to support new chips ([428fdbbd](https://github.com/espressif/esp-protocols/commit/428fdbbd), [#558](https://github.com/espressif/esp-protocols/issues/558))
- Remove tests and support for IDFv4.4, added IDFv5.4 ([433a033f](https://github.com/espressif/esp-protocols/commit/433a033f))
- Fix typo GENETIC -> GENERIC in mode types ([090b1ff8](https://github.com/espressif/esp-protocols/commit/090b1ff8), [#667](https://github.com/espressif/esp-protocols/issues/667))
- Add support for URC handler into C-API ([295d99df](https://github.com/espressif/esp-protocols/commit/295d99df), [#180](https://github.com/espressif/esp-protocols/issues/180))
## [1.2.0](https://github.com/espressif/esp-protocols/commits/modem-v1.2.0)
### Features
- Add support for guessing mode ([52598e5f](https://github.com/espressif/esp-protocols/commit/52598e5f))
- Delete CMUX internal implementation even if terminal exit fails ([0e0cbd6b](https://github.com/espressif/esp-protocols/commit/0e0cbd6b))
- Add support for handling URC ([1b6a3b3b](https://github.com/espressif/esp-protocols/commit/1b6a3b3b), [#180](https://github.com/espressif/esp-protocols/issues/180))
- add ability to change ESP_MODEM_C_API_STR_MAX from Kconfig ([17909892](https://github.com/espressif/esp-protocols/commit/17909892))
- Added target test config with CHAP authentication ([f8ae7def](https://github.com/espressif/esp-protocols/commit/f8ae7def))
- example add esp32p4 usb support ([adafeae5](https://github.com/espressif/esp-protocols/commit/adafeae5))
- Publish mbedtls component ([0140455f](https://github.com/espressif/esp-protocols/commit/0140455f))
- host test support of the latest ESP-IDF release ([3f74b4e8](https://github.com/espressif/esp-protocols/commit/3f74b4e8))
### Bug Fixes
- Fix console example to use urc/detect features ([1a9eaf3e](https://github.com/espressif/esp-protocols/commit/1a9eaf3e))
- Update target test builds to use external Catch2 ([554f022c](https://github.com/espressif/esp-protocols/commit/554f022c))
- Fix arguments names when spawn esp_modem_xxx declarations ([b6792c52](https://github.com/espressif/esp-protocols/commit/b6792c52))
- Remove catch dependency ([c3480768](https://github.com/espressif/esp-protocols/commit/c3480768))
- Examples: use local configs for MQTT topic/data ([f5c13b92](https://github.com/espressif/esp-protocols/commit/f5c13b92))
- Fixed clang-tidy warnings ([70fa3af7](https://github.com/espressif/esp-protocols/commit/70fa3af7))
- Fix CI build per IDFv5.3 ([d0c17ef0](https://github.com/espressif/esp-protocols/commit/d0c17ef0))
- Fixed UART task to check for buffered data periodically ([4bdd90cc](https://github.com/espressif/esp-protocols/commit/4bdd90cc), [#536](https://github.com/espressif/esp-protocols/issues/536))
- Cleanup unused configs from PPPoS example ([08a62ccc](https://github.com/espressif/esp-protocols/commit/08a62ccc))
- Update CMUX example with SIM7070_gnss cleaned-up ([56fe5327](https://github.com/espressif/esp-protocols/commit/56fe5327))
- Update console example with SIM7070_gnss format comments ([5baaf542](https://github.com/espressif/esp-protocols/commit/5baaf542))
- Fix remaining print format warnings ([3b80181d](https://github.com/espressif/esp-protocols/commit/3b80181d))
### Updated
- docs(modem): Fix esp_modem_at_raw() description (C-API) ([492a6a00](https://github.com/espressif/esp-protocols/commit/492a6a00))
- ci(common): updated github actions(checkout, upload, download) v3 to 4, Ubuntu 20.04 to v22.04 ([a23a0027](https://github.com/espressif/esp-protocols/commit/a23a0027))
## [1.1.0](https://github.com/espressif/esp-protocols/commits/modem-v1.1.0)
### Features

View File

@ -15,6 +15,13 @@ else()
endif()
if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
set(command_dir "generate") # using in-place macro expansion with AT commands
else()
set(command_dir "command") # using pre-generated AT commands
endif()
set(srcs ${platform_srcs}
"src/esp_modem_dte.cpp"
"src/esp_modem_dce.cpp"
@ -26,12 +33,10 @@ set(srcs ${platform_srcs}
"src/esp_modem_term_fs.cpp"
"src/esp_modem_vfs_uart_creator.cpp"
"src/esp_modem_vfs_socket_creator.cpp"
"src/esp_modem_modules.cpp")
set(include_dirs "include")
"${command_dir}/src/esp_modem_modules.cpp")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
INCLUDE_DIRS include ${command_dir}/include
PRIV_INCLUDE_DIRS private_include
REQUIRES ${dependencies})

View File

@ -76,4 +76,31 @@ menu "esp-modem"
help
If enabled, APIs to add URC handler are available
config ESP_MODEM_PPP_ESCAPE_BEFORE_EXIT
bool "Send escape sequence when switching PPP -> CMD"
default n
help
If enabled, the library sends a PPP escape ("+++" command)
to switch to command mode. This make switching from PPP to CMD
mode more robust for some devices (e.g. Quectel), but might cause
trouble for other devices (e.g. SIMCOM).
config ESP_MODEM_ADD_DEBUG_LOGS
bool "Add UART Tx/Rx logs"
default n
help
If enabled, the library dumps all transmitted and received data.
This option is only used for debugging.
config ESP_MODEM_ENABLE_DEVELOPMENT_MODE
bool "Use development mode"
default n
help
We use pre-generated headers and sources with common AT commands.
This is useful for using ESP-MODEM library with IDEs and code navigation.
When developing ESP-MODEM library, it's usually more convenient
to work with sources directly, using C-preprocessor's metaprogramming.
Set this to 'y' if you're making changes to the actual sources of
the AT command definitions (typically in esp_modem_command_declare.inc)
endmenu

View File

@ -0,0 +1,347 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "cxx_include/esp_modem_dte.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
#include "cxx_include/esp_modem_types.hpp"
namespace esp_modem {
namespace dce_commands {
/**
* @defgroup ESP_MODEM_DCE_COMMAND ESP_MODEM DCE command library
* @brief Library of the most useful DCE commands
*/
/** @addtogroup ESP_MODEM_DCE_COMMAND
* @{
*/
/**
* @brief Generic AT command to be send with pass and fail phrases
*
* @param t Commandable object (anything that can accept commands)
* @param command Command to be sent do the commandable object
* @param pass_phrase String to be present in the reply to pass this command
* @param fail_phrase String to be present in the reply to fail this command
* @param timeout_ms Timeout in ms
*/
command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms);
/**
* @brief Declaration of all commands is generated from esp_modem_command_declare.inc
*/
/**
* @brief Sends the initial AT sequence to sync up with the device
* @return OK, FAIL or TIMEOUT
*/
command_result sync(CommandableIf *t);
/**
* @brief Reads the operator name
* @param[out] name operator name
* @param[out] act access technology
* @return OK, FAIL or TIMEOUT
*/
command_result get_operator_name(CommandableIf *t, std::string &name, int &act);
/**
* @brief Stores current user profile
* @return OK, FAIL or TIMEOUT
*/
command_result store_profile(CommandableIf *t);
/**
* @brief Sets the supplied PIN code
* @param[in] pin Pin
* @return OK, FAIL or TIMEOUT
*/
command_result set_pin(CommandableIf *t, const std::string &pin);
/**
* @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
* @param[in] cmd String command that's send to DTE
* @param[out] out Raw output from DTE
* @param[in] pass Pattern in response for the API to return OK
* @param[in] fail Pattern in response for the API to return FAIL
* @param[in] timeout AT command timeout in milliseconds
* @return OK, FAIL or TIMEOUT
*/
command_result at_raw(CommandableIf *t, const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout);
/**
* @brief Execute the supplied AT command
* @param[in] cmd AT command
* @param[out] out Command output string
* @param[in] timeout AT command timeout in milliseconds
* @return OK, FAIL or TIMEOUT
*/
command_result at(CommandableIf *t, const std::string &cmd, std::string &out, int timeout);
/**
* @brief Checks if the SIM needs a PIN
* @param[out] pin_ok true if the SIM card doesn't need a PIN to unlock
* @return OK, FAIL or TIMEOUT
*/
command_result read_pin(CommandableIf *t, bool &pin_ok);
/**
* @brief Sets echo mode
* @param[in] echo_on true if echo mode on (repeats the commands)
* @return OK, FAIL or TIMEOUT
*/
command_result set_echo(CommandableIf *t, const bool echo_on);
/**
* @brief Sets the Txt or Pdu mode for SMS (only txt is supported)
* @param[in] txt true if txt mode
* @return OK, FAIL or TIMEOUT
*/
command_result sms_txt_mode(CommandableIf *t, const bool txt);
/**
* @brief Sets the default (GSM) character set
* @return OK, FAIL or TIMEOUT
*/
command_result sms_character_set(CommandableIf *t);
/**
* @brief Sends SMS message in txt mode
* @param[in] number Phone number to send the message to
* @param[in] message Text message to be sent
* @return OK, FAIL or TIMEOUT
*/
command_result send_sms(CommandableIf *t, const std::string &number, const std::string &message);
/**
* @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
* @return OK, FAIL or TIMEOUT
*/
command_result resume_data_mode(CommandableIf *t);
/**
* @brief Sets php context
* @param[in] p1 PdP context struct to setup modem cellular connection
* @return OK, FAIL or TIMEOUT
*/
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp);
/**
* @brief Switches to the command mode
* @return OK, FAIL or TIMEOUT
*/
command_result set_command_mode(CommandableIf *t);
/**
* @brief Switches to the CMUX mode
* @return OK, FAIL or TIMEOUT
*/
command_result set_cmux(CommandableIf *t);
/**
* @brief Reads the IMSI number
* @param[out] imsi Module's IMSI number
* @return OK, FAIL or TIMEOUT
*/
command_result get_imsi(CommandableIf *t, std::string &imsi);
/**
* @brief Reads the IMEI number
* @param[out] imei Module's IMEI number
* @return OK, FAIL or TIMEOUT
*/
command_result get_imei(CommandableIf *t, std::string &imei);
/**
* @brief Reads the module name
* @param[out] name module name
* @return OK, FAIL or TIMEOUT
*/
command_result get_module_name(CommandableIf *t, std::string &name);
/**
* @brief Sets the modem to data mode
* @return OK, FAIL or TIMEOUT
*/
command_result set_data_mode(CommandableIf *t);
/**
* @brief Get Signal quality
* @param[out] rssi signal strength indication
* @param[out] ber channel bit error rate
* @return OK, FAIL or TIMEOUT
*/
command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber);
/**
* @brief Sets HW control flow
* @param[in] dce_flow 0=none, 2=RTS hw flow control of DCE
* @param[in] dte_flow 0=none, 2=CTS hw flow control of DTE
* @return OK, FAIL or TIMEOUT
*/
command_result set_flow_control(CommandableIf *t, int dce_flow, int dte_flow);
/**
* @brief Hangs up current data call
* @return OK, FAIL or TIMEOUT
*/
command_result hang_up(CommandableIf *t);
/**
* @brief Get voltage levels of modem power up circuitry
* @param[out] voltage Current status in mV
* @param[out] bcs charge status (-1-Not available, 0-Not charging, 1-Charging, 2-Charging done)
* @param[out] bcl 1-100% battery capacity, -1-Not available
* @return OK, FAIL or TIMEOUT
*/
command_result get_battery_status(CommandableIf *t, int &voltage, int &bcs, int &bcl);
/**
* @brief Power down the module
* @return OK, FAIL or TIMEOUT
*/
command_result power_down(CommandableIf *t);
/**
* @brief Reset the module
* @return OK, FAIL or TIMEOUT
*/
command_result reset(CommandableIf *t);
/**
* @brief Configures the baudrate
* @param[in] baud Desired baud rate of the DTE
* @return OK, FAIL or TIMEOUT
*/
command_result set_baud(CommandableIf *t, int baud);
/**
* @brief Force an attempt to connect to a specific operator
* @param[in] mode mode of attempt
* mode=0 - automatic
* mode=1 - manual
* mode=2 - deregister
* mode=3 - set format for read operation
* mode=4 - manual with fallback to automatic
* @param[in] format what format the operator is given in
* format=0 - long format
* format=1 - short format
* format=2 - numeric
* @param[in] oper the operator to connect to
* @return OK, FAIL or TIMEOUT
*/
command_result set_operator(CommandableIf *t, int mode, int format, const std::string &oper);
/**
* @brief Attach or detach from the GPRS service
* @param[in] state 1-attach 0-detach
* @return OK, FAIL or TIMEOUT
*/
command_result set_network_attachment_state(CommandableIf *t, int state);
/**
* @brief Get network attachment state
* @param[out] state 1-attached 0-detached
* @return OK, FAIL or TIMEOUT
*/
command_result get_network_attachment_state(CommandableIf *t, int &state);
/**
* @brief What mode the radio should be set to
* @param[in] state state 1-full 0-minimum ...
* @return OK, FAIL or TIMEOUT
*/
command_result set_radio_state(CommandableIf *t, int state);
/**
* @brief Get current radio state
* @param[out] state 1-full 0-minimum ...
* @return OK, FAIL or TIMEOUT
*/
command_result get_radio_state(CommandableIf *t, int &state);
/**
* @brief Set network mode
* @param[in] mode preferred mode
* @return OK, FAIL or TIMEOUT
*/
command_result set_network_mode(CommandableIf *t, int mode);
/**
* @brief Preferred network mode (CAT-M and/or NB-IoT)
* @param[in] mode preferred selection
* @return OK, FAIL or TIMEOUT
*/
command_result set_preferred_mode(CommandableIf *t, int mode);
/**
* @brief Set network bands for CAT-M or NB-IoT
* @param[in] mode CAT-M or NB-IoT
* @param[in] bands bitmap in hex representing bands
* @param[in] size size of teh bands bitmap
* @return OK, FAIL or TIMEOUT
*/
command_result set_network_bands(CommandableIf *t, const std::string &mode, const int *bands, int size);
/**
* @brief Show network system mode
* @param[out] mode current network mode
* @return OK, FAIL or TIMEOUT
*/
command_result get_network_system_mode(CommandableIf *t, int &mode);
/**
* @brief GNSS power control
* @param[out] mode power mode (0 - off, 1 - on)
* @return OK, FAIL or TIMEOUT
*/
command_result set_gnss_power_mode(CommandableIf *t, int mode);
/**
* @brief GNSS power control
* @param[out] mode power mode (0 - off, 1 - on)
* @return OK, FAIL or TIMEOUT
*/
command_result get_gnss_power_mode(CommandableIf *t, int &mode);
/**
* @brief Configure PSM
* @param[in] mode psm mode (0 - off, 1 - on, 2 - off & discard stored params)
* @return OK, FAIL or TIMEOUT
*/
command_result config_psm(CommandableIf *t, int mode, const std::string &tau, const std::string &active_time);
/**
* @brief Configure CEREG urc
* @param[in] value
* value = 0 - Disable network URC
* value = 1 - Enable network URC
* value = 2 - Enable network URC with location information
* value = 3 - Enable network URC with location information and EMM cause
* value = 4 - Enable network URC with location information and PSM value
* value = 5 - Enable network URC with location information and PSM value, EMM cause
*/
command_result config_network_registration_urc(CommandableIf *t, int value);
/**
* @brief Gets the current network registration state
* @param[out] state The current network registration state
* state = 0 - Not registered, MT is not currently searching an operator to register to
* state = 1 - Registered, home network
* state = 2 - Not registered, but MT is currently trying to attach or searching an operator to register to
* state = 3 - Registration denied
* state = 4 - Unknown
* state = 5 - Registered, Roaming
* state = 6 - Registered, for SMS only, home network (NB-IoT only)
* state = 7 - Registered, for SMS only, roaming (NB-IoT only)
* state = 8 - Attached for emergency bearer services only
* state = 9 - Registered for CSFB not preferred, home network
* state = 10 - Registered for CSFB not preferred, roaming
*/
command_result get_network_registration_state(CommandableIf *t, int &state);
/**
* @brief Configures the mobile termination error (+CME ERROR)
* @param[in] mode The form of the final result code
* mode = 0 - Disable, use and send ERROR instead
* mode = 1 - Enable, use numeric error values
* mode = 2 - Enable, result code and use verbose error values
*/
command_result config_mobile_termination_error(CommandableIf *t, int mode);
/**
* @brief Configure eDRX
* @param[in] mode
* mode = 0 - Disable
* mode = 1 - Enable
* mode = 2 - Enable + URC
* mode = 3 - Disable + Reset parameter.
* @param[in] access_technology
* act = 0 - ACT is not using eDRX (used in URC)
* act = 1 - EC-GSM-IoT (A/Gb mode)
* act = 2 - GSM (A/Gb mode)
* act = 3 - UTRAN (Iu mode)
* act = 4 - E-UTRAN (WB-S1 mode)
* act = 5 - E-UTRAN (NB-S1 mode)
* @param[in] edrx_value nible string containing encoded eDRX time
* @param[in] ptw_value nible string containing encoded Paging Time Window
*/
command_result config_edrx(CommandableIf *t, int mode, int access_technology, const std::string &edrx_value);
/**
* @brief Following commands that are different for some specific modules
*/
command_result get_battery_status_sim7xxx(CommandableIf *t, int &voltage, int &bcs, int &bcl);
command_result set_gnss_power_mode_sim76xx(CommandableIf *t, int mode);
command_result power_down_sim76xx(CommandableIf *t);
command_result power_down_sim70xx(CommandableIf *t);
command_result set_network_bands_sim76xx(CommandableIf *t, const std::string &mode, const int *bands, int size);
command_result power_down_sim8xx(CommandableIf *t);
command_result set_data_mode_alt(CommandableIf *t);
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp, uint32_t timeout_ms);
/**
* @}
*/
} // dce_commands
} // esp_modem

Some files were not shown because too many files have changed in this diff Show More