diff --git a/.gitignore b/.gitignore index fbb16ff..e0853e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ **/build **/debug +**/svd **/__repo__ **/.vscode **/CMakePresets.json **/mcux_include.json +dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/*.o # PQC demo desktop client build artifacts (built by build-client.sh) dm-wolfssl-tls-hello-server-pqc-with-zephyr/client-tls13-filetransfer diff --git a/README.md b/README.md index 07518e6..eec86eb 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,10 @@ In the figure below, the imported project is of the type `Zephyr`, which means i Once you select the `Repository` and `SDK` you want to use with the imported project, hit the `Import` button. -## Setting Up wolfSSL, wolfMQTT, and wolfSSH +## Setting Up wolfSSL, wolfMQTT, wolfSSH and wolfBoot Currently, with the way the projects are set up, you will need to add wolfSSL, wolfSSH, wolfMQTT, etc., to the `west.yml` file inside the Zephyr repo specified during the import of the project. +wolfBoot is required for dm-wolfssl-ota-client-with-zephyr only. So for example the following would be added to `/Users/night1rider/Documents/VS-Code/Zephyr-Main/zephyr/west.yml` and then a `west update` performed inside that directory to update the repositories available to `Zephyr`. @@ -71,6 +72,8 @@ manifest: url-base: https://github.com/wolfssl - name: wolfmqtt url-base: https://github.com/wolfssl + - name: wolfboot + url-base: https://github.com/wolfssl projects: # @@ -86,6 +89,10 @@ manifest: path: modules/lib/wolfmqtt revision: v2.0.0 remote: wolfmqtt + - name: wolfboot + path: modules/bootloader/wolfboot + revision: v2.8.0 + remote: wolfboot ``` For more Zephyr-specific examples, look at the following READMEs: diff --git a/dm-wolfssl-ota-client-with-zephyr/CMakeLists.txt b/dm-wolfssl-ota-client-with-zephyr/CMakeLists.txt new file mode 100644 index 0000000..bc08bb7 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/CMakeLists.txt @@ -0,0 +1,191 @@ +cmake_minimum_required(VERSION 3.20.0) + +# ── wolfBoot module path ─────────────────────────────────────────────────────── +if(NOT "$ENV{ZEPHYR_BASE}" STREQUAL "") + set(_wb_zbase "$ENV{ZEPHYR_BASE}") +elseif(NOT "${ZEPHYR_BASE}" STREQUAL "") + set(_wb_zbase "${ZEPHYR_BASE}") +else() + message(FATAL_ERROR + "ZEPHYR_BASE is not set. " + "Run cmake with --preset debug or export ZEPHYR_BASE in the environment.") +endif() +get_filename_component(WOLFBOOT_MODULE_DIR + "${_wb_zbase}/../modules/bootloader/wolfboot" ABSOLUTE) +get_filename_component(_ZEPHYRPROJECT_DIR "${_wb_zbase}/.." ABSOLUTE) + +set(_MCUX_ROOT "${_ZEPHYRPROJECT_DIR}/modules/hal/nxp/mcux/mcux-sdk-ng") +set(_MCUX_CMSIS "${_ZEPHYRPROJECT_DIR}/modules/hal/cmsis_6/CMSIS") +set(_MCUX_DRV "${_MCUX_ROOT}/devices/MCX/MCXN/MCXN947") +set(_MCUX_TMPL "${CMAKE_SOURCE_DIR}/wolfbootConfig") +set(_WOLFSSL_ROOT "${_ZEPHYRPROJECT_DIR}/modules/crypto/wolfssl") + +# ── Apply wolfBoot board patch (idempotent, configure time) ─────────────────── +set(_WB_PATCH "${CMAKE_SOURCE_DIR}/wolfbootConfig/0001-Update-configs-and-memory-map.patch") +execute_process( + COMMAND git apply --check --reverse "${_WB_PATCH}" + WORKING_DIRECTORY "${WOLFBOOT_MODULE_DIR}" + RESULT_VARIABLE _patch_applied + OUTPUT_QUIET ERROR_QUIET +) +if(NOT _patch_applied EQUAL 0) + execute_process( + COMMAND git apply "${_WB_PATCH}" + WORKING_DIRECTORY "${WOLFBOOT_MODULE_DIR}" + RESULT_VARIABLE _patch_result + ERROR_VARIABLE _patch_error + ) + if(NOT _patch_result EQUAL 0) + message(FATAL_ERROR + "Failed to apply wolfBoot patch\n" + " patch: ${_WB_PATCH}\n" + " workdir: ${WOLFBOOT_MODULE_DIR}\n" + " git output: ${_patch_error}") + endif() +endif() + +# ── wolfBoot binary build (configure time, before Zephyr) ───────────────────── +# CORTEX_M33=1 must be on the command line so the ifeq in arch.mk (~line 357) +# is evaluated true during Makefile parse. arch.mk sets CORTEX_M33=1 for mcxn +# only at ~line 834, which is too late for the parse-time ifeq. Without this, +# -mcmse is never added to CFLAGS and --cmse-implib is never added to +# SECURE_LDFLAGS, so libwolfboot.o lacks SG stubs and wolfboot_tz_nsc.o is +# never emitted by the linker. +# If wolfboot_tz_nsc.o is absent, remove libwolfboot.o (the only object with +# CMSE entry functions) and the stale elf/bin so make recompiles and relinks. +if(NOT EXISTS "${WOLFBOOT_MODULE_DIR}/src/wolfboot_tz_nsc.o") + file(REMOVE + "${WOLFBOOT_MODULE_DIR}/src/libwolfboot.o" + "${WOLFBOOT_MODULE_DIR}/wolfboot.elf" + "${WOLFBOOT_MODULE_DIR}/wolfboot.bin" + ) +endif() +execute_process( + COMMAND make wolfboot.bin keytools tools/bin-assemble/bin-assemble + WOLFBOOT_ROOT=${WOLFBOOT_MODULE_DIR} + TARGET=mcxn ARCH=ARM TZEN=1 SIGN=ECC384 HASH=SHA384 + CORTEX_M33=1 + MCUXSDK=1 + MCUXPRESSO=${_MCUX_ROOT} + MCUXPRESSO_CMSIS=${_MCUX_CMSIS} + MCUXPRESSO_CPU=MCXN947VDF_cm33_core0 + MCUXPRESSO_DRIVERS=${_MCUX_DRV} + MCUXPRESSO_PROJECT_TEMPLATE=${_MCUX_TMPL} + WOLFBOOT_LIB_WOLFSSL=${_WOLFSSL_ROOT} + NO_MPU=1 NVM_FLASH_WRITEONCE=1 SPMATH=1 RAM_CODE=1 PKA=1 DEBUG_UART=1 + NO_ARM_ASM=1 WOLFBOOT_VERSION=0 + WOLFBOOT_SECTOR_SIZE=0x2000 + WOLFBOOT_KEYVAULT_ADDRESS=0x1000D000 WOLFBOOT_KEYVAULT_SIZE=0 + WOLFBOOT_NSC_ADDRESS=0x1000E000 WOLFBOOT_NSC_SIZE=0x1000 + WOLFBOOT_PARTITION_SIZE=0xC0000 + WOLFBOOT_PARTITION_BOOT_ADDRESS=0x10000 + WOLFBOOT_PARTITION_UPDATE_ADDRESS=0xD0000 + WOLFBOOT_PARTITION_SWAP_ADDRESS=0x190000 + IMAGE_HEADER_SIZE=1024 + WORKING_DIRECTORY "${WOLFBOOT_MODULE_DIR}" + RESULT_VARIABLE _wb_result +) +if(NOT _wb_result EQUAL 0) + message(FATAL_ERROR "wolfBoot make failed") +endif() + +file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/zephyr") +file(COPY "${WOLFBOOT_MODULE_DIR}/wolfboot.bin" + "${WOLFBOOT_MODULE_DIR}/wolfboot.elf" + DESTINATION "${CMAKE_BINARY_DIR}/zephyr") + +# ── Default board + overlay so no CMakePresets.json edit is required ─────────── +# This application runs in the non-secure world and is booted by wolfBoot in the +# secure world: it links the wolfBoot NSC veneer (wolfboot_tz_nsc.o), defines +# TZEN, and uses system_init_ns.c. On FRDM-MCXN947 it must be built for the /ns +# board variant -- a secure (non-/ns) build still links and signs, but wolfBoot +# hands off to the wrong memory map / vector table and the app faults at boot +# (garbled UART, never reaches the Zephyr banner). Default board and overlay here +# so the stock build needs no preset edit; an explicit override is still honored +# for porting to other boards. +set(_DEFAULT_BOARD "frdm_mcxn947/mcxn947/cpu0/ns") +set(_SECURE_BOARD "frdm_mcxn947/mcxn947/cpu0") # non-bootable secure variant pinned by mistake +if(NOT DEFINED BOARD OR "${BOARD}" STREQUAL "") + set(BOARD "${_DEFAULT_BOARD}" CACHE STRING "Target board" FORCE) +elseif("${BOARD}" STREQUAL "${_SECURE_BOARD}") + message(WARNING + "BOARD='${BOARD}' is the secure variant and will not boot under wolfBoot; " + "forcing '${_DEFAULT_BOARD}'. (Delete the build dir if it was already " + "configured for the secure board.)") + set(BOARD "${_DEFAULT_BOARD}" CACHE STRING "Target board" FORCE) +elseif(NOT "${BOARD}" STREQUAL "${_DEFAULT_BOARD}") + message(WARNING + "BOARD='${BOARD}' overrides the default '${_DEFAULT_BOARD}'. On " + "FRDM-MCXN947 the app is non-secure-world only and a non-/ns build will " + "not boot under wolfBoot; ignore this if you are porting to another board.") +endif() +if(NOT DEFINED DTC_OVERLAY_FILE OR "${DTC_OVERLAY_FILE}" STREQUAL "") + set(DTC_OVERLAY_FILE "${CMAKE_SOURCE_DIR}/app.overlay" + CACHE STRING "Devicetree overlay" FORCE) +endif() + +# ── Normal Zephyr app build ─────────────────────────────────────────────────── +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(wolfssl-ota-client) + +if(NOT EXISTS "${CMAKE_SOURCE_DIR}/__repo__") + execute_process( + COMMAND ${CMAKE_COMMAND} -E create_symlink + $ENV{ZEPHYR_BASE}/.. ${CMAKE_SOURCE_DIR}/__repo__ + COMMAND_ECHO STDOUT) +endif() + +target_sources(app PRIVATE src/main.c) +target_sources(app PRIVATE src/system_init_ns.c) +target_sources(app PRIVATE __repo__/modules/crypto/wolfssl/wolfcrypt/test/test.c) +target_sources(app PRIVATE __repo__/modules/crypto/wolfssl/wolfcrypt/benchmark/benchmark.c) +target_include_directories(app PRIVATE src) + +target_sources(app PRIVATE src/mqttClient/fwclient.c) +target_sources(app PRIVATE src/mqttClient/mqttexample.c) +target_sources(app PRIVATE src/mqttClient/mqttnet.c) +target_sources(app PRIVATE src/mqttClient/mqttport.c) +target_include_directories(app PRIVATE src/mqttClient) +target_compile_definitions(app PRIVATE WOLFMQTT_USER_SETTINGS) + +add_definitions(-DWOLFSSL_USER_SETTINGS) + +target_compile_definitions(app PRIVATE TZEN) +target_link_libraries(app PRIVATE "${WOLFBOOT_MODULE_DIR}/src/wolfboot_tz_nsc.o") +target_include_directories(app PRIVATE "${WOLFBOOT_MODULE_DIR}/include") + +# ── Auto strip + sign after every build ─────────────────────────────────────── +set(_SIGN_TOOL "${WOLFBOOT_MODULE_DIR}/tools/keytools/sign") +set(_SIGN_KEY "${WOLFBOOT_MODULE_DIR}/wolfboot_signing_private_key.der") +set(_ZEPHYR_ELF "${CMAKE_BINARY_DIR}/zephyr/zephyr.elf") +set(_ZEPHYR_BIN "${CMAKE_BINARY_DIR}/zephyr/zephyr.bin") +set(_ZEPHYR_STRIPPED "${CMAKE_BINARY_DIR}/zephyr/zephyr_stripped.bin") + +add_custom_target(sign_images ALL + COMMENT "Strip header and sign wolfBoot images (v1, v2)" + COMMAND ${CMAKE_OBJCOPY} + --gap-fill 0xFF --output-target=binary + --remove-section=.comment --remove-section=COMMON + "${_ZEPHYR_ELF}" "${_ZEPHYR_BIN}" + COMMAND dd "if=${_ZEPHYR_BIN}" "of=${_ZEPHYR_STRIPPED}" bs=1 skip=1024 + COMMAND ${CMAKE_COMMAND} -E env "IMAGE_HEADER_SIZE=1024" + "${_SIGN_TOOL}" --ecc384 --sha384 "${_ZEPHYR_STRIPPED}" "${_SIGN_KEY}" 1 + COMMAND ${CMAKE_COMMAND} -E env "IMAGE_HEADER_SIZE=1024" + "${_SIGN_TOOL}" --ecc384 --sha384 "${_ZEPHYR_STRIPPED}" "${_SIGN_KEY}" 2 + VERBATIM +) +add_dependencies(sign_images zephyr_final) + +# ── Factory image (wolfboot + signed app) ───────────────────────────────────── +set(_BIN_ASSEMBLE "${WOLFBOOT_MODULE_DIR}/tools/bin-assemble/bin-assemble") +set(_FACTORY_BIN "${CMAKE_BINARY_DIR}/zephyr/factory.bin") + +add_custom_target(factory_bin ALL + COMMENT "Assemble factory image: wolfboot at 0x0, signed app at 0x10000" + COMMAND "${_BIN_ASSEMBLE}" + "${_FACTORY_BIN}" + 0x0 "${CMAKE_BINARY_DIR}/zephyr/wolfboot.bin" + 0x10000 "${CMAKE_BINARY_DIR}/zephyr/zephyr_stripped_v1_signed.bin" + VERBATIM +) +add_dependencies(factory_bin sign_images) diff --git a/dm-wolfssl-ota-client-with-zephyr/Images/FRDM-MCXN947-TOP.jpg b/dm-wolfssl-ota-client-with-zephyr/Images/FRDM-MCXN947-TOP.jpg new file mode 100644 index 0000000..f59fb02 Binary files /dev/null and b/dm-wolfssl-ota-client-with-zephyr/Images/FRDM-MCXN947-TOP.jpg differ diff --git a/dm-wolfssl-ota-client-with-zephyr/Images/mcxn-OTA.svg b/dm-wolfssl-ota-client-with-zephyr/Images/mcxn-OTA.svg new file mode 100644 index 0000000..d40f239 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/Images/mcxn-OTA.svg @@ -0,0 +1 @@ +OTA SequenceOTA SequencewolfBootappmqttBrokerfwserverwolfBootwolfBootappappmqttBrokermqttBrokerfwserverfwserverVerify the signature of application image1Hash the image2Verify Signature in image header3Trigger the applicationTCP & TLS connection established4MQTT Connect5MQTT Connect Ack6MQTT Subscribe7MQTT Subscribe AckWaitingformessage8Trigger the Erase command9MQTT Publish (Erase)10Erase the update partition for new firmwareRepeat for each firmware chunk11Publish firmware chunk12MQTT Publish13Write the new data chunk into internal flash14Trigger the updateThe downloaded firmware will be applied in next boot \ No newline at end of file diff --git a/dm-wolfssl-ota-client-with-zephyr/LICENSE.txt b/dm-wolfssl-ota-client-with-zephyr/LICENSE.txt new file mode 100644 index 0000000..29f1b43 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/dm-wolfssl-ota-client-with-zephyr/README.md b/dm-wolfssl-ota-client-with-zephyr/README.md new file mode 100644 index 0000000..ce1858d --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/README.md @@ -0,0 +1,281 @@ +# wolfSSL NXP Application Code Hub + + + +## wolfSSL OTA client using Zephyr RTOS + +This demo demonstrates capabilities of the new FRDM-MCXN947. + +### Demo +Creating a simple OTA client using the Zephyr RTOS and wolfMQTT to realize the OTA functionality under the TrustZone. +This app is intended to work with wolfBoot, which is a secure bootloader running in the secure world. +#### Boards: FRDM-MCXN947 +#### Categories: RTOS, Zephyr, Networking +#### Peripherals: UART, ETHERNET +#### Toolchains: Zephyr + +## Table of Contents +1. [Software](#step1) +2. [Hardware](#step2) +3. [Setup](#step3) +4. [Run Demonstrator](#step4) +5. [Sequence Diagram](#step5) +6. [FAQs](#step6) +7. [Support](#step7) +8. [Release Notes](#step8) + +## 1. Software +- [MCUXpresso for VS Code 25.7.59 or newer](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc800-arm-cortex-m0-plus-/mcuxpresso-for-visual-studio-code:MCUXPRESSO-VSC?cid=wechat_iot_303216) + +- [Zephyr Setup](https://docs.zephyrproject.org/latest/develop/getting_started/index.html) + - [wolfSSL as a Module added to Zephyr](https://github.com/wolfSSL/wolfssl/blob/master/zephyr/README.md) + - [Adding the Zephyr Repository (Part 5)](https://community.nxp.com/t5/MCUXpresso-for-VSCode-Knowledge/Training-Walkthrough-of-MCUXpresso-for-VS-Code/ta-p/1634002) + +- MCUXpresso Installer: + - MCUXpresso SDK Developer + - Zephyr Developer + - Linkserver + +- Ubuntu or MacOS with the following packages: + - autoconf + - automake + - libtool + - make + - gcc + - git + + - Zephyr: + - SDK 1.0.1 + - Version 4.4.0 +- wolfSSL + - Version v5.9.1-stable +- wolfBoot + - Version 2.8.0 +- wolfMQTT + - Version 2.0.0 + +- Optional Software: + - Local mqtt broker like mosquitto + +## 2. Hardware +- [FRDM-MCXN947.](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/mcx-arm-cortex-m/mcx-n94x-and-n54x-mcus-with-dual-core-arm-cortex-m33-eiq-neutron-npu-and-edgelock-secure-enclave-core-profile:MCX-N94X-N54X) +[](Images/FRDM-MCXN947-TOP.jpg) +- USB Type-C cable. +- Ethernet Cable. +- Networking/Router +- Personal Computer. + + +## 3. Setup + +### Limitation +This setup is not tested on native Windows. +Some scripts may not work for the case. + +### Import the Project +Follow section 1: `Setup` in the top level [README](../README.md). +The project should be called `dm-wolfssl-ota-client-with-zephyr`. +You need to several stuff before build. + +### Board selection +This application runs in the non-secure world and is booted by wolfBoot running in the secure world, +so it must be built for the `frdm_mcxn947/mcxn947/cpu0/ns` board. This is set automatically in +`CMakeLists.txt` (along with the `app.overlay` devicetree overlay) — no `CMakePresets.json` edit is +required. To port the app to a different board, override `BOARD` on the command line +(e.g. `cmake . --preset debug -DBOARD=`). + +### Setup wolfBoot +wolfBoot is included in the west workspace at `__repo__/modules/bootloader/wolfboot/`. + +No manual setup is required. Running `cmake . --preset debug` automatically: +1. Applies the FRDM-MCXN947 board patch (`wolfbootConfig/0001-Update-configs-and-memory-map.patch`) +2. Builds the wolfBoot binary and keytools +3. Copies `wolfboot.bin` and `wolfboot.elf` into the build directory + +If you run `west update`, the patch is re-applied automatically on the next `cmake .` invocation. + +### Build and Prepare application image +You can trigger the build from the MCUXpresso for VS Code extension, or build manually. +``` +cmake . --preset debug +cmake --build debug --parallel +``` +After the build completes, the following files are produced automatically: +- `debug/zephyr/zephyr_stripped_v1_signed.bin` — boot image (version 1) +- `debug/zephyr/zephyr_stripped_v2_signed.bin` — OTA update image (version 2) +- `debug/zephyr/factory.bin` — combined image for initial programming (wolfboot at 0x0 + signed app at 0x10000) + +No manual strip, sign, or assembly commands are needed. + +### Build fwserver +fwserver is a simple OTA server app running on your laptop. +Build: +``` +cd fwserver +mkdir build && cd build +cmake .. +cmake --build . --parallel +``` +Then, please copy the downloadable image like `debug/zephyr/zephyr_stripped_v2_signed.bin` to the same directory with `fwserver/build/fwserver`. + +### Prepare MQTT Broker +We recommend preparing a local MQTT broker for a stable evaluation environment. +You can use mqttBroker/dockerfile to build the mosquitto test broker in local. +For example: +``` +cd mqttBroker +docker build -t mosquitto . +docker run -d -p 1883:1883 -p 8883:8883 --name mosquitto mosquitto +``` +The current setting is to use 192.168.1.10/24 on port 8883 with TLS. +Please assign the IP address to your network interface. +If you want to change the target broker, you can define it using the macro DEFAULT_MQTT_HOST. +Or please edit the following code directly. +In fwserver/mqttexample.h: +``` +#ifndef DEFAULT_MQTT_HOST + /* Default MQTT host broker to use, + * when none is specified in the examples */ + #define DEFAULT_MQTT_HOST "localhost" + /* "iot.eclipse.org" */ + /* "broker.emqx.io" */ + /* "broker.hivemq.com" */ +#endif +``` +In src/mqttClient/mqttexample.h: +``` +#ifndef DEFAULT_MQTT_HOST + /* Default MQTT host broker to use, + * when none is specified in the examples */ + #define DEFAULT_MQTT_HOST "192.168.1.10" + /* "iot.eclipse.org" */ + /* "broker.emqx.io" */ + /* "broker.hivemq.com" */ +#endif +``` + +### Connect Hardware +1. Connect the FRDM-MCXN947 to your computer with the provided USB-C Cable + +2. Connect the FRDM-MCXN947 to your network with an Ethernet cable + +### Program the wolfBoot and application +Flash both of wolfBoot and application image file to FRDM-MCXN947. +factory.bin is automatically generated based on both images. +You may need to let the MCXN enters to ISP mode to flash new image. Please check the [official application note](https://www.nxp.com/docs/en/application-note/AN14460.pdf) for the details of flash procedures. +You can use LinkServer, which is installed with MCUXpresso toolchains. +``` +/Path/To/YourLinkServer/LinkServer flash MCXN947 load debug/zephyr/factory.bin:0x0 +``` + +## 4. Run Demonstrator +Please connect to the Serial Output of the FRDM-MCXN947 via: + - Serial monitor - `/dev/tty/YourMCXN-Port` + - Screen Command - `screen /dev/tty"MCXN-Port 115200"` + - Some Serial Terminal you are familiar with + +Push reset button on the FRDM-MCXN947 board and view the startup message. Note the IP address and MQTT subscriptions. +``` +Boot partition: 0x10000 (sz 313728, ver 0x1, type 0x601) +Partition 1 header magic 0xFFFFFFFF invalid at 0xD0000 +Boot partition: 0x10000 (sz 313728, ver 0x1, type 0x601) +Booting version: 0x1 +*** Booting Zephyr OS build v4.4.0-753-gf01a10b99584 *** +[wolfBoot] BOOT v=1 state=0xaa(rc=-1) UPDATE v=0 state=0xaa(rc=-1) -> NO_UPDATE +IP Address is: 192.168.1.70 +Running wolfMQTT firmware client for OTA +MQTT Firmware Client: QoS 2, Use TLS 1 +MQTT Net Init: Success (0) +MQTT Init: Success (0) +NetConnect: Host 192.168.1.10, Port 8883, Timeout 5000 ms, Use TLS 1 +[00:00:00.050,000] phy_mii: PHY (0) ID 7C121 +[00:00:00.052,000] eth_nxp_enet_qos_mac: Link is down +MQTT TLS Setup (1) +MQTT TLS Verify Callback for fwclient: PreVerify 0, Error -188 (no support for error strings built in) + Subject's domain name is MyMosquittoCA + Allowing cert anyways +MQTT Socket Connect: Success (0) +MQTT Connect: Proto (v3.1.1), Success (0) +MQTT Connect Ack: Return Code 0, Session Present 0 +MQTT Subscribe: Success (0) + Topic wolfMQTT/example/command, Qos 2, Return Code 2 +MQTT Subscribing to firmware topic... +[0MQTT Subscribe: Success (0) + Topic wolfMQTT/example/firmware, Qos 2, Return Code 2 +MQTT Waiting for message... +MQTT Status Publish: Success (0) -> {"boot":1,"update":0,"state":"NO_UPDATE","raw":170,"rc":-1} +0:00:01.755,000] phy_mii: PHY (0) Link speed 100 Mb, full duplex +[00:00:01.755,000] eth_nxp_enet_qos_mac: Link is up +``` +Open another terminal and run fwserver: +``` +cd fwserver/build +fwserver -t +``` +The serial output shows the logs during OTA. +After the new image is downloaded, you'll see the messages: +``` +Firmware transfer complete: 301532 bytes +MQTT Exiting... +MQTT Disconnect: Success (0) +MQTT Socket Disconnect: Success (0) +Firmware client completed successfully! +Triggering wolfBoot update +``` +Reset is triggered automatically now. +wolfBoot detects the new image in the update partition and verifies it. +Then, wolfBoot will swap the downloaded image from update partition to boot partition. +``` +... +Copy sector 33 (part 0->1) +Copy sector 33 (part 2->0) +Copy sector 34 (part 1->2) +Copy sector 34 (part 0->1) +Copy sector 34 (part 2->0) +Copy sector 35 (part 1->2) +Copy sector 35 (part 0->1) +Copy sector 35 (part 2->0) +Copy sector 36 (part 1->2) +Copy sector 36 (part 0->1) +Copy sector 36 (part 2->0) +Erasing remainder of partition (57 sectors)... +Boot partition: 0x10000 (sz 300508, ver 0x2, type 0x601) +Update partition: 0xD0000 (sz 300508, ver 0x1, type 0x601) +Copy sector 93 (part 0->2) +Copied boot sector to swap +Boot partition: 0x10000 (sz 300508, ver 0x2, type 0x601) +Booting version: 0x2 +``` + +## 5. Sequence Diagram +### Overview +[](Images/mcxn-OTA.svg) + +## 6. FAQs +No FAQs have been identified for this project. + +## 7. Support + +#### Project Metadata + +[![Board badge](https://img.shields.io/badge/Board-FRDM–MCXN947-blue)](https://github.com/search?q=org%3Anxp-appcodehub+FRDM-MCXN947+in%3Areadme&type=Repositories) + + + + + +[![Peripheral badge](https://img.shields.io/badge/Peripheral-UART-yellow)](https://github.com/search?q=org%3Anxp-appcodehub+uart+in%3Areadme&type=Repositories) [![Peripheral badge](https://img.shields.io/badge/Peripheral-ETHERNET-yellow)](https://github.com/search?q=org%3Anxp-appcodehub+ethernet+in%3Areadme&type=Repositories) + + +[![Toolchain badge](https://img.shields.io/badge/Toolchain-VS%20CODE-orange)](https://github.com/search?q=org%3Anxp-appcodehub+vscode+in%3Areadme&type=Repositories) + +Questions regarding the content/correctness of this example can be entered as Issues within this GitHub repository. + +>**Warning**: For more general technical questions regarding NXP Microcontrollers and the difference in expected functionality, enter your questions on the [NXP Community Forum](https://community.nxp.com/) + + + +## 8. Release Notes +| Version | Description / Update | Date | +|:-------:|------------------------------------------------|----------------------------:| +| 1.0 | Initial release on Application Code Hub | April 23rd 2026| diff --git a/dm-wolfssl-ota-client-with-zephyr/app.overlay b/dm-wolfssl-ota-client-with-zephyr/app.overlay new file mode 100644 index 0000000..98b3785 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/app.overlay @@ -0,0 +1,46 @@ +/* + * Upstream cpu0/ns board targets TF-M: chosen points at slot0_ns_partition (256 KiB flash) + * and non_secure_ram (128 KiB). This app runs under wolfBoot with a different map; see + * wolfbootConfig/.config and hal/mcxn.c SAU (0x20016000 .. 0x20065FFF). + */ + +/ { + chosen { + zephyr,sram = &app_ns_sram; + zephyr,code-partition = &slot0_partition; + }; + + /* NS-visible SRAM for the Zephyr image (must match wolfBoot SAU). */ + app_ns_sram: memory@20016000 { + compatible = "mmio-sram"; + reg = <0x20016000 0x50000>; /* 320 KiB */ + }; +}; + +/* Drop TF-M flash layout; replace with wolfBoot partition table from wolfbootConfig/.config */ +&flash { + /delete-node/ partitions; +}; + +&flash { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "wolfboot"; + reg = <0x0 0x10000>; + }; + + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x10000 0xc0000>; + }; + + slot1_partition: partition@d0000 { + label = "image-1"; + reg = <0xd0000 0xc0000>; + }; + }; +}; diff --git a/dm-wolfssl-ota-client-with-zephyr/dm-wolfssl-ota-client-with-zephyr.xml b/dm-wolfssl-ota-client-with-zephyr/dm-wolfssl-ota-client-with-zephyr.xml new file mode 100644 index 0000000..d7b355b --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/dm-wolfssl-ota-client-with-zephyr.xml @@ -0,0 +1,25 @@ + + + wolfSSL OTA client with Zephyr + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/CMakeLists.txt b/dm-wolfssl-ota-client-with-zephyr/fwserver/CMakeLists.txt new file mode 100644 index 0000000..8e707c0 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.20) +project(fwserver C) + +set(REPO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../__repo__) + +# wolfSSL options — disable tests/examples for faster build +set(WOLFSSL_EXAMPLES "no" CACHE STRING "") +set(WOLFSSL_CRYPT_TESTS "no" CACHE STRING "") + +# wolfMQTT options — static lib, TLS on by default via wolfSSL tree +set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build static library") +set(WOLFMQTT_EXAMPLES "no" CACHE STRING "Do not build wolfMQTT examples") +set(WOLFMQTT_TLS "yes" CACHE STRING "Enable TLS support") +set(WITH_WOLFSSL_TREE ${REPO_DIR}/modules/crypto/wolfssl + CACHE PATH "Path to wolfSSL source tree") + +add_subdirectory( + ${REPO_DIR}/modules/lib/wolfmqtt + ${CMAKE_CURRENT_BINARY_DIR}/wolfmqtt_build +) + +add_executable(fwserver + fwserver.c + mqttexample.c + mqttnet.c + mqttport.c +) + +# wolfmqtt exports wolfmqtt + wolfssl include dirs transitively. +# Add fwserver dir for local headers (firmware.h, fwserver.h, mqttexample.h, +# mqttnet.h, mqttport.h). +target_include_directories(fwserver PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(fwserver PRIVATE wolfmqtt) diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/firmware.h b/dm-wolfssl-ota-client-with-zephyr/fwserver/firmware.h new file mode 100644 index 0000000..57d6c89 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/firmware.h @@ -0,0 +1,56 @@ +/* firmware.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_FIRMWARE_H +#define WOLFMQTT_FIRMWARE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define FIRMWARE_TOPIC_NAME "wolfMQTT/example/firmware" +#define COMMAND_TOPIC_NAME "wolfMQTT/example/command" +#define FIRMWARE_MAX_BUFFER 2048 +#define FIRMWARE_MAX_PACKET (int)(FIRMWARE_MAX_BUFFER + sizeof(MqttPacket) + XSTRLEN(FIRMWARE_TOPIC_NAME) + MQTT_DATA_LEN_SIZE) +#define FIRMWARE_MQTT_QOS MQTT_QOS_2 +#define FIRMWARE_PUBLISH_DELAY_MS 200 + +typedef struct messageHeader { + word16 chunkNumber; + word16 chunkSize; + word32 totalLen; +} WOLFMQTT_PACK MessageHeader; + +typedef struct commandHeader { + word16 commandId; + word16 commandLen; +} WOLFMQTT_PACK CommandHeader; + +enum CommandIds { + COMMAND_ID_REBOOT = 1, + COMMAND_ID_ERASE = 2, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFMQTT_FIRMWARE_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/fwserver.c b/dm-wolfssl-ota-client-with-zephyr/fwserver/fwserver.c new file mode 100644 index 0000000..60cfa07 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/fwserver.c @@ -0,0 +1,668 @@ +/* fwpush.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_client.h" + +#if defined(USE_WINDOWS_API) + #include +#elif !defined(NO_FILESYSTEM) + #include +#endif + +#if defined(ENABLE_MQTT_TLS) + #if !defined(WOLFSSL_USER_SETTINGS) && !defined(USE_WINDOWS_API) + #include + #endif + #include +#endif + +#include "fwserver.h" +#include "firmware.h" +#include "mqttexample.h" +#include "mqttnet.h" + +/* Configuration */ +#ifndef MAX_BUFFER_SIZE +#define MAX_BUFFER_SIZE FIRMWARE_MAX_PACKET +#endif + +#define FIRMWARE_CHUNK_DATA_MAX ((word32)(FIRMWARE_MAX_BUFFER - sizeof(MessageHeader))) +#define ERASE_COMMAND_WAIT_MS 2000 + +/* Locals */ +static int mStopRead = 0; + +#if !defined(NO_FILESYSTEM) +typedef struct FwpushTransfer_s { + FILE* fp; + const char* filename; + word32 total_len; + word32 bytes_sent; + word16 chunk_number; + word16 chunk_payload_len; + int chunk_ready; + int done; + int erase_cmd_ready; + int erase_cmd_sent; + int erase_wait_done; +} FwpushTransfer; +#endif + +static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, + byte msg_new, byte msg_done) +{ + MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx; + + (void)mqttCtx; + (void)msg; + (void)msg_new; + (void)msg_done; + + /* Return negative to terminate publish processing */ + return MQTT_CODE_SUCCESS; +} + +static void fwpush_publish_delay(void) +{ +#if FIRMWARE_PUBLISH_DELAY_MS > 0 + #ifdef USE_WINDOWS_API + Sleep(FIRMWARE_PUBLISH_DELAY_MS); + #else + usleep(FIRMWARE_PUBLISH_DELAY_MS * 1000); + #endif +#endif +} + +#if !defined(NO_FILESYSTEM) +static void fwpush_erase_wait(void) +{ +#ifdef USE_WINDOWS_API + Sleep(ERASE_COMMAND_WAIT_MS); +#else + usleep(ERASE_COMMAND_WAIT_MS * 1000); +#endif +} +#endif + +#if !defined(NO_FILESYSTEM) +static void fwpush_transfer_deinit(FwpushTransfer* transfer) +{ + if (transfer != NULL && transfer->fp != NULL) { + fclose(transfer->fp); + transfer->fp = NULL; + } +} + +static int fwpush_transfer_init(MQTTCtx* mqttCtx, FwpushTransfer* transfer, + const char* filename) +{ + long file_len; + + if (mqttCtx == NULL || transfer == NULL || filename == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + XMEMSET(transfer, 0, sizeof(*transfer)); + transfer->filename = filename; + transfer->fp = fopen(filename, "rb"); + if (transfer->fp == NULL) { + PRINTF("Firmware file %s open error", filename); + return MQTT_CODE_ERROR_SYSTEM; + } + + if (fseek(transfer->fp, 0, SEEK_END) != 0) { + PRINTF("Firmware file %s seek end failed", filename); + return MQTT_CODE_ERROR_SYSTEM; + } + + file_len = ftell(transfer->fp); + if (file_len <= 0) { + PRINTF("Firmware file %s has invalid size %ld", filename, file_len); + return MQTT_CODE_ERROR_OUT_OF_BUFFER; + } + + if ((unsigned long)file_len > 0xFFFFFFFFUL) { + PRINTF("Firmware file %s exceeds max supported size", filename); + return MQTT_CODE_ERROR_OUT_OF_BUFFER; + } + + if (fseek(transfer->fp, 0, SEEK_SET) != 0) { + PRINTF("Firmware file %s seek start failed", filename); + return MQTT_CODE_ERROR_SYSTEM; + } + + transfer->total_len = (word32)file_len; + + PRINTF("Firmware file %s is %u bytes", filename, transfer->total_len); + + (void)mqttCtx; + return MQTT_CODE_SUCCESS; +} + +static int fwpush_transfer_prepare_chunk(MQTTCtx* mqttCtx, + FwpushTransfer* transfer) +{ + MessageHeader* header; + size_t bytes_read; + MqttPublish* publish; + + if (mqttCtx == NULL || transfer == NULL || transfer->fp == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + publish = &mqttCtx->publish; + header = (MessageHeader*)publish->buffer; + + bytes_read = fread(&publish->buffer[sizeof(MessageHeader)], 1, + FIRMWARE_CHUNK_DATA_MAX, transfer->fp); + if (bytes_read == 0) { + if (feof(transfer->fp)) { + transfer->done = 1; + return MQTT_CODE_SUCCESS; + } + PRINTF("Firmware file %s read error", transfer->filename); + return MQTT_CODE_ERROR_SYSTEM; + } + + if (bytes_read > (size_t)0xFFFFU) { + PRINTF("Chunk size overflow %u", (unsigned)bytes_read); + return MQTT_CODE_ERROR_OUT_OF_BUFFER; + } + + header->chunkNumber = transfer->chunk_number; + header->chunkSize = (word16)bytes_read; + header->totalLen = transfer->total_len; + + transfer->chunk_payload_len = (word16)bytes_read; + transfer->chunk_ready = 1; + + publish->packet_id = mqtt_get_packetid(); + publish->total_len = sizeof(MessageHeader) + (word32)bytes_read; + publish->buffer_len = publish->total_len; + + return MQTT_CODE_SUCCESS; +} + +static int fwpush_transfer_send(MQTTCtx* mqttCtx, FwpushTransfer* transfer) +{ + int rc; + + if (mqttCtx == NULL || transfer == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + while (!transfer->done) { + if (!transfer->chunk_ready) { + rc = fwpush_transfer_prepare_chunk(mqttCtx, transfer); + if (rc != MQTT_CODE_SUCCESS) { + return rc; + } + + if (transfer->done) { + break; + } + } + + rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + if (rc != MQTT_CODE_SUCCESS) { + return rc; + } + + transfer->bytes_sent += transfer->chunk_payload_len; + PRINTF("MQTT Publish chunk %u: %u bytes (%u/%u)", + transfer->chunk_number, + transfer->chunk_payload_len, + transfer->bytes_sent, + transfer->total_len); + + transfer->chunk_ready = 0; + + if (transfer->bytes_sent > transfer->total_len) { + PRINTF("Transferred bytes exceed expected total"); + return MQTT_CODE_ERROR_MALFORMED_DATA; + } + + if (transfer->bytes_sent == transfer->total_len) { + transfer->done = 1; + break; + } + + fwpush_publish_delay(); + + if (transfer->chunk_number == 0xFFFFU) { + PRINTF("Firmware requires more than 65536 chunks"); + return MQTT_CODE_ERROR_OUT_OF_BUFFER; + } + + transfer->chunk_number++; + } + + return MQTT_CODE_SUCCESS; +} + +static int fwpush_send_erase_command(MQTTCtx* mqttCtx, FwpushTransfer* transfer) +{ + int rc; + CommandHeader* command; + MqttPublish* publish; + + if (mqttCtx == NULL || transfer == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + publish = &mqttCtx->publish; + + if (!transfer->erase_cmd_ready) { + command = (CommandHeader*)publish->buffer; + command->commandId = COMMAND_ID_ERASE; + command->commandLen = 0; + + publish->topic_name = COMMAND_TOPIC_NAME; + publish->packet_id = mqtt_get_packetid(); + publish->total_len = sizeof(CommandHeader); + publish->buffer_len = publish->total_len; + transfer->erase_cmd_ready = 1; + } + + rc = MqttClient_Publish(&mqttCtx->client, publish); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + if (rc != MQTT_CODE_SUCCESS) { + return rc; + } + + transfer->erase_cmd_ready = 0; + transfer->erase_cmd_sent = 1; + publish->topic_name = mqttCtx->topic_name; + publish->buffer_len = FIRMWARE_MAX_BUFFER; + + PRINTF("MQTT Command publish complete: ERASE"); + + return MQTT_CODE_SUCCESS; +} +#endif /* !NO_FILESYSTEM */ + +int fwpush_test(MQTTCtx *mqttCtx) +{ + int rc; +#if !defined(NO_FILESYSTEM) + FwpushTransfer* transfer = NULL; +#endif + + if (mqttCtx == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* restore callback data */ +#if !defined(NO_FILESYSTEM) + transfer = (FwpushTransfer*)mqttCtx->publish.ctx; +#endif + + /* check for stop */ + if (mStopRead) { + rc = MQTT_CODE_SUCCESS; + PRINTF("MQTT Exiting..."); + mStopRead = 0; + goto disconn; + } + + switch (mqttCtx->stat) + { + case WMQ_BEGIN: + { + PRINTF("MQTT Firmware Push Client: QoS %d, Use TLS %d", + mqttCtx->qos, mqttCtx->use_tls); + } + FALL_THROUGH; + + case WMQ_NET_INIT: + { + mqttCtx->stat = WMQ_NET_INIT; + + /* Initialize Network */ + rc = MqttClientNet_Init(&mqttCtx->net, mqttCtx); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Net Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* setup tx/rx buffers */ + mqttCtx->tx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + mqttCtx->rx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + } + FALL_THROUGH; + + case WMQ_INIT: + { + mqttCtx->stat = WMQ_INIT; + + /* Initialize MqttClient structure */ + rc = MqttClient_Init(&mqttCtx->client, &mqttCtx->net, + mqtt_message_cb, + mqttCtx->tx_buf, MAX_BUFFER_SIZE, + mqttCtx->rx_buf, MAX_BUFFER_SIZE, + mqttCtx->cmd_timeout_ms); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + mqttCtx->client.ctx = mqttCtx; + } + FALL_THROUGH; + + case WMQ_TCP_CONN: + { + mqttCtx->stat = WMQ_TCP_CONN; + + /* Connect to broker */ + rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host, + mqttCtx->port, DEFAULT_CON_TIMEOUT_MS, mqttCtx->use_tls, + mqtt_tls_cb); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Socket Connect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* Build connect packet */ + XMEMSET(&mqttCtx->connect, 0, sizeof(MqttConnect)); + mqttCtx->connect.keep_alive_sec = mqttCtx->keep_alive_sec; + mqttCtx->connect.clean_session = mqttCtx->clean_session; + mqttCtx->connect.client_id = mqttCtx->client_id; + if (mqttCtx->enable_lwt) { + /* Send client id in LWT payload */ + mqttCtx->lwt_msg.qos = mqttCtx->qos; + mqttCtx->lwt_msg.retain = 0; + mqttCtx->lwt_msg.topic_name = FIRMWARE_TOPIC_NAME"lwttopic"; + mqttCtx->lwt_msg.buffer = (byte*)mqttCtx->client_id; + mqttCtx->lwt_msg.total_len = + (word16)XSTRLEN(mqttCtx->client_id); + } + + /* Optional authentication */ + mqttCtx->connect.username = mqttCtx->username; + mqttCtx->connect.password = mqttCtx->password; + } + FALL_THROUGH; + + case WMQ_MQTT_CONN: + { + mqttCtx->stat = WMQ_MQTT_CONN; + + /* Send Connect and wait for Connect Ack */ + rc = MqttClient_Connect(&mqttCtx->client, &mqttCtx->connect); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + + PRINTF("MQTT Connect: Proto (%s), %s (%d)", + MqttClient_GetProtocolVersionString(&mqttCtx->client), + MqttClient_ReturnCodeToString(rc), rc); + + /* Validate Connect Ack info */ + PRINTF("MQTT Connect Ack: Return Code %u, Session Present %d", + mqttCtx->connect.ack.return_code, + (mqttCtx->connect.ack.flags & + MQTT_CONNECT_ACK_FLAG_SESSION_PRESENT) ? + 1 : 0 + ); + + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + + /* setup publish message */ + XMEMSET(&mqttCtx->publish, 0, sizeof(MqttPublish)); + mqttCtx->publish.retain = mqttCtx->retain; + mqttCtx->publish.qos = mqttCtx->qos; + mqttCtx->publish.duplicate = 0; + mqttCtx->publish.topic_name = mqttCtx->topic_name; + mqttCtx->publish.buffer_len = FIRMWARE_MAX_BUFFER; + mqttCtx->publish.buffer = (byte*)WOLFMQTT_MALLOC(FIRMWARE_MAX_BUFFER); + if (mqttCtx->publish.buffer == NULL) { + rc = MQTT_CODE_ERROR_OUT_OF_BUFFER; + goto disconn; + } + +#if !defined(NO_FILESYSTEM) + transfer = (FwpushTransfer*)WOLFMQTT_MALLOC(sizeof(FwpushTransfer)); + if (transfer == NULL) { + rc = MQTT_CODE_ERROR_OUT_OF_BUFFER; + goto disconn; + } + + rc = fwpush_transfer_init(mqttCtx, transfer, mqttCtx->pub_file); + if (rc != MQTT_CODE_SUCCESS) { + mqtt_show_usage(mqttCtx); + goto disconn; + } + + mqttCtx->publish.ctx = transfer; +#else + PRINTF("Firmware push requires filesystem support"); + rc = MQTT_CODE_ERROR_SYSTEM; + goto disconn; +#endif + } + FALL_THROUGH; + + case WMQ_PUB: + { + mqttCtx->stat = WMQ_PUB; + +#if !defined(NO_FILESYSTEM) + if (!transfer->erase_cmd_sent) { + rc = fwpush_send_erase_command(mqttCtx, transfer); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + } + + if (!transfer->erase_wait_done) { + PRINTF("Waiting %u ms after erase command...", ERASE_COMMAND_WAIT_MS); + fwpush_erase_wait(); + transfer->erase_wait_done = 1; + } + + rc = fwpush_transfer_send(mqttCtx, transfer); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + + PRINTF("MQTT Publish complete: %u bytes sent in %u chunks", + transfer->bytes_sent, (unsigned)transfer->chunk_number + 1U); +#else + rc = MQTT_CODE_ERROR_SYSTEM; + goto disconn; +#endif + } + FALL_THROUGH; + + case WMQ_DISCONNECT: + { + mqttCtx->stat = WMQ_DISCONNECT; + + /* Disconnect */ + rc = MqttClient_Disconnect(&mqttCtx->client); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + } + FALL_THROUGH; + + case WMQ_NET_DISCONNECT: + { + mqttCtx->stat = WMQ_NET_DISCONNECT; + + rc = MqttClient_NetDisconnect(&mqttCtx->client); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Socket Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + FALL_THROUGH; + + case WMQ_DONE: + { + mqttCtx->stat = WMQ_DONE; + rc = mqttCtx->return_code; + goto exit; + } + + case WMQ_SUB: + case WMQ_WAIT_MSG: + case WMQ_UNSUB: + case WMQ_PING: + default: + rc = MQTT_CODE_ERROR_STAT; + goto exit; + } /* switch */ + +disconn: + mqttCtx->stat = WMQ_NET_DISCONNECT; + mqttCtx->return_code = rc; + rc = MQTT_CODE_CONTINUE; + +exit: + + if (rc != MQTT_CODE_CONTINUE) { +#if !defined(NO_FILESYSTEM) + if (transfer != NULL) { + fwpush_transfer_deinit(transfer); + WOLFMQTT_FREE(transfer); + mqttCtx->publish.ctx = NULL; + } +#endif + if (mqttCtx->publish.buffer) WOLFMQTT_FREE(mqttCtx->publish.buffer); + if (mqttCtx->tx_buf) WOLFMQTT_FREE(mqttCtx->tx_buf); + if (mqttCtx->rx_buf) WOLFMQTT_FREE(mqttCtx->rx_buf); + + /* Cleanup network */ + MqttClientNet_DeInit(&mqttCtx->net); + + MqttClient_DeInit(&mqttCtx->client); + } + + return rc; +} + + +/* so overall tests can pull in test function */ +#ifdef USE_WINDOWS_API + #include /* for ctrl handler */ + + static BOOL CtrlHandler(DWORD fdwCtrlType) + { + if (fdwCtrlType == CTRL_C_EVENT) { + mStopRead = 1; + PRINTF("Received Ctrl+c"); + return TRUE; + } + return FALSE; + } +#elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + mStopRead = 1; + PRINTF("Received SIGINT"); + (void)signal(SIGINT, SIG_DFL); + (void)raise(SIGINT); + } + } +#endif + +#if defined(NO_MAIN_DRIVER) +int fwpush_main(int argc, char** argv) +#else +int main(int argc, char** argv) +#endif +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + mqttCtx.app_name = "fwpush"; + mqttCtx.client_id = mqtt_append_random(FIRMWARE_PUSH_CLIENT_ID, + (word32)XSTRLEN(FIRMWARE_PUSH_CLIENT_ID)); + mqttCtx.dynamicClientId = 1; + mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; + mqttCtx.qos = FIRMWARE_MQTT_QOS; + mqttCtx.pub_file = FIRMWARE_PUSH_DEF_FILE; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + return rc; + } + +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif + + do { + rc = fwpush_test(&mqttCtx); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); + + mqtt_free_ctx(&mqttCtx); + + return (rc == 0) ? 0 : EXIT_FAILURE; +} diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/fwserver.h b/dm-wolfssl-ota-client-with-zephyr/fwserver/fwserver.h new file mode 100644 index 0000000..0656aa3 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/fwserver.h @@ -0,0 +1,37 @@ +/* fwpush.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_FWPUSH_H +#define WOLFMQTT_FWPUSH_H + +#include "mqttexample.h" + +#define FIRMWARE_PUSH_CLIENT_ID "WolfMQTTFwPush" +#define FIRMWARE_PUSH_DEF_FILE "zephyr_stripped_v2_signed.bin" + +/* Exposed functions */ +int fwpush_test(MQTTCtx *mqttCtx); + +#if defined(NO_MAIN_DRIVER) +int fwpush_main(int argc, char** argv); +#endif + +#endif /* WOLFMQTT_FWPUSH_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttexample.c b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttexample.c new file mode 100644 index 0000000..affa6cb --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttexample.c @@ -0,0 +1,934 @@ +/* mqttexample.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "wolfmqtt/mqtt_client.h" +#include "mqttexample.h" +#include "mqttnet.h" +#include "mqttport.h" + + +/* locals */ +static volatile word16 mPacketIdLast; +static const char* kDefTopicName = DEFAULT_TOPIC_NAME; +static const char* kDefClientId = DEFAULT_CLIENT_ID; + +/* argument parsing */ +static int myoptind = 0; +static char* myoptarg = NULL; + +#ifdef ENABLE_MQTT_TLS +#ifdef HAVE_SNI +static int useSNI; +static const char* mTlsSniHostName = NULL; +#endif +#ifdef HAVE_PQC +static const char* mTlsPQAlg = NULL; +#endif +#endif /* ENABLE_MQTT_TLS */ + +static int mygetopt(int argc, char** argv, const char* optstring) +{ + static char* next = NULL; + + char c; + char* cp; + + if (myoptind == 0) + next = NULL; /* we're starting new/over */ + + if (next == NULL || *next == '\0') { + if (myoptind == 0) + myoptind++; + + if (myoptind >= argc || argv[myoptind][0] != '-' || + argv[myoptind][1] == '\0') { + myoptarg = NULL; + if (myoptind < argc) + myoptarg = argv[myoptind]; + + return -1; + } + + if (XSTRNCMP(argv[myoptind], "--", 2) == 0) { + myoptind++; + myoptarg = NULL; + + if (myoptind < argc) + myoptarg = argv[myoptind]; + + return -1; + } + + next = argv[myoptind]; + next++; /* skip - */ + myoptind++; + } + + c = *next++; + /* The C++ strchr can return a different value */ + cp = (char*)XSTRCHR(optstring, c); + + if (cp == NULL || c == ':') + return '?'; + + cp++; + + if (*cp == ':') { + if (*next != '\0') { + myoptarg = next; + next = NULL; + } + else if (myoptind < argc) { + myoptarg = argv[myoptind]; + myoptind++; + } + else + return '?'; + } + else if (*cp == ';') { + myoptarg = (char*)""; + if (*next != '\0') { + myoptarg = next; + next = NULL; + } + else if (myoptind < argc) { + /* Check if next argument is not a parameter argument */ + if (argv[myoptind] && argv[myoptind][0] != '-') { + myoptarg = argv[myoptind]; + myoptind++; + } + } + } + + return c; +} + + +/* used for testing only, requires wolfSSL RNG */ +#ifdef ENABLE_MQTT_TLS +#include +#endif + +static int mqtt_get_rand(byte* data, word32 len) +{ + int ret = -1; +#ifdef ENABLE_MQTT_TLS + WC_RNG rng; + ret = wc_InitRng(&rng); + if (ret == 0) { + ret = wc_RNG_GenerateBlock(&rng, data, len); + wc_FreeRng(&rng); + } +#elif defined(HAVE_RAND) + word32 i; + for (i = 0; i (int)sizeof(rndBytes)) + sz = (int)sizeof(rndBytes); + sz /= 2; /* 1 byte expands to 2 bytes */ + + rc = mqtt_get_rand(rndBytes, sz); + if (rc == 0) { + /* Convert random to hex string */ + for (i=0; i> 4]; + buf[pos + (i*2)+1] = kHexChar[in & 0xf]; + } + pos += sz*2; + } + else { + PRINTF("MQTT Fill Random Failed! %d", rc); + } + } + return rc; +} + +#ifndef TEST_RAND_SZ +#define TEST_RAND_SZ 4 +#endif +char* mqtt_append_random(const char* inStr, word32 inLen) +{ + int rc = 0; + char *tmp; + + tmp = (char*)WOLFMQTT_MALLOC(inLen + 1 + (TEST_RAND_SZ*2) + 1); + if (tmp == NULL) { + rc = MQTT_CODE_ERROR_MEMORY; + } + if (rc == 0) { + /* Format: inStr + `_` randhex + null term */ + XMEMCPY(tmp, inStr, inLen); + tmp[inLen] = '_'; + rc = mqtt_fill_random_hexstr(tmp + inLen + 1, (TEST_RAND_SZ*2)); + tmp[inLen + 1 + (TEST_RAND_SZ*2)] = '\0'; /* null term */ + } + if (rc != 0) { + WOLFMQTT_FREE(tmp); + tmp = NULL; + } + return tmp; +} + +void mqtt_show_usage(MQTTCtx* mqttCtx) +{ + PRINTF("%s:", mqttCtx->app_name); + PRINTF("-? Help, print this usage"); + PRINTF("-h Host to connect to, default: %s", + mqttCtx->host); +#ifdef ENABLE_MQTT_TLS + PRINTF("-p Port to connect on, default: Normal %d, TLS %d", + MQTT_DEFAULT_PORT, MQTT_SECURE_PORT); + PRINTF("-t Enable TLS"); /* Note: this string is used in test + * scripts to detect TLS feature */ + PRINTF("-A Load CA (validate peer)"); + PRINTF("-K Use private key (for TLS mutual auth)"); + PRINTF("-c Use certificate (for TLS mutual auth)"); + #ifndef ENABLE_MQTT_CURL + #ifdef HAVE_SNI + /* Remove SNI args for sn-client */ + if(XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){ + PRINTF("-S Use Host Name Indication, blank defaults to host"); + } + #endif /* HAVE_SNI */ + #ifdef HAVE_PQC + PRINTF("-Q Use Key Share with post-quantum algorithm"); + #endif /* HAVE_PQC */ + #endif /* !ENABLE_MQTT_CURL */ + PRINTF("-p Port to connect on, default: %d", + MQTT_DEFAULT_PORT); +#endif + PRINTF("-q Qos Level 0-2, default: %d", + mqttCtx->qos); + PRINTF("-s Disable clean session connect flag"); + PRINTF("-k Keep alive seconds, default: %d", + mqttCtx->keep_alive_sec); + PRINTF("-i Client Id, default: %s", + mqttCtx->client_id); + PRINTF("-l Enable LWT (Last Will and Testament)"); + PRINTF("-u Username"); + PRINTF("-w Password"); + if (mqttCtx->message) { + /* Only mqttclient example can set message from CLI */ + PRINTF("-m Message, default: %s", mqttCtx->message); + } + PRINTF("-n Topic name, default: %s", mqttCtx->topic_name); + PRINTF("-r Set Retain flag on publish message"); + PRINTF("-C Command Timeout, default: %dms", + mqttCtx->cmd_timeout_ms); +#ifdef WOLFMQTT_V5 + PRINTF("-P Max packet size the client will accept, default: %d", + DEFAULT_MAX_PKT_SZ); +#endif + PRINTF("-T Test mode"); + PRINTF("-x Skip subscribe (for testing session persistence)"); + PRINTF("-R Ready file (touched when subscribed, for test sync)"); + PRINTF("-f Use file contents for publish"); + if (!mqttCtx->debug_on) { + PRINTF("-d Enable example debug messages"); + } +} + +void mqtt_init_ctx(MQTTCtx* mqttCtx) +{ + XMEMSET(mqttCtx, 0, sizeof(MQTTCtx)); + mqttCtx->host = DEFAULT_MQTT_HOST; + mqttCtx->qos = DEFAULT_MQTT_QOS; + mqttCtx->clean_session = 1; + mqttCtx->keep_alive_sec = DEFAULT_KEEP_ALIVE_SEC; + mqttCtx->client_id = kDefClientId; + mqttCtx->topic_name = kDefTopicName; + mqttCtx->cmd_timeout_ms = DEFAULT_CMD_TIMEOUT_MS; + mqttCtx->debug_on = 1; +#ifdef WOLFMQTT_V5 + mqttCtx->max_packet_size = DEFAULT_MAX_PKT_SZ; + mqttCtx->topic_alias = 1; + mqttCtx->topic_alias_max = 1; +#endif +#ifdef WOLFMQTT_DEFAULT_TLS + mqttCtx->use_tls = WOLFMQTT_DEFAULT_TLS; +#endif +#ifdef ENABLE_MQTT_TLS + mqttCtx->ca_file = NULL; + mqttCtx->mtls_keyfile = NULL; + mqttCtx->mtls_certfile = NULL; +#endif + mqttCtx->app_name = "mqttclient"; + mqttCtx->message = DEFAULT_MESSAGE; +} + +int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) +{ + int rc; + + #ifdef ENABLE_MQTT_TLS + #ifdef ENABLE_MQTT_CURL + #define MQTT_TLS_ARGS "c:A:K:" + #else + #define MQTT_TLS_ARGS "c:A:K:S;Q:" + #endif + #else + #define MQTT_TLS_ARGS "" + #endif + #ifdef WOLFMQTT_V5 + #define MQTT_V5_ARGS "P:" + #else + #define MQTT_V5_ARGS "" + #endif + + while ((rc = mygetopt(argc, argv, "?h:p:q:sk:i:lu:w:m:n:C:Tf:rtdxR:" \ + MQTT_TLS_ARGS MQTT_V5_ARGS)) != -1) { + switch ((char)rc) { + case '?' : + mqtt_show_usage(mqttCtx); + return MY_EX_USAGE; + + case 'h' : + mqttCtx->host = myoptarg; + break; + + case 'p' : + mqttCtx->port = (word16)XATOI(myoptarg); + if (mqttCtx->port == 0) { + return err_sys("Invalid Port Number!"); + } + break; + + case 'q' : + mqttCtx->qos = (MqttQoS)((byte)XATOI(myoptarg)); + if (mqttCtx->qos > MQTT_QOS_2) { + return err_sys("Invalid QoS value!"); + } + break; + + case 's': + mqttCtx->clean_session = 0; + break; + + case 'k': + mqttCtx->keep_alive_sec = XATOI(myoptarg); + break; + + case 'i': + mqttCtx->client_id = myoptarg; + break; + + case 'l': + mqttCtx->enable_lwt = 1; + break; + + case 'u': + mqttCtx->username = myoptarg; + break; + + case 'w': + mqttCtx->password = myoptarg; + break; + + case 'm': + mqttCtx->message = myoptarg; + break; + + case 'n': + mqttCtx->topic_name = myoptarg; + break; + + case 'C': + mqttCtx->cmd_timeout_ms = XATOI(myoptarg); + break; + + case 'T': + mqttCtx->test_mode = 1; + break; + + case 'f': + mqttCtx->pub_file = myoptarg; + break; + + case 'r': + mqttCtx->retain = 1; + break; + + case 't': + mqttCtx->use_tls = 1; + break; + + case 'd': + mqttCtx->debug_on = 1; + break; + + case 'x': + mqttCtx->skip_subscribe = 1; + break; + + case 'R': + mqttCtx->ready_file = myoptarg; + break; + + #ifdef ENABLE_MQTT_TLS + case 'A': + mqttCtx->ca_file = myoptarg; + break; + case 'c': + mqttCtx->mtls_certfile = myoptarg; + break; + case 'K': + mqttCtx->mtls_keyfile = myoptarg; + break; + #ifndef ENABLE_MQTT_CURL + case 'S': + #ifdef HAVE_SNI + useSNI = 1; + mTlsSniHostName = myoptarg; + #else + PRINTF("To use '-S', enable SNI in wolfSSL"); + #endif + break; + case 'Q': + #ifdef HAVE_PQC + mTlsPQAlg = myoptarg; + #else + PRINTF("To use '-Q', build wolfSSL with --enable-mlkem --enable-dilithium"); + #endif + break; + #endif /* !ENABLE_MQTT_CURL */ + #endif /* ENABLE_MQTT_TLS */ + + #ifdef WOLFMQTT_V5 + case 'P': + mqttCtx->max_packet_size = XATOI(myoptarg); + break; + #endif + + default: + mqtt_show_usage(mqttCtx); + return MY_EX_USAGE; + } + + /* Remove SNI functionality for sn-client */ + if(!XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){ + #ifdef HAVE_SNI + useSNI=0; + #endif + } + } + + rc = 0; + myoptind = 0; /* reset for test cases */ + + /* if TLS not enable, check args */ +#ifndef ENABLE_MQTT_TLS + if (mqttCtx->use_tls) { + PRINTF("Use TLS option not allowed (TLS not compiled in)"); + mqttCtx->use_tls = 0; + if (mqttCtx->test_mode) { + return MY_EX_USAGE; + } + } +#endif + +#ifdef HAVE_SNI + if ((useSNI == 1) && (XSTRLEN(mTlsSniHostName) == 0)) { + /* Set SNI host name to host if -S was blank */ + mTlsSniHostName = mqttCtx->host; + } +#endif + + /* for test mode only */ + /* add random data to end of client_id and topic_name */ + if (mqttCtx->test_mode && mqttCtx->topic_name == kDefTopicName) { + char* topic_name = mqtt_append_random(kDefTopicName, + (word32)XSTRLEN(kDefTopicName)); + if (topic_name) { + mqttCtx->topic_name = (const char*)topic_name; + mqttCtx->dynamicTopic = 1; + } + } + if (mqttCtx->test_mode && mqttCtx->client_id == kDefClientId) { + char* client_id = mqtt_append_random(kDefClientId, + (word32)XSTRLEN(kDefClientId)); + if (client_id) { + mqttCtx->client_id = (const char*)client_id; + mqttCtx->dynamicClientId = 1; + } + } + + return rc; +} + +void mqtt_free_ctx(MQTTCtx* mqttCtx) +{ + if (mqttCtx == NULL) { + return; + } + + if (mqttCtx->dynamicTopic && mqttCtx->topic_name) { + WOLFMQTT_FREE((char*)mqttCtx->topic_name); + mqttCtx->topic_name = NULL; + } + if (mqttCtx->dynamicClientId && mqttCtx->client_id) { + WOLFMQTT_FREE((char*)mqttCtx->client_id); + mqttCtx->client_id = NULL; + } +} + +#if defined(__GNUC__) && !defined(NO_EXIT) && !defined(WOLFMQTT_ZEPHYR) + __attribute__ ((noreturn)) +#endif +int err_sys(const char* msg) +{ + if (msg) { + PRINTF("wolfMQTT error: %s", msg); + } + exit(EXIT_FAILURE); +#ifdef WOLFMQTT_ZEPHYR + /* Zephyr compiler produces below warning. Let's silence it. + * warning: 'noreturn' function does return + * 477 | } + * | ^ + */ + return 0; +#endif +} + + +word16 mqtt_get_packetid(void) +{ + /* Check rollover */ + if (mPacketIdLast >= MAX_PACKET_ID) { + mPacketIdLast = 0; + } + + return ++mPacketIdLast; +} + +#ifdef WOLFMQTT_NONBLOCK +#if defined(MICROCHIP_MPLAB_HARMONY) + #include +#else + #include +#endif + +static word32 mqtt_get_timer_seconds(void) +{ + word32 timer_sec = 0; + +#if defined(MICROCHIP_MPLAB_HARMONY) + timer_sec = (word32)(SYS_TMR_TickCountGet() / + SYS_TMR_TickCounterFrequencyGet()); +#else + /* Posix style time */ + timer_sec = (word32)time(0); +#endif + + return timer_sec; +} + +int mqtt_check_timeout(int rc, word32* start_sec, word32 timeout_sec) +{ + word32 elapsed_sec; + + /* if start seconds not set or is not continue */ + if (*start_sec == 0 || rc != MQTT_CODE_CONTINUE) { + *start_sec = mqtt_get_timer_seconds(); + return rc; + } + + /* Default to 2s timeout. This function sometimes incorrectly + * triggers if 1s is used because of rounding. */ + if (timeout_sec == 0) { + timeout_sec = DEFAULT_CHK_TIMEOUT_S; + } + + elapsed_sec = mqtt_get_timer_seconds(); + if (*start_sec < elapsed_sec) { + elapsed_sec -= *start_sec; + if (elapsed_sec >= timeout_sec) { + *start_sec = mqtt_get_timer_seconds(); + PRINTF("Timeout timer %d seconds", timeout_sec); + return MQTT_CODE_ERROR_TIMEOUT; + } + } + + return rc; +} +#endif /* WOLFMQTT_NONBLOCK */ + + +#if defined(ENABLE_MQTT_TLS) && !defined(EXTERNAL_MQTT_TLS_CALLBACK) + +#ifdef WOLFSSL_ENCRYPTED_KEYS +int mqtt_password_cb(char* passwd, int sz, int rw, void* userdata) +{ + (void)rw; + (void)userdata; + if (userdata != NULL) { + XSTRNCPY(passwd, (char*)userdata, sz); + return (int)XSTRLEN((char*)userdata); + } + else { + XSTRNCPY(passwd, "yassl123", sz); + return (int)XSTRLEN(passwd); + } +} +#endif + +static int mqtt_tls_verify_cb(int preverify, WOLFSSL_X509_STORE_CTX* store) +{ + char buffer[WOLFSSL_MAX_ERROR_SZ]; + MQTTCtx *mqttCtx = NULL; + char appName[PRINT_BUFFER_SIZE] = {0}; + + if (store->userCtx != NULL) { + /* The client.ctx was stored during MqttSocket_Connect. */ + mqttCtx = (MQTTCtx *)store->userCtx; + XSTRNCPY(appName, " for ", PRINT_BUFFER_SIZE-1); + XSTRNCAT(appName, mqttCtx->app_name, + PRINT_BUFFER_SIZE-XSTRLEN(appName)-1); + } + + PRINTF("MQTT TLS Verify Callback%s: PreVerify %d, Error %d (%s)", + appName, preverify, + store->error, store->error != 0 ? + wolfSSL_ERR_error_string(store->error, buffer) : "none"); + PRINTF(" Subject's domain name is %s", store->domain); + + if (store->error != 0) { + /* Allowing to continue */ + /* Should check certificate and return 0 if not okay */ + PRINTF(" Allowing cert anyways"); + } + + return 1; +} + +/* Use this callback to setup TLS certificates and verify callbacks */ +int mqtt_tls_cb(MqttClient* client) +{ + int rc = WOLFSSL_FAILURE; + SocketContext * sock = (SocketContext *)client->net->context; + + /* Use highest available and allow downgrade. If wolfSSL is built with + * old TLS support, it is possible for a server to force a downgrade to + * an insecure version. */ + client->tls.ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); + if (client->tls.ctx) { + wolfSSL_CTX_set_verify(client->tls.ctx, WOLFSSL_VERIFY_PEER, + mqtt_tls_verify_cb); + + /* default to success */ + rc = WOLFSSL_SUCCESS; + +#if !defined(NO_CERT) + #if !defined(NO_FILESYSTEM) + if (sock->mqttCtx->ca_file) { + /* Load CA certificate file */ + rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx, + sock->mqttCtx->ca_file, NULL); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading CA %s: %d (%s)", sock->mqttCtx->ca_file, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + } + if (sock->mqttCtx->mtls_certfile && sock->mqttCtx->mtls_keyfile) { + /* Load If using a mutual authentication */ + rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx, + sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading certificate %s: %d (%s)", + sock->mqttCtx->mtls_certfile, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + + #ifdef WOLFSSL_ENCRYPTED_KEYS + /* Setup password callback for pkcs8 key */ + wolfSSL_CTX_set_default_passwd_cb(client->tls.ctx, + mqtt_password_cb); + #endif + + rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx, + sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading key %s: %d (%s)", + sock->mqttCtx->mtls_keyfile, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + } + #else + /* Note: Zephyr example uses NO_FILESYSTEM */ + #ifdef WOLFSSL_ENCRYPTED_KEYS + /* Setup password callback for pkcs8 key */ + wolfSSL_CTX_set_default_passwd_cb(client->tls.ctx, + mqtt_password_cb); + #endif + /* Examples for loading buffer directly */ + /* Load CA certificate buffer */ + rc = wolfSSL_CTX_load_verify_buffer_ex(client->tls.ctx, + (const byte*)root_ca, (long)sizeof(root_ca), + WOLFSSL_FILETYPE_ASN1, 0, WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY); + + /* Load Client Cert */ + if (rc == WOLFSSL_SUCCESS) { + rc = wolfSSL_CTX_use_certificate_buffer(client->tls.ctx, + (const byte*)device_cert, (long)sizeof(device_cert), + WOLFSSL_FILETYPE_ASN1); + } + + /* Load Private Key */ + if (rc == WOLFSSL_SUCCESS) { + rc = wolfSSL_CTX_use_PrivateKey_buffer(client->tls.ctx, + (const byte*)device_priv_key, (long)sizeof(device_priv_key), + WOLFSSL_FILETYPE_ASN1); + } + #endif /* !NO_FILESYSTEM */ +#endif /* !NO_CERT */ +#ifdef HAVE_SNI + if ((rc == WOLFSSL_SUCCESS) && (mTlsSniHostName != NULL)) { + rc = wolfSSL_CTX_UseSNI(client->tls.ctx, WOLFSSL_SNI_HOST_NAME, + mTlsSniHostName, (word16) XSTRLEN(mTlsSniHostName)); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("UseSNI failed"); + } + } +#endif /* HAVE_SNI */ +#ifdef HAVE_PQC + if ((rc == WOLFSSL_SUCCESS) && (mTlsPQAlg != NULL)) { + int group = 0; + if (XSTRCMP(mTlsPQAlg, "ML_KEM_768") == 0) { + group = WOLFSSL_ML_KEM_768; + } else if (XSTRCMP(mTlsPQAlg, "SecP384r1MLKEM768") == 0) { + group = WOLFSSL_SECP384R1MLKEM768; + } else { + PRINTF("Invalid post-quantum KEM specified"); + } + + if (group != 0) { + client->tls.ssl = wolfSSL_new(client->tls.ctx); + if (client->tls.ssl == NULL) { + rc = WOLFSSL_FAILURE; + } + + if (rc == WOLFSSL_SUCCESS) { + rc = wolfSSL_UseKeyShare(client->tls.ssl, group); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Use key share failed"); + } + } + } + } +#endif /* HAVE_PQC */ + } + +#if defined(NO_CERT) || defined(NO_FILESYSTEM) + (void)sock; +#endif + + PRINTF("MQTT TLS Setup (%d)", rc); + + return rc; +} + +#ifdef WOLFMQTT_SN +int mqtt_dtls_cb(MqttClient* client) { +#ifdef WOLFSSL_DTLS + int rc = WOLFSSL_FAILURE; + SocketContext * sock = (SocketContext *)client->net->context; + + client->tls.ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method()); + if (client->tls.ctx) { + wolfSSL_CTX_set_verify(client->tls.ctx, WOLFSSL_VERIFY_PEER, + mqtt_tls_verify_cb); + + /* default to success */ + rc = WOLFSSL_SUCCESS; + +#if !defined(NO_CERT) && !defined(NO_FILESYSTEM) + if (sock->mqttCtx->ca_file) { + /* Load CA certificate file */ + rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx, + sock->mqttCtx->ca_file, NULL); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading CA %s: %d (%s)", sock->mqttCtx->ca_file, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + } + if (sock->mqttCtx->mtls_certfile && sock->mqttCtx->mtls_keyfile) { + /* Load If using a mutual authentication */ + rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx, + sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading certificate %s: %d (%s)", + sock->mqttCtx->mtls_certfile, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + + rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx, + sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading key %s: %d (%s)", + sock->mqttCtx->mtls_keyfile, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + } +#else + (void)sock; +#endif + + client->tls.ssl = wolfSSL_new(client->tls.ctx); + if (client->tls.ssl == NULL) { + rc = WOLFSSL_FAILURE; + return rc; + } + } + + PRINTF("MQTT DTLS Setup (%d)", rc); +#else /* WOLFSSL_DTLS */ + (void)client; + int rc = 0; + PRINTF("MQTT DTLS Setup - Must enable DTLS in wolfSSL!"); +#endif + return rc; +} +#endif /* WOLFMQTT_SN */ +#else +int mqtt_tls_cb(MqttClient* client) +{ + (void)client; + return 0; +} +#ifdef WOLFMQTT_SN +int mqtt_dtls_cb(MqttClient* client) +{ + (void)client; + return 0; +} +#endif +#endif /* ENABLE_MQTT_TLS */ + +int mqtt_file_load(const char* filePath, byte** fileBuf, int *fileLen) +{ +#if !defined(NO_FILESYSTEM) + int rc = 0; + XFILE file = NULL; + long int pos = -1L; + + /* Check arguments */ + if (filePath == NULL || XSTRLEN(filePath) == 0 || fileLen == NULL || + fileBuf == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* Open file */ + file = XFOPEN(filePath, "rb"); + if (file == NULL) { + PRINTF("File '%s' does not exist!", filePath); + rc = EXIT_FAILURE; + goto exit; + } + + /* Determine length of file */ + if (XFSEEK(file, 0, XSEEK_END) != 0) { + PRINTF("fseek() failed"); + rc = EXIT_FAILURE; + goto exit; + } + + pos = (int)XFTELL(file); + if (pos == -1L) { + PRINTF("ftell() failed"); + rc = EXIT_FAILURE; + goto exit; + } + + *fileLen = (int)pos; + if (XFSEEK(file, 0, XSEEK_SET) != 0) { + PRINTF("fseek() failed"); + rc = EXIT_FAILURE; + goto exit; + } +#ifdef DEBUG_WOLFMQTT + PRINTF("File %s is %d bytes", filePath, *fileLen); +#endif + + /* Allocate buffer for image */ + *fileBuf = (byte*)WOLFMQTT_MALLOC(*fileLen); + if (*fileBuf == NULL) { + PRINTF("File buffer malloc failed!"); + rc = MQTT_CODE_ERROR_MEMORY; + goto exit; + } + + /* Load file into buffer */ + rc = (int)XFREAD(*fileBuf, 1, *fileLen, file); + if (rc != *fileLen) { + PRINTF("Error reading file! %d", rc); + rc = EXIT_FAILURE; + goto exit; + } + rc = 0; /* Success */ + +exit: + if (file) { + XFCLOSE(file); + } + if (rc != 0) { + if (*fileBuf) { + WOLFMQTT_FREE(*fileBuf); + *fileBuf = NULL; + } + } + return rc; + +#else + (void)filePath; + (void)fileBuf; + (void)fileLen; + PRINTF("File system support is not configured."); + return EXIT_FAILURE; +#endif +} diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttexample.h b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttexample.h new file mode 100644 index 0000000..0f2e300 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttexample.h @@ -0,0 +1,240 @@ +/* mqttexample.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_EXAMPLE_H +#define WOLFMQTT_EXAMPLE_H + +#include "wolfmqtt/mqtt_client.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Compatibility Options */ +#ifdef NO_EXIT + #undef exit + #define exit(rc) return rc +#endif + +#ifndef MY_EX_USAGE +#define MY_EX_USAGE 2 /* Exit reason code */ +#endif + +/* STDIN / FGETS for examples */ +#ifndef WOLFMQTT_NO_STDIO + /* For Linux/Mac */ + #if !defined(FREERTOS) && !defined(USE_WINDOWS_API) && \ + !defined(FREESCALE_MQX) && !defined(FREESCALE_KSDK_MQX) && \ + !defined(MICROCHIP_MPLAB_HARMONY) && !defined(WOLFMQTT_ZEPHYR) + /* Make sure its not explicitly disabled and not already defined */ + #if !defined(WOLFMQTT_NO_STDIN_CAP) && \ + !defined(WOLFMQTT_ENABLE_STDIN_CAP) + /* Wake on stdin activity */ + #define WOLFMQTT_ENABLE_STDIN_CAP + #endif + #endif + + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + #ifndef XFGETS + #define XFGETS fgets + #endif + #ifndef STDIN + #define STDIN 0 + #endif + #endif +#endif /* !WOLFMQTT_NO_STDIO */ + + +/* Default Configurations */ + +#ifndef DEFAULT_MQTT_HOST + /* Default MQTT host broker to use, + * when none is specified in the examples */ + #define DEFAULT_MQTT_HOST "localhost" + /* "iot.eclipse.org" */ + /* "broker.emqx.io" */ + /* "broker.hivemq.com" */ +#endif + +#define DEFAULT_CMD_TIMEOUT_MS 30000 +#define DEFAULT_CON_TIMEOUT_MS 5000 +#define DEFAULT_CHK_TIMEOUT_S 2 +#define DEFAULT_MQTT_QOS MQTT_QOS_0 +#define DEFAULT_KEEP_ALIVE_SEC 60 +#define DEFAULT_CLIENT_ID "WolfMQTTClient" +#ifndef WOLFMQTT_TOPIC_NAME + #define WOLFMQTT_TOPIC_NAME "wolfMQTT/example/" + #define DEFAULT_TOPIC_NAME WOLFMQTT_TOPIC_NAME"testTopic" +#else + #define DEFAULT_TOPIC_NAME WOLFMQTT_TOPIC_NAME +#endif +#define DEFAULT_AUTH_METHOD "EXTERNAL" +#define PRINT_BUFFER_SIZE 80 +#define DEFAULT_MESSAGE "test" + +#ifdef WOLFMQTT_V5 +#define DEFAULT_MAX_PKT_SZ 1024*1024 /* The max MQTT control packet size + the client is willing to accept. */ +#define DEFAULT_SUB_ID 1 /* Sub ID starts at 1 */ +#define DEFAULT_SESS_EXP_INT 0xFFFFFFFF +#endif + +/* certs are either static or extern, depending on the specific example */ +#ifndef EXTERNAL_MQTT_TLS_CALLBACK +#ifdef WOLFMQTT_EXTERN_CERT + #undef WOLFMQTT_EXAMPLE_CERT + #define WOLFMQTT_EXAMPLE_CERT /* init extern from mqttexample.h */ + extern const char* root_ca; + extern const char* device_cert; + extern const char* device_priv_key; +#else + #undef WOLFMQTT_EXAMPLE_CERT + #define WOLFMQTT_EXAMPLE_CERT static +#endif +#endif /* !EXTERNAL_MQTT_TLS_CALLBACK */ +/* MQTT Client state */ +typedef enum _MQTTCtxState { + WMQ_BEGIN = 0, + WMQ_NET_INIT, + WMQ_INIT, + WMQ_TCP_CONN, + WMQ_MQTT_CONN, + WMQ_SUB, + WMQ_PUB, + WMQ_WAIT_MSG, + WMQ_PING, + WMQ_UNSUB, + WMQ_DISCONNECT, + WMQ_NET_DISCONNECT, + WMQ_DONE +} MQTTCtxState; + +/* MQTT Client context */ +/* This is used for the examples as reference */ +/* Use of this structure allow non-blocking context */ +typedef struct _MQTTCtx { + MQTTCtxState stat; + + void* app_ctx; /* For storing application specific data */ + + /* client and net containers */ + MqttClient client; + MqttNet net; + + /* temp mqtt containers */ + MqttConnect connect; + MqttMessage lwt_msg; + MqttSubscribe subscribe; + MqttUnsubscribe unsubscribe; + MqttTopic topics[1]; + MqttPublish publish; + MqttDisconnect disconnect; + MqttPing ping; +#ifdef WOLFMQTT_SN + SN_Publish publishSN; +#endif + + /* configuration */ + MqttQoS qos; + const char* app_name; + const char* host; + const char* username; + const char* password; + const char* topic_name; + const char* message; + const char* pub_file; + const char* client_id; +#if defined (ENABLE_MQTT_TLS) + const char* ca_file; + const char* mtls_keyfile; + const char* mtls_certfile; +#endif + byte *tx_buf, *rx_buf; + int return_code; + int use_tls; + int retain; + int enable_lwt; +#ifdef WOLFMQTT_V5 + int max_packet_size; +#endif + word32 cmd_timeout_ms; +#ifdef WOLFMQTT_NONBLOCK + word32 start_sec; /* used for timeout and keep-alive */ +#endif + word16 keep_alive_sec; + word16 port; +#ifdef WOLFMQTT_V5 + word16 topic_alias; + word16 topic_alias_max; /* Server property */ +#endif + byte clean_session; + byte test_mode; + byte debug_on:1; /* enable debug messages in example */ +#ifdef WOLFMQTT_V5 + byte subId_not_avail; /* Server property */ + byte enable_eauth; /* Enhanced authentication */ +#endif + unsigned int dynamicTopic:1; + unsigned int dynamicClientId:1; + unsigned int skip_subscribe:1; + const char* ready_file; /* touch file when ready (e.g., after SUBACK) */ +#ifdef WOLFMQTT_NONBLOCK + unsigned int useNonBlockMode:1; /* set to use non-blocking mode. + network callbacks can return MQTT_CODE_CONTINUE to indicate "would block" */ +#endif +#ifdef WOLFMQTT_WOLFIP + struct wolfIP *stack; /* wolfIP TCP/IP stack instance */ +#endif +} MQTTCtx; + + +void mqtt_show_usage(MQTTCtx* mqttCtx); +void mqtt_init_ctx(MQTTCtx* mqttCtx); +void mqtt_free_ctx(MQTTCtx* mqttCtx); +int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv); +int err_sys(const char* msg); + +int mqtt_tls_cb(MqttClient* client); + +#ifdef WOLFMQTT_SN +int mqtt_dtls_cb(MqttClient* client); +#endif + +word16 mqtt_get_packetid(void); + +#ifdef WOLFMQTT_NONBLOCK +int mqtt_check_timeout(int rc, word32* start_sec, word32 timeout_sec); +#endif + +int mqtt_fill_random_hexstr(char* buf, word32 bufLen); +char* mqtt_append_random(const char* inStr, word32 inLen); + +int mqtt_file_load(const char* filePath, byte** fileBuf, int *fileLen); + +#ifdef WOLFSSL_ENCRYPTED_KEYS +int mqtt_password_cb(char* passwd, int sz, int rw, void* userdata); +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_EXAMPLE_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttnet.c b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttnet.c new file mode 100644 index 0000000..84e57e2 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttnet.c @@ -0,0 +1,2028 @@ +/* mqttnet.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "mqttnet.h" + +#if 0 /* TODO: add multicast support */ +typedef struct MulticastCtx { + +} MulticastCtx; +#endif + +#ifndef WOLFMQTT_TEST_NONBLOCK_TIMES + #define WOLFMQTT_TEST_NONBLOCK_TIMES 1 +#endif + +/* Private functions */ + +/* -------------------------------------------------------------------------- */ +/* FREERTOS TCP NETWORK CALLBACK EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#ifdef FREERTOS_TCP + +#ifndef WOLFMQTT_NO_TIMEOUT + static SocketSet_t gxFDSet = NULL; +#endif +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + uint32_t hostIp = 0; + int rc = -1; + MQTTCtx* mqttCtx = sock->mqttCtx; + + switch (sock->stat) { + case SOCK_BEGIN: + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", + host, port, timeout_ms, mqttCtx->use_tls); + } + + hostIp = FreeRTOS_gethostbyname_a(host, NULL, 0, 0); + if (hostIp == 0) + break; + + sock->addr.sin_family = FREERTOS_AF_INET; + sock->addr.sin_port = FreeRTOS_htons(port); + sock->addr.sin_addr = hostIp; + + /* Create socket */ + sock->fd = FreeRTOS_socket(sock->addr.sin_family, FREERTOS_SOCK_STREAM, + FREERTOS_IPPROTO_TCP); + + if (sock->fd == FREERTOS_INVALID_SOCKET) + break; + +#ifndef WOLFMQTT_NO_TIMEOUT + /* Set timeouts for socket */ + timeout_ms = pdMS_TO_TICKS(timeout_ms); + FreeRTOS_setsockopt(sock->fd, 0, FREERTOS_SO_SNDTIMEO, + (void*)&timeout_ms, sizeof(timeout_ms)); + FreeRTOS_setsockopt(sock->fd, 0, FREERTOS_SO_RCVTIMEO, + (void*)&timeout_ms, sizeof(timeout_ms)); +#else + (void)timeout_ms; +#endif + sock->stat = SOCK_CONN; + + FALL_THROUGH; + case SOCK_CONN: + /* Start connect */ + rc = FreeRTOS_connect(sock->fd, (SOCK_ADDR_IN*)&sock->addr, + sizeof(sock->addr)); + break; + } + + return rc; +} + +static int NetRead(void *context, byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc = -1, timeout = 0; + word32 bytes = 0; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#ifndef WOLFMQTT_NO_TIMEOUT + /* Create the set of sockets that will be passed into FreeRTOS_select(). */ + if (gxFDSet == NULL) + gxFDSet = FreeRTOS_CreateSocketSet(); + if (gxFDSet == NULL) + return MQTT_CODE_ERROR_OUT_OF_BUFFER; + timeout_ms = pdMS_TO_TICKS(timeout_ms); /* convert ms to ticks */ +#else + (void)timeout_ms; +#endif + + /* Loop until buf_len has been read, error or timeout */ + while ((bytes < buf_len) && (timeout == 0)) { + +#ifndef WOLFMQTT_NO_TIMEOUT + /* set the socket to do used */ + FreeRTOS_FD_SET(sock->fd, gxFDSet, eSELECT_READ | eSELECT_EXCEPT); + + /* Wait for any event within the socket set. */ + rc = FreeRTOS_select(gxFDSet, timeout_ms); + if (rc != 0) { + if (FreeRTOS_FD_ISSET(sock->fd, gxFDSet)) +#endif + { + /* Try and read number of buf_len provided, + minus what's already been read */ + rc = (int)FreeRTOS_recv(sock->fd, &buf[bytes], + buf_len - bytes, 0); + + if (rc <= 0) { + break; /* Error */ + } + else { + /* Clamp return value: defensive check against + * platform API returning more than requested */ + if (rc > buf_len - (int)bytes) { + rc = buf_len - (int)bytes; + } + bytes += rc; /* Data */ + } + } +#ifndef WOLFMQTT_NO_TIMEOUT + } + else { + timeout = 1; + } +#endif + } + + if (rc == 0 || timeout) { + rc = MQTT_CODE_ERROR_TIMEOUT; + } + else if (rc < 0) { + #ifdef WOLFMQTT_NONBLOCK + if (rc == -pdFREERTOS_ERRNO_EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + #endif + PRINTF("NetRead: Error %d", rc); + rc = MQTT_CODE_ERROR_NETWORK; + } + else { + rc = bytes; + } + + return rc; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc = -1; + + (void)timeout_ms; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = (int)FreeRTOS_send(sock->fd, buf, buf_len, 0); + + if (rc < 0) { + #ifdef WOLFMQTT_NONBLOCK + if (rc == -pdFREERTOS_ERRNO_EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + #endif + PRINTF("NetWrite: Error %d", rc); + rc = MQTT_CODE_ERROR_NETWORK; + } + + return rc; +} + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + FreeRTOS_closesocket(sock->fd); + sock->stat = SOCK_BEGIN; + } + +#ifndef WOLFMQTT_NO_TIMEOUT + if (gxFDSet != NULL) { + FreeRTOS_DeleteSocketSet(gxFDSet); + gxFDSet = NULL; + } +#endif + + return 0; +} + + +/* -------------------------------------------------------------------------- */ +/* MICROCHIP HARMONY TCP NETWORK CALLBACK EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(MICROCHIP_MPLAB_HARMONY) + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + closesocket(sock->fd); + sock->fd = SOCKET_INVALID; + } + + sock->stat = SOCK_BEGIN; + } + return 0; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int type = SOCK_STREAM; + int rc = MQTT_CODE_ERROR_NETWORK; + struct addrinfo hints; + struct hostent *hostInfo; + MQTTCtx* mqttCtx = sock->mqttCtx; + + /* Get address information for host and locate IPv4 */ + switch(sock->stat) { + case SOCK_BEGIN: + { + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } + XMEMSET(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + XMEMSET(&sock->addr, 0, sizeof(sock->addr)); + sock->addr.sin_family = AF_INET; + + hostInfo = gethostbyname((char *)host); + if (hostInfo != NULL) { + sock->addr.sin_port = port; /* htons(port); */ + sock->addr.sin_family = AF_INET; + XMEMCPY(&sock->addr.sin_addr.S_un, + *(hostInfo->h_addr_list), sizeof(IPV4_ADDR)); + } + else { + return MQTT_CODE_CONTINUE; + } + + /* Create socket */ + sock->fd = SOCK_OPEN(sock->addr.sin_family, type, 0); + if (sock->fd == SOCKET_INVALID) + goto exit; + + sock->stat = SOCK_CONN; + } + FALL_THROUGH; + + case SOCK_CONN: + { + /* Start connect */ + rc = SOCK_CONNECT(sock->fd, (struct sockaddr*)&sock->addr, + sizeof(sock->addr)); + break; + } + + default: + rc = MQTT_CODE_ERROR_BAD_ARG; + break; + } /* switch */ + + (void)timeout_ms; + +exit: + + /* check for error */ + if (rc != 0) { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + NetDisconnect(context); + + /* Show error */ + PRINTF("NetConnect: Rc=%d, ErrNo=%d", rc, errno); + } + + return rc; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = (int)send(sock->fd, buf, (size_t)buf_len, 0); + if (rc <= 0) { + /* Check for in progress */ + if (errno == EINPROGRESS || errno == EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + + PRINTF("NetWrite Error: Rc %d, BufLen %d, ErrNo %d", rc, buf_len, errno); + rc = MQTT_CODE_ERROR_NETWORK; + } + + (void)timeout_ms; + + return rc; +} + +static int NetRead(void *context, byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc = MQTT_CODE_ERROR_NETWORK; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = (int)recv(sock->fd, + &buf[sock->bytes], + (size_t)(buf_len - sock->bytes), + 0); + if (rc < 0) { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + + PRINTF("NetRead Error: Rc %d, BufLen %d, ErrNo %d", rc, buf_len, errno); + rc = MQTT_CODE_ERROR_NETWORK; + } + else { + /* Clamp return value: defensive check against + * platform API returning more than requested */ + if (rc > buf_len - sock->bytes) { + rc = buf_len - sock->bytes; + } + sock->bytes += rc; + if (sock->bytes < buf_len) { + return MQTT_CODE_CONTINUE; + } + rc = sock->bytes; + sock->bytes = 0; + } + + (void)timeout_ms; + + return rc; +} + + +/* -------------------------------------------------------------------------- */ +/* NETX SOCKET BACKEND EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(HAVE_NETX) + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + nx_tcp_socket_disconnect(&sock->fd, NX_NO_WAIT); + nx_tcp_socket_delete(&sock->fd); + + sock->stat = SOCK_BEGIN; + } + return 0; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc = MQTT_CODE_ERROR_NETWORK; + MQTTCtx* mqttCtx = sock->mqttCtx; + UINT status; + NXD_ADDRESS ipAddress; + UINT ticks; + + /* Get address information for host and locate IPv4 */ + switch(sock->stat) { + case SOCK_BEGIN: + { + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } + #ifndef WOLFMQTT_NO_NETX_DNS + if (sock->dnsPtr == NULL) { + PRINTF("DNS pointer was NULL and needs set, sock->dnsPtr"); + return MQTT_CODE_ERROR_NETWORK; + } + + /* Convert hostname to IP address using NETX DUO DNS */ + status = nxd_dns_host_by_name_get(sock->dnsPtr, (UCHAR *)host, + &ipAddress, timeout_ms, NX_IP_VERSION_V4); + if (status != NX_SUCCESS) { + PRINTF("DNS lookup failed: %d", status); + return MQTT_CODE_ERROR_NETWORK; + } + #else + PRINTF("DNS lookup not available"); + return MQTT_CODE_ERROR_NETWORK; + #endif + status = nx_tcp_socket_create(sock->ipPtr, &sock->fd, + (CHAR*)"MQTT Socket", NX_IP_NORMAL, + NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, + 1024, NX_NULL, NX_NULL); + if (status != NX_SUCCESS) { + PRINTF("Socket create failed: %d", status); + return MQTT_CODE_ERROR_NETWORK; + } + + /* Bind the socket to a local port */ + status = nx_tcp_client_socket_bind(&sock->fd, port, timeout_ms); + if (status != NX_SUCCESS) { + PRINTF("Socket bind failed: %d", status); + return MQTT_CODE_ERROR_NETWORK; + } + + sock->stat = SOCK_CONN; + } + FALL_THROUGH; + + case SOCK_CONN: + { + /* Convert timeout_ms to ticks and round up */ + ticks = (timeout_ms * TX_TIMER_TICKS_PER_SECOND + 999) / 1000; + + /* Connect to server using NETX DUO */ + status = nxd_tcp_client_socket_connect(&sock->fd, &ipAddress, port, + ticks); + if (status != NX_SUCCESS) { + PRINTF("Socket connect failed: %d", status); + NetDisconnect(context); + return MQTT_CODE_ERROR_NETWORK; + } + return MQTT_CODE_SUCCESS; + } + + default: + rc = MQTT_CODE_ERROR_BAD_ARG; + break; + } /* switch */ + + return rc; +} + + +static int NetWrite(void *context, const byte* buf, int buf_len, int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + NX_PACKET* packet; + NX_PACKET_POOL* pool; /* shorthand */ + UINT status; + UINT ticks; + + if (sock == NULL) { + PRINTF("NetX Send NULL parameters"); + return MQTT_CODE_ERROR_BAD_ARG; + } + + pool = sock->fd.nx_tcp_socket_ip_ptr->nx_ip_default_packet_pool; + status = nx_packet_allocate(pool, &packet, NX_TCP_PACKET, timeout_ms); + if (status != NX_SUCCESS) { + PRINTF("NetX Send packet alloc error"); + return MQTT_CODE_ERROR_NETWORK; + } + + status = nx_packet_data_append(packet, (VOID*)buf, buf_len, pool, + timeout_ms); + if (status != NX_SUCCESS) { + nx_packet_release(packet); + PRINTF("NetX Send data append error"); + return MQTT_CODE_ERROR_NETWORK; + } + + + /* Convert timeout_ms to ticks and round up */ + ticks = (timeout_ms * TX_TIMER_TICKS_PER_SECOND + 999) / 1000; + + status = nx_tcp_socket_send(&sock->fd, packet, ticks); + if (status != NX_SUCCESS) { + nx_packet_release(packet); + PRINTF("NetX Send socket send error"); + return MQTT_CODE_ERROR_NETWORK; + } + + return buf_len; +} + + +static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + ULONG left; + ULONG total; + ULONG copied = 0; + UINT status; + UINT ticks; + + if (sock == NULL) { + PRINTF("NetX Recv NULL parameters"); + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->nxPacket == NULL) { + /* Convert timeout_ms to ticks and round up */ + ticks = (timeout_ms * TX_TIMER_TICKS_PER_SECOND + 999) / 1000; + status = nx_tcp_socket_receive(&sock->fd, &sock->nxPacket, ticks); + if (status != NX_SUCCESS) { + if (status == NX_NO_PACKET) { + PRINTF("NetX Recv timeout"); + return MQTT_CODE_ERROR_TIMEOUT; + } + PRINTF("NetX Recv receive error"); + return MQTT_CODE_ERROR_NETWORK; + } + } + + if (sock->nxPacket) { + status = nx_packet_length_get(sock->nxPacket, &total); + if (status != NX_SUCCESS) { + PRINTF("NetX Recv length get error"); + return MQTT_CODE_ERROR_NETWORK; + } + + left = total - sock->nxOffset; + status = nx_packet_data_extract_offset(sock->nxPacket, + sock->nxOffset, buf, buf_len, &copied); + if (status != NX_SUCCESS) { + PRINTF("NetX Recv data extract offset error"); + return MQTT_CODE_ERROR_NETWORK; + } + + sock->nxOffset += copied; + + if (copied == left) { + PRINTF("NetX Recv Drained packet"); + nx_packet_release(sock->nxPacket); + sock->nxPacket = NULL; + sock->nxOffset = 0; + } + } + + return copied; +} + + +/* -------------------------------------------------------------------------- */ +/* CURL EASY SOCKET BACKEND EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(ENABLE_MQTT_CURL) + +/* How many times to retry after a timeout. */ +#define MQTT_CURL_NUM_RETRY (3) + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) +/* Tells the calling function to either return early with + * MQTT_CODE_CONTINUE, or proceed with a smaller buffer read/write. + * Used for testing nonblocking. */ +static int +mqttcurl_test_nonblock_read(int* buf_len) +{ + static int testNbReadAlt = 0; + static int testSmallerRead = 0; + + if (testNbReadAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbReadAlt++; + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("mqttcurl_test_nonblock_read: returning early with CONTINUE"); + #endif + return MQTT_CODE_CONTINUE; + } + + testNbReadAlt = 0; + + if (!testSmallerRead) { + if (*buf_len > 2) { + *buf_len /= 2; + testSmallerRead = 1; + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("mqttcurl_test_nonblock_read: testing small buff: %d", + *buf_len); + #endif + } + } + else { + testSmallerRead = 0; + } + + return MQTT_CODE_SUCCESS; +} + +static int +mqttcurl_test_nonblock_write(int* buf_len) +{ + static int testNbWriteAlt = 0; + static int testSmallerWrite = 0; + + if (testNbWriteAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbWriteAlt++; + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("mqttcurl_test_nonblock_write: returning early with CONTINUE"); + #endif + return MQTT_CODE_CONTINUE; + } + + testNbWriteAlt = 0; + + if (!testSmallerWrite) { + if (*buf_len > 2) { + *buf_len /= 2; + testSmallerWrite = 1; + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("mqttcurl_test_nonblock_write: testing small buff: %d", + *buf_len); + #endif + } + } + else { + testSmallerWrite = 0; + } + + return MQTT_CODE_SUCCESS; +} + +#endif /* WOLFMQTT_NONBLOCK && WOLFMQTT_TEST_NONBLOCK */ + +static int +mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms, + int test_mode) +{ + struct timeval tv; + fd_set infd; + fd_set outfd; + fd_set errfd; + int rc = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (int)(timeout_ms % 1000) * 1000; + + FD_ZERO(&infd); + FD_ZERO(&outfd); + FD_ZERO(&errfd); + + FD_SET(sockfd, &errfd); + + if (for_recv) { + FD_SET(sockfd, &infd); + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + if (!test_mode) { + FD_SET(STDIN, &infd); + } + #endif /* WOLFMQTT_ENABLE_STDIN_CAP */ + } + else { + FD_SET(sockfd, &outfd); + } + + rc = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); + + if (rc > 0) { + if (for_recv && FD_ISSET(sockfd, &infd)) { + return MQTT_CODE_CONTINUE; + } + else if (!for_recv && FD_ISSET(sockfd, &outfd)) { + return MQTT_CODE_CONTINUE; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + else if (for_recv && !test_mode && FD_ISSET(STDIN, &infd)) { + return MQTT_CODE_STDIN_WAKE; + } + #endif /* WOLFMQTT_ENABLE_STDIN_CAP */ + else if (FD_ISSET(sockfd, &errfd)) { + return MQTT_CODE_ERROR_NETWORK; + } + } + else if (rc == 0) { + return MQTT_CODE_ERROR_TIMEOUT; + } + + #ifndef WOLFMQTT_ENABLE_STDIN_CAP + (void)test_mode; + #endif + + return MQTT_CODE_ERROR_NETWORK; +} + +static int +mqttcurl_connect(SocketContext* sock, const char* host, word16 port, + int timeout_ms) +{ + CURLcode res = CURLE_OK; + + if (sock == NULL || sock->curl == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#ifdef DEBUG_WOLFMQTT + res = curl_easy_setopt(sock->curl, CURLOPT_VERBOSE, 1L); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(VERBOSE, 1L) returned: %d, %s", + res, curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } +#endif + + if (timeout_ms != 0) { + res = curl_easy_setopt(sock->curl, CURLOPT_CONNECTTIMEOUT_MS, + (long)timeout_ms); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CONNECTTIMEOUT_MS, %d) " + "returned %d", timeout_ms, res); + return MQTT_CODE_ERROR_CURL; + } + /* Note: CURLOPT_TIMEOUT_MS is not used here because it sets a total + * transfer timeout, which is not applicable with CURLOPT_CONNECT_ONLY + * mode where we use curl_easy_send/recv manually after connect. */ + } + + res = curl_easy_setopt(sock->curl, CURLOPT_URL, host); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(URL, %s) returned: %d", + host, res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_setopt(sock->curl, CURLOPT_PORT, (long)port); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(PORT, %d) returned: %d", + port, res); + return MQTT_CODE_ERROR_CURL; + } + + #ifdef ENABLE_MQTT_TLS + if (sock->mqttCtx->use_tls) { + /* Set TLS specific options. */ + res = curl_easy_setopt(sock->curl, CURLOPT_SSLVERSION, + CURL_SSLVERSION_TLSv1_2); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSLVERSION) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* With CURLOPT_CONNECT_ONLY this means do TLS by default. */ + res = curl_easy_setopt(sock->curl, CURLOPT_DEFAULT_PROTOCOL, + "https"); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(DEFAULT_PROTOCOL) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Set path to Certificate Authority (CA) file bundle. */ + if (sock->mqttCtx->ca_file != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_CAINFO, + sock->mqttCtx->ca_file); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CAINFO) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to mutual TLS keyfile. */ + if (sock->mqttCtx->mtls_keyfile != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSLKEY, + sock->mqttCtx->mtls_keyfile); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_SSLKEY) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to mutual TLS certfile. */ + if (sock->mqttCtx->mtls_certfile != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSLCERT, + sock->mqttCtx->mtls_certfile); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_SSLCERT) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to dir holding CA files. + * Unused at the moment. */ + /* + if (sock->mqttCtx->ca_path != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_CAPATH, + sock->mqttCtx->ca_path); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CAPATH) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + */ + + /* Set peer and host verification. */ + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYPEER, 1L); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYPEER) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Only do server host verification when not running against + * localhost broker. */ + if (XSTRCMP(host, "localhost") == 0) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYHOST, 0L); + } + else { + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYHOST, 2L); + } + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYHOST) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + #endif /* ENABLE_MQTT_TLS */ + + #if 0 + /* Set proxy options. + * Unused at the moment. */ + if (sock->mqttCtx->use_proxy) { + /* Set the proxy hostname or ip address string. Append + * ":[port num]" to the string to specify a port. */ + res = curl_easy_setopt(sock->curl, CURLOPT_PROXY, + sock->mqttCtx->proxy_str); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_PROXY, %s) returned: %d", + res, sock->mqttCtx->proxy_str); + return MQTT_CODE_ERROR_CURL; + } + + /* Set the proxy type. E.g. CURLPROXY_HTTP, CURLPROXY_HTTPS, + * CURLPROXY_HTTPS2, etc. */ + res = curl_easy_setopt(sock->curl, CURLOPT_PROXYTYPE, + CURLPROXY_HTTP); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_PROXYTYPE) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + #endif + + res = curl_easy_setopt(sock->curl, CURLOPT_CONNECT_ONLY, 1L); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CONNECT_ONLY, 1) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Finally do the connection. */ + res = curl_easy_perform(sock->curl); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_perform returned: %d, %s", res, + curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + return MQTT_CODE_SUCCESS; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext * sock = (SocketContext*)context; + int rc = 0; + + if (context == NULL || host == NULL || *host == '\0') { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->mqttCtx == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", + host, port, timeout_ms, sock->mqttCtx->use_tls); +#endif + + sock->curl = curl_easy_init(); + + if (sock->curl == NULL) { + PRINTF("error: curl_easy_init returned NULL"); + return MQTT_CODE_ERROR_MEMORY; + } + + rc = mqttcurl_connect(sock, host, port, timeout_ms); + + if (rc != MQTT_CODE_SUCCESS) { + curl_easy_cleanup(sock->curl); + sock->curl = NULL; + return rc; + } + + sock->bytes = 0; + sock->stat = SOCK_CONN; + return MQTT_CODE_SUCCESS; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = CURLE_OK; + SocketContext * sock = (SocketContext*)context; + size_t sent = 0; + curl_socket_t sockfd = 0; + int wait_rc = 0; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (sock->mqttCtx->useNonBlockMode) { + if (mqttcurl_test_nonblock_write(&buf_len)) { + return MQTT_CODE_CONTINUE; + } + } +#endif /* WOLFMQTT_NONBLOCK && WOLFMQTT_TEST_NONBLOCK */ + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(sock->curl, CURLINFO_ACTIVESOCKET, &sockfd); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* check it makes sense */ + if (sockfd <= 0) { + PRINTF("error: libcurl sockfd: %d", sockfd); + return MQTT_CODE_ERROR_CURL; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("sock->curl = %p, sockfd = %d", (void *)sock->curl, sockfd); +#endif + + /* Loop until all data is sent or error */ + do { + #ifdef WOLFMQTT_MULTITHREAD + { + int rc = wm_SemLock(&sock->mqttCtx->client.lockCURL); + if (rc != 0) { + return rc; + } + } + #endif + + res = curl_easy_send(sock->curl, &buf[sock->bytes], + buf_len - sock->bytes, &sent); + + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&sock->mqttCtx->client.lockCURL); + #endif + + if (res == CURLE_OK) { + /* Clamp return value: defensive check against + * library API returning more than requested */ + if ((int)sent > buf_len - sock->bytes) { + sent = (size_t)(buf_len - sock->bytes); + } + sock->bytes += (int)sent; + if (sock->bytes == buf_len) { + /* Complete, reset for next operation */ + sent = sock->bytes; + sock->bytes = 0; + return (int)sent; + } + /* Partial write, continue loop */ + } + else if (res == CURLE_AGAIN) { + wait_rc = mqttcurl_wait(sockfd, 0, timeout_ms, + sock->mqttCtx->test_mode); + if (wait_rc != MQTT_CODE_CONTINUE) { + return wait_rc; + } + } + else { + PRINTF("error: curl_easy_send(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); + sock->bytes = 0; + return MQTT_CODE_ERROR_CURL; + } + } while (1); +} + +static int NetRead(void *context, byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = CURLE_OK; + SocketContext * sock = (SocketContext*)context; + size_t recvd = 0; + curl_socket_t sockfd = 0; + int wait_rc = 0; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (sock->mqttCtx->useNonBlockMode) { + if (mqttcurl_test_nonblock_read(&buf_len)) { + return MQTT_CODE_CONTINUE; + } + } +#endif /* WOLFMQTT_NONBLOCK && WOLFMQTT_TEST_NONBLOCK */ + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(sock->curl, CURLINFO_ACTIVESOCKET, &sockfd); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* check it makes sense */ + if (sockfd <= 0) { + PRINTF("error: libcurl sockfd: %d", sockfd); + return MQTT_CODE_ERROR_CURL; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("sock->curl = %p, sockfd = %d", (void *)sock->curl, sockfd); +#endif + + /* Loop until all data is received or error */ + do { + #ifdef WOLFMQTT_MULTITHREAD + { + int rc = wm_SemLock(&sock->mqttCtx->client.lockCURL); + if (rc != 0) { + return rc; + } + } + #endif + + res = curl_easy_recv(sock->curl, &buf[sock->bytes], + buf_len - sock->bytes, &recvd); + + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&sock->mqttCtx->client.lockCURL); + #endif + + if (res == CURLE_OK) { + if (recvd == 0) { + /* Connection closed */ + PRINTF("error: connection closed by peer"); + sock->bytes = 0; + return MQTT_CODE_ERROR_NETWORK; + } + sock->bytes += (int)recvd; + if (sock->bytes == buf_len) { + /* Complete, reset for next operation */ + recvd = sock->bytes; + sock->bytes = 0; + return (int)recvd; + } + /* Partial read, continue loop */ + } + else if (res == CURLE_AGAIN) { + wait_rc = mqttcurl_wait(sockfd, 1, timeout_ms, + sock->mqttCtx->test_mode); + if (wait_rc != MQTT_CODE_CONTINUE) { + return wait_rc; + } + } + else { + PRINTF("error: curl_easy_recv(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); + sock->bytes = 0; + return MQTT_CODE_ERROR_CURL; + } + } while (1); +} + +static int NetDisconnect(void *context) +{ + SocketContext * sock = (SocketContext*)context; + + if (sock == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->curl != NULL) { +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: curl_easy_cleanup"); +#endif + curl_easy_cleanup(sock->curl); + sock->curl = NULL; + } + sock->bytes = 0; + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* WOLFIP TCP/IP STACK NETWORK CALLBACK EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(WOLFMQTT_WOLFIP) + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + wolfIP_sock_close(sock->stack, sock->fd); + sock->fd = SOCKET_INVALID; + } + sock->stat = SOCK_BEGIN; + } + return 0; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + struct wolfIP_sockaddr_in addr; + int rc; + + (void)timeout_ms; + + if (context == NULL || host == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + switch (sock->stat) { + case SOCK_BEGIN: + { + /* Create TCP socket */ + sock->fd = wolfIP_sock_socket(sock->stack, AF_INET, + IPSTACK_SOCK_STREAM, 0); + if (sock->fd < 0) { + return MQTT_CODE_ERROR_NETWORK; + } + + sock->stat = SOCK_CONN; + } + FALL_THROUGH; + + case SOCK_CONN: + { + /* Set up address and initiate connect. + * Note: atoip4() only supports dotted-quad IPv4 strings + * (e.g., "192.168.1.1") and does not resolve DNS hostnames. */ + XMEMSET(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(port); + addr.sin_addr.s_addr = atoip4(host); + if (addr.sin_addr.s_addr == 0) { + NetDisconnect(context); + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = wolfIP_sock_connect(sock->stack, sock->fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + if (rc == 0) { + return MQTT_CODE_SUCCESS; + } + if (rc == -WOLFIP_EAGAIN) { + return MQTT_CODE_CONTINUE; + } + + /* Connection failed */ + NetDisconnect(context); + return MQTT_CODE_ERROR_NETWORK; + } + + default: + break; + } + + return MQTT_CODE_ERROR_NETWORK; +} + +static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc; + + (void)timeout_ms; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = wolfIP_sock_recv(sock->stack, sock->fd, buf, buf_len, 0); + /* -WOLFIP_EAGAIN: no data yet; -1: socket not yet in ESTABLISHED state */ + if (rc == -WOLFIP_EAGAIN || rc == -1) { + return MQTT_CODE_CONTINUE; + } + if (rc <= 0) { + return MQTT_CODE_ERROR_NETWORK; + } + return rc; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc; + + (void)timeout_ms; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = wolfIP_sock_send(sock->stack, sock->fd, buf, buf_len, 0); + /* -WOLFIP_EAGAIN: send buffer full; -1: socket not yet in ESTABLISHED state */ + if (rc == -WOLFIP_EAGAIN || rc == -1) { + return MQTT_CODE_CONTINUE; + } + if (rc <= 0) { + return MQTT_CODE_ERROR_NETWORK; + } + return rc; +} + + +/* -------------------------------------------------------------------------- */ +/* GENERIC BSD SOCKET TCP NETWORK CALLBACK EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#else + +#ifndef WOLFMQTT_NO_TIMEOUT +static void tcp_setup_timeout(struct timeval* tv, int timeout_ms) +{ + tv->tv_sec = timeout_ms / 1000; + tv->tv_usec = (timeout_ms % 1000) * 1000; + + /* Make sure there is a minimum value specified */ + if (tv->tv_sec < 0 || (tv->tv_sec == 0 && tv->tv_usec <= 0)) { + tv->tv_sec = 0; + tv->tv_usec = 100; + } +} + +static void tcp_set_fds(SocketContext* sock, fd_set* recvfds, fd_set* errfds) +{ + /* Setup select file descriptors to watch */ + FD_ZERO(errfds); + FD_SET(sock->fd, errfds); + FD_ZERO(recvfds); + FD_SET(sock->fd, recvfds); +#ifdef WOLFMQTT_ENABLE_STDIN_CAP + #ifdef WOLFMQTT_MULTITHREAD + FD_SET(sock->pfd[0], recvfds); + #endif + if (!sock->mqttCtx->test_mode) { + FD_SET(STDIN, recvfds); + } +#endif /* WOLFMQTT_ENABLE_STDIN_CAP */ +} + +#ifdef WOLFMQTT_NONBLOCK +static void tcp_set_nonblocking(SOCKET_T* sockfd) +{ +#ifdef USE_WINDOWS_API + unsigned long blocking = 1; + int ret = ioctlsocket(*sockfd, FIONBIO, &blocking); + if (ret == SOCKET_ERROR) + PRINTF("ioctlsocket failed!"); +#else + int flags = fcntl(*sockfd, F_GETFL, 0); + if (flags < 0) + PRINTF("fcntl get failed!"); + flags = fcntl(*sockfd, F_SETFL, flags | O_NONBLOCK); + if (flags < 0) + PRINTF("fcntl set failed!"); +#endif +} +#endif /* WOLFMQTT_NONBLOCK */ +#endif /* !WOLFMQTT_NO_TIMEOUT */ + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + SOCK_CLOSE(sock->fd); + sock->fd = SOCKET_INVALID; + } + + sock->stat = SOCK_BEGIN; + } + return 0; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int type = SOCK_STREAM; + int rc = -1; + SOERROR_T so_error = 0; + struct addrinfo *result = NULL; + struct addrinfo hints; + MQTTCtx* mqttCtx = sock->mqttCtx; + + /* Get address information for host and locate IPv4 */ + switch(sock->stat) { + case SOCK_BEGIN: + { + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } + + XMEMSET(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + XMEMSET(&sock->addr, 0, sizeof(sock->addr)); + sock->addr.sin_family = AF_INET; + + rc = getaddrinfo(host, NULL, &hints, &result); + if (rc == 0) { + struct addrinfo* result_i = result; + + if (! result) { + rc = -1; + goto exit; + } + + /* prefer ip4 addresses */ + while (result_i) { + if (result_i->ai_family == AF_INET) + break; + result_i = result_i->ai_next; + } + + if (result_i) { + sock->addr.sin_port = htons(port); + sock->addr.sin_family = AF_INET; + sock->addr.sin_addr = + ((SOCK_ADDR_IN*)(result_i->ai_addr))->sin_addr; + } + else { + rc = -1; + } + + freeaddrinfo(result); + } + if (rc != 0) + goto exit; + + /* Default to error */ + rc = -1; + + /* Create socket */ + sock->fd = SOCK_OPEN(sock->addr.sin_family, type, 0); + if (sock->fd == SOCKET_INVALID) + goto exit; + + sock->stat = SOCK_CONN; + } + FALL_THROUGH; + + case SOCK_CONN: + { + #ifndef WOLFMQTT_NO_TIMEOUT + fd_set fdset; + struct timeval tv; + + /* Setup timeout and FD's */ + tcp_setup_timeout(&tv, timeout_ms); + FD_ZERO(&fdset); + FD_SET(sock->fd, &fdset); + #endif /* !WOLFMQTT_NO_TIMEOUT */ + + #if !defined(WOLFMQTT_NO_TIMEOUT) && defined(WOLFMQTT_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + /* Set socket as non-blocking */ + tcp_set_nonblocking(&sock->fd); + } + #endif + + /* Start connect */ + rc = SOCK_CONNECT(sock->fd, (struct sockaddr*)&sock->addr, + sizeof(sock->addr)); + if (rc < 0) { + /* set default error case */ + rc = MQTT_CODE_ERROR_NETWORK; + #ifdef WOLFMQTT_NONBLOCK + { + /* Check for error */ + GET_SOCK_ERROR(sock->fd, SOL_SOCKET, SO_ERROR, so_error); + } + if ( + #ifndef _WIN32 + (errno == EINPROGRESS) || + #endif + SOCK_EQ_ERROR(so_error)) + { + #ifndef WOLFMQTT_NO_TIMEOUT + /* Wait for connect */ + if (select((int)SELECT_FD(sock->fd), NULL, &fdset, + NULL, &tv) > 0) { + rc = MQTT_CODE_SUCCESS; + } + #else + rc = MQTT_CODE_CONTINUE; + #endif + } + #endif + } + break; + } + + default: + rc = -1; + } /* switch */ + + (void)timeout_ms; + +exit: + if ((rc != 0) && (rc != MQTT_CODE_CONTINUE)) { + NetDisconnect(context); + PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); /* Show error */ + } + + return rc; +} + +#ifdef WOLFMQTT_SN +static int SN_NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int type = SOCK_DGRAM; + int rc; + SOERROR_T so_error = 0; + struct addrinfo *result = NULL; + struct addrinfo hints; + MQTTCtx* mqttCtx = sock->mqttCtx; + + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use DTLS %d", + host, port, timeout_ms, mqttCtx->use_tls); + + /* Get address information for host and locate IPv4 */ + XMEMSET(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + + XMEMSET(&sock->addr, 0, sizeof(sock->addr)); + sock->addr.sin_family = AF_INET; + + rc = getaddrinfo(host, NULL, &hints, &result); + if (rc == 0) { + struct addrinfo* result_i = result; + + if (! result) { + rc = -1; + goto exit; + } + + /* prefer ip4 addresses */ + while (result_i) { + if (result_i->ai_family == AF_INET) + break; + result_i = result_i->ai_next; + } + + if (result_i) { + sock->addr.sin_port = htons(port); + sock->addr.sin_family = AF_INET; + sock->addr.sin_addr = + ((SOCK_ADDR_IN*)(result_i->ai_addr))->sin_addr; + } + else { + rc = -1; + } + + freeaddrinfo(result); + } + if (rc != 0) + goto exit; + + if (rc == 0) { + /* Create the socket */ + sock->fd = SOCK_OPEN(sock->addr.sin_family, type, 0); + if (sock->fd == SOCKET_INVALID) { + rc = -1; + } + } + + if (rc == 0) + { + #ifndef WOLFMQTT_NO_TIMEOUT + fd_set fdset; + struct timeval tv; + + /* Setup timeout and FD's */ + tcp_setup_timeout(&tv, timeout_ms); + FD_ZERO(&fdset); + FD_SET(sock->fd, &fdset); + #else + (void)timeout_ms; + #endif /* !WOLFMQTT_NO_TIMEOUT */ + + /* Start connect */ + rc = SOCK_CONNECT(sock->fd, (struct sockaddr*)&sock->addr, + sizeof(sock->addr)); + } + + exit: + /* Show error */ + if ((rc != 0) && (rc != MQTT_CODE_CONTINUE)) { + NetDisconnect(context); + PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); + } + + return rc; +} +#endif + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + MQTTCtx* mqttCtx; + int rc; + SOERROR_T so_error = 0; +#ifndef WOLFMQTT_NO_TIMEOUT + struct timeval tv; +#endif +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbWriteAlt = 0; + static int testSmallerWrite = 0; +#endif + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->fd == SOCKET_INVALID) + return MQTT_CODE_ERROR_BAD_ARG; + + mqttCtx = sock->mqttCtx; + (void)mqttCtx; + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + if (testNbWriteAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbWriteAlt++; + return MQTT_CODE_CONTINUE; + } + testNbWriteAlt = 0; + if (!testSmallerWrite) { + if (buf_len > 2) { + buf_len /= 2; + } + testSmallerWrite = 1; + } + else { + testSmallerWrite = 0; + } + } +#endif + +#ifndef WOLFMQTT_NO_TIMEOUT + /* Setup timeout */ + tcp_setup_timeout(&tv, timeout_ms); + (void)setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, + sizeof(tv)); +#endif + + rc = (int)SOCK_SEND(sock->fd, buf, buf_len, 0); + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: SOCK_SEND(%d) returned %d, buf_len is %d", + buf_len, rc, buf_len); + #endif + if (rc == -1) { + { + /* Get error */ + GET_SOCK_ERROR(sock->fd, SOL_SOCKET, SO_ERROR, so_error); + } + if (so_error == 0) { + #if defined(USE_WINDOWS_API) && defined(WOLFMQTT_NONBLOCK) + /* assume non-blocking case */ + rc = MQTT_CODE_CONTINUE; + #else + rc = 0; /* Handle signal */ + #endif + } + else { + #ifdef WOLFMQTT_NONBLOCK + if (SOCK_EQ_ERROR(so_error)) { + return MQTT_CODE_CONTINUE; + } + #endif + rc = MQTT_CODE_ERROR_NETWORK; + PRINTF("NetWrite: Error %d", so_error); + } + } + + (void)timeout_ms; + + return rc; +} + +static int NetRead_ex(void *context, byte* buf, int buf_len, + int timeout_ms, byte peek) +{ + SocketContext *sock = (SocketContext*)context; + MQTTCtx* mqttCtx; + int rc = -1, timeout = 0; + SOERROR_T so_error = 0; + int bytes = 0; + int flags = 0; +#ifndef WOLFMQTT_NO_TIMEOUT + fd_set recvfds; + fd_set errfds; + struct timeval tv; +#else + (void)timeout_ms; +#endif +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbReadAlt = 0; + static int testSmallerRead = 0; +#endif + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->fd == SOCKET_INVALID) + return MQTT_CODE_ERROR_BAD_ARG; + + if (peek == 1) { + flags |= MSG_PEEK; + } + + mqttCtx = sock->mqttCtx; + (void)mqttCtx; + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + if (testNbReadAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbReadAlt++; + return MQTT_CODE_CONTINUE; + } + testNbReadAlt = 0; + if (!testSmallerRead) { + if (buf_len > 2) { + buf_len /= 2; + } + testSmallerRead = 1; + } + else { + testSmallerRead = 0; + } + } +#endif + + /* Loop until buf_len has been read, error or timeout */ + while (bytes < buf_len) { + int do_read = 0; + + #ifndef WOLFMQTT_NO_TIMEOUT + #ifdef WOLFMQTT_NONBLOCK + if (mqttCtx->useNonBlockMode) { + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + /* quick no timeout check if data is available on stdin */ + tcp_setup_timeout(&tv, 0); + + /* Setup select file descriptors to watch */ + tcp_set_fds(sock, &recvfds, &errfds); + + rc = select((int)SELECT_FD(sock->fd), &recvfds, NULL, &errfds, &tv); + if (rc > 0) { + if (FD_ISSET(sock->fd, &recvfds)) { + do_read = 1; + } + else if ((!mqttCtx->test_mode && FD_ISSET(STDIN, &recvfds))) { + return MQTT_CODE_STDIN_WAKE; + } + } + #else + do_read = 1; + #endif + } + else + #endif /* WOLFMQTT_NONBLOCK */ + { + /* Wait for rx data to be available */ + tcp_setup_timeout(&tv, timeout_ms); + + /* Setup select file descriptors to watch */ + tcp_set_fds(sock, &recvfds, &errfds); + + rc = select((int)SELECT_FD(sock->fd), &recvfds, NULL, &errfds, &tv); + if (rc > 0) { + if (FD_ISSET(sock->fd, &recvfds)) { + do_read = 1; + } + /* Check if rx or error */ + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + else if ((!mqttCtx->test_mode && FD_ISSET(STDIN, &recvfds)) + #ifdef WOLFMQTT_MULTITHREAD + || FD_ISSET(sock->pfd[0], &recvfds) + #endif + ) { + return MQTT_CODE_STDIN_WAKE; + } + #endif + if (FD_ISSET(sock->fd, &errfds)) { + rc = -1; + break; + } + } + else { + timeout = 1; + break; /* timeout or signal */ + } + } + #else + do_read = 1; + #endif /* !WOLFMQTT_NO_TIMEOUT */ + + if (do_read) { + /* Try and read number of buf_len provided, + * minus what's already been read */ + rc = (int)SOCK_RECV(sock->fd, + &buf[bytes], + buf_len - bytes, + flags); + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: SOCK_RECV(%d) returned %d, buf_len - bytes is %d", + bytes, rc, buf_len - bytes); + #endif + if (rc <= 0) { + rc = -1; + goto exit; /* Error */ + } + else { + /* Clamp return value: defensive check against + * platform API returning more than requested */ + if (rc > buf_len - bytes) { + rc = buf_len - bytes; + } + bytes += rc; /* Data */ + #ifdef ENABLE_MQTT_TLS + if (MqttClient_Flags(&mqttCtx->client, 0, 0) + & MQTT_CLIENT_FLAG_IS_TLS) { + break; + } + #endif + } + } + + /* no timeout and non-block should always exit loop */ + #ifdef WOLFMQTT_NONBLOCK + if (mqttCtx->useNonBlockMode) { + break; + } + #endif + #ifdef WOLFMQTT_NO_TIMEOUT + break; + #endif + } /* while */ + +exit: + + if (rc == 0 && timeout) { + rc = MQTT_CODE_ERROR_TIMEOUT; + } + else if (rc < 0) { + { + /* Get error */ + GET_SOCK_ERROR(sock->fd, SOL_SOCKET, SO_ERROR, so_error); + } + if (so_error == 0) { + rc = 0; /* Handle signal */ + } + else { + #ifdef WOLFMQTT_NONBLOCK + if (SOCK_EQ_ERROR(so_error)) { + return MQTT_CODE_CONTINUE; + } + #endif + rc = MQTT_CODE_ERROR_NETWORK; + PRINTF("NetRead: Error %d", so_error); + } + } + else { + rc = bytes; + } + + return rc; +} + +static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms) +{ + return NetRead_ex(context, buf, buf_len, timeout_ms, 0); +} + +#ifdef WOLFMQTT_SN +static int NetPeek(void *context, byte* buf, int buf_len, int timeout_ms) +{ + return NetRead_ex(context, buf, buf_len, timeout_ms, 1); +} +#endif + +#endif + + +/* Public Functions */ +int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx) +{ +#if defined(USE_WINDOWS_API) && !defined(FREERTOS_TCP) + WSADATA wsd; + WSAStartup(0x0002, &wsd); +#endif + +#ifdef MICROCHIP_MPLAB_HARMONY + static IPV4_ADDR dwLastIP[2] = { {-1}, {-1} }; + IPV4_ADDR ipAddr; + int Dummy; + int nNets; + int i; + SYS_STATUS stat; + TCPIP_NET_HANDLE netH; + + stat = TCPIP_STACK_Status(sysObj.tcpip); + if (stat < 0) { + return MQTT_CODE_CONTINUE; + } + + nNets = TCPIP_STACK_NumberOfNetworksGet(); + for (i = 0; i < nNets; i++) { + netH = TCPIP_STACK_IndexToNet(i); + ipAddr.Val = TCPIP_STACK_NetAddress(netH); + if (ipAddr.v[0] == 0) { + return MQTT_CODE_CONTINUE; + } + if (dwLastIP[i].Val != ipAddr.Val) { + dwLastIP[i].Val = ipAddr.Val; + PRINTF("%s", TCPIP_STACK_NetNameGet(netH)); + PRINTF(" IP Address: %d.%d.%d.%d", + ipAddr.v[0], ipAddr.v[1], ipAddr.v[2], ipAddr.v[3]); + } + } +#endif /* MICROCHIP_MPLAB_HARMONY */ + + if (net) { + SocketContext* sockCtx; + + XMEMSET(net, 0, sizeof(MqttNet)); + net->connect = NetConnect; + net->read = NetRead; + net->write = NetWrite; + net->disconnect = NetDisconnect; + + sockCtx = (SocketContext*)WOLFMQTT_MALLOC(sizeof(SocketContext)); + if (sockCtx == NULL) { + return MQTT_CODE_ERROR_MEMORY; + } + net->context = sockCtx; + XMEMSET(sockCtx, 0, sizeof(SocketContext)); +#if defined(ENABLE_MQTT_CURL) + sockCtx->curl = NULL; +#endif +#if !defined(HAVE_NETX) + sockCtx->fd = SOCKET_INVALID; +#endif + sockCtx->stat = SOCK_BEGIN; + sockCtx->mqttCtx = mqttCtx; + #ifdef WOLFMQTT_WOLFIP + if (mqttCtx != NULL) { + sockCtx->stack = mqttCtx->stack; + } + #endif + + #if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) + /* setup the pipe for waking select() */ + if (pipe(sockCtx->pfd) != 0) { + PRINTF("Failed to set up pipe for stdin"); + return -1; + } + #endif + } + + return MQTT_CODE_SUCCESS; +} + +#ifdef WOLFMQTT_SN +int SN_ClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx) +{ + if (net) { + SocketContext* sockCtx; + + XMEMSET(net, 0, sizeof(MqttNet)); + net->connect = SN_NetConnect; + net->read = NetRead; + net->write = NetWrite; + net->peek = NetPeek; + net->disconnect = NetDisconnect; + + sockCtx = (SocketContext*)WOLFMQTT_MALLOC(sizeof(SocketContext)); + if (sockCtx == NULL) { + return MQTT_CODE_ERROR_MEMORY; + } + net->context = sockCtx; + XMEMSET(sockCtx, 0, sizeof(SocketContext)); + sockCtx->stat = SOCK_BEGIN; + sockCtx->mqttCtx = mqttCtx; + + #if 0 /* TODO: add multicast support */ + MulticastCtx* multi_ctx; + multi_ctx = (MulticastCtx*)WOLFMQTT_MALLOC(sizeof(MulticastCtx)); + if (multi_ctx == NULL) { + return MQTT_CODE_ERROR_MEMORY; + } + net->multi_ctx = multi_ctx; + XMEMSET(multi_ctx, 0, sizeof(MulticastCtx)); + multi_ctx->stat = SOCK_BEGIN; + #endif + + #if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) + /* setup the pipe for waking select() */ + if (pipe(sockCtx->pfd) != 0) { + PRINTF("Failed to set up pipe for stdin"); + return -1; + } + #endif + } + + return MQTT_CODE_SUCCESS; +} +#endif + +int MqttClientNet_DeInit(MqttNet* net) +{ + if (net) { + if (net->context) { + WOLFMQTT_FREE(net->context); + } + XMEMSET(net, 0, sizeof(MqttNet)); + } + return 0; +} + +int MqttClientNet_Wake(MqttNet* net) +{ +#if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) + if (net) { + SocketContext* sockCtx = (SocketContext*)net->context; + if (sockCtx) { + /* wake the select() */ + if (write(sockCtx->pfd[1], "\n", 1) < 0) { + PRINTF("Failed to wake select"); + return -1; + } + } + } +#else + (void)net; +#endif + return 0; +} diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttnet.h b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttnet.h new file mode 100644 index 0000000..ea33342 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttnet.h @@ -0,0 +1,93 @@ +/* mqttnet.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_NET_H +#define WOLFMQTT_NET_H + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef ENABLE_MQTT_CURL + #include +#endif + +#include "mqttexample.h" +#include "mqttport.h" + +#if defined(HAVE_NETX) && !defined(WOLFMQTT_NO_NETX_DNS) + /* include the NetX DNS addon header */ + #include "nxd_dns.h" +#endif + + +/* Local context for Net callbacks */ +typedef enum { + SOCK_BEGIN = 0, + SOCK_CONN +} NB_Stat; + +typedef struct _SocketContext { + SOCKET_T fd; + NB_Stat stat; + SOCK_ADDR_IN addr; +#ifdef MICROCHIP_MPLAB_HARMONY + word32 bytes; +#endif +#if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) + /* "self pipe" -> signal wake sleep() */ + SOCKET_T pfd[2]; +#endif +#ifdef ENABLE_MQTT_CURL + CURL * curl; + int bytes; /* track partial read/write */ +#endif +#ifdef ENABLE_MQTT_WEBSOCKET + void* websocket_ctx; +#endif +#ifdef HAVE_NETX +#ifndef WOLFMQTT_NO_NETX_DNS + NX_DNS *dnsPtr; +#endif + NX_IP *ipPtr; + NX_PACKET *nxPacket; + ULONG nxOffset; +#endif +#ifdef WOLFMQTT_WOLFIP + struct wolfIP *stack; +#endif + MQTTCtx* mqttCtx; +} SocketContext; + +/* Functions used to handle the MqttNet structure creation / destruction */ +int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx); +int MqttClientNet_DeInit(MqttNet* net); +#ifdef WOLFMQTT_SN +int SN_ClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx); +#endif + +int MqttClientNet_Wake(MqttNet* net); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_NET_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttport.c b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttport.c new file mode 100644 index 0000000..b64eb2b --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttport.c @@ -0,0 +1,105 @@ +/* mqttport.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_client.h" +#include "mqttnet.h" +#include "mqttexample.h" +#include "mqttport.h" + +#ifdef WOLFMQTT_ZEPHYR + +#ifndef NO_FILESYSTEM +#ifndef WOLFSSL_ZEPHYR +XFILE z_fs_open(const char* filename, const char* mode) +{ + XFILE file; + fs_mode_t flags = 0; + + if (mode == NULL) + return NULL; + + /* Parse mode */ + switch (*mode++) { + case 'r': + flags |= FS_O_READ; + break; + case 'w': + flags |= FS_O_WRITE|FS_O_CREATE; + break; + case 'a': + flags |= FS_O_APPEND|FS_O_CREATE; + break; + default: + return NULL; + } + + /* Ignore binary flag */ + if (*mode == 'b') + mode++; + if (*mode == '+') { + flags |= FS_O_READ; + /* Don't add write flag if already appending */ + if (!(flags & FS_O_APPEND)) + flags |= FS_O_RDWR; + } + /* Ignore binary flag */ + if (*mode == 'b') + mode++; + /* Incorrect mode string */ + if (*mode != '\0') + return NULL; + + file = (XFILE)WOLFMQTT_MALLOC(sizeof(*file)); + if (file != NULL) { + if (fs_open(file, filename, flags) != 0) { + WOLFMQTT_FREE(file); + file = NULL; + } + } + + return file; +} + +int z_fs_close(XFILE file) +{ + int ret; + + if (file == NULL) + return -1; + ret = (fs_close(file) == 0) ? 0 : -1; + + WOLFMQTT_FREE(file); + + return ret; +} +#endif /* !WOLFSSL_ZEPHYR */ +#endif /* !NO_FILESYSTEM */ + +#else + +/* Default implementations */ + +#endif diff --git a/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttport.h b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttport.h new file mode 100644 index 0000000..b1e0179 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/fwserver/mqttport.h @@ -0,0 +1,320 @@ +/* + * mqttport.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_PORT_H +#define WOLFMQTT_PORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* FreeRTOS TCP */ +#ifdef FREERTOS_TCP + #include "FreeRTOS.h" + #include "task.h" + #include "FreeRTOS_IP.h" + #include "FreeRTOS_DNS.h" + #include "FreeRTOS_Sockets.h" + + #define SOCKET_T Socket_t + #define SOCK_ADDR_IN struct freertos_sockaddr + +/* ToppersOS and LWIP */ +#elif defined(TOPPERS) && defined(WOLFSSL_LWIP) + /* lwIP includes. */ + #include "lwip/api.h" + #include "lwip/tcpip.h" + #include "lwip/memp.h" + #include "lwip/stats.h" + #include "lwip/sockets.h" + #include "lwip/netdb.h" + +/* FreeRTOS and LWIP */ +#elif defined(FREERTOS) && defined(WOLFSSL_LWIP) + /* Scheduler includes. */ + #include "FreeRTOS.h" + #include "task.h" + #include "semphr.h" + + /* lwIP includes. */ + #include "lwip/api.h" + #include "lwip/tcpip.h" + #include "lwip/memp.h" + #include "lwip/stats.h" + #include "lwip/sockets.h" + #include "lwip/netdb.h" + +/* LWIP only */ +#elif defined(WOLFSSL_LWIP) + /* lwIP includes. */ + #include "lwip/api.h" + #include "lwip/tcpip.h" + #include "lwip/memp.h" + #include "lwip/stats.h" + #include "lwip/sockets.h" + #include "lwip/netdb.h" + +/* wolfIP TCP/IP stack */ +#elif defined(WOLFMQTT_WOLFIP) + #include "wolfip.h" + + #define SOCKET_T int + #define SOCKET_INVALID (-1) + #define SOCK_ADDR_IN struct wolfIP_sockaddr_in + /* For wolfIP targets without filesystem support, define NO_FILESYSTEM + * via build configuration (e.g., compiler flags or user_settings.h). */ + #ifndef NO_FILESYSTEM + #define NO_FILESYSTEM + #endif + +/* User defined IO */ +#elif defined(WOLFMQTT_USER_IO) + #include "userio_template.h" + +/* NetX */ +#elif defined(HAVE_NETX) + #include "nx_api.h" + + #define SOCKET_T NX_TCP_SOCKET + #define SOCK_ADDR_IN NXD_ADDRESS + +/* Windows */ +#elif defined(USE_WINDOWS_API) + #include + #include + #include + #define SOCKET_T SOCKET + #ifdef _WIN32 + #define SOERROR_T int + #else + #define SOERROR_T char + #endif + #define SELECT_FD(fd) (fd) + #ifndef SOCKET_INVALID /* Do not redefine from wolfssl */ + #define SOCKET_INVALID ((SOCKET_T)INVALID_SOCKET) + #endif + #define SOCK_CLOSE closesocket + #define SOCK_SEND(s,b,l,f) send((s), (const char*)(b), (size_t)(l), (f)) + #define SOCK_RECV(s,b,l,f) recv((s), (char*)(b), (size_t)(l), (f)) + #define GET_SOCK_ERROR(f,s,o,e) (e) = WSAGetLastError() + #define SOCK_EQ_ERROR(e) (((e) == WSAEWOULDBLOCK) || ((e) == WSAEINPROGRESS)) + +/* Freescale MQX / RTCS */ +#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) + #if defined(FREESCALE_MQX) + #include + #endif + #include + /* Note: Use "RTCS_geterror(sock->fd);" to get error number */ + #define SOCKET_INVALID RTCS_SOCKET_ERROR + #define SOCKET_T uint32_t + #define SOCK_CLOSE closesocket + #define SOCK_OPEN RTCS_socket + +/* Microchip MPLABX Harmony, TCP/IP */ +#elif defined(MICROCHIP_MPLAB_HARMONY) + #include "app.h" + #include "system_config.h" + #include "tcpip/tcpip.h" + #include + #include + + #define SOCKET_INVALID (-1) + #define SOCK_CLOSE closesocket + + #ifndef WOLFMQTT_NONBLOCK + #error wolfMQTT must be built with WOLFMQTT_NONBLOCK defined for Harmony + #endif + +/* Zephyr RTOS */ +#elif defined(WOLFMQTT_ZEPHYR) + #include + #include + #ifndef CONFIG_POSIX_API + #include + #endif + #ifdef CONFIG_ARCH_POSIX + #include + #else + #include + #endif + + + #define SOCKET_INVALID (-1) + + typedef zsock_fd_set fd_set; + #define FD_ZERO ZSOCK_FD_ZERO + #define FD_SET ZSOCK_FD_SET + #define FD_ISSET ZSOCK_FD_ISSET + #define select zsock_select + + #ifdef WOLFSSL_ZEPHYR + /* wolfSSL takes care of most defines */ + #include + #else + #define addrinfo zsock_addrinfo + #define getaddrinfo zsock_getaddrinfo + #define freeaddrinfo zsock_freeaddrinfo + #define socket zsock_socket + #define close zsock_close + #define SOCK_CONNECT zsock_connect + #define getsockopt zsock_getsockopt + #define setsockopt zsock_setsockopt + #define send zsock_send + #define recv zsock_recv + #define MSG_PEEK ZSOCK_MSG_PEEK + #ifndef NO_FILESYSTEM + #define XFOPEN z_fs_open + #define XFCLOSE z_fs_close + + #define XFILE struct fs_file_t* + /* These are our wrappers for opening and closing files to + * make the API more POSIX like. Copied from wolfSSL */ + XFILE z_fs_open(const char* filename, const char* mode); + int z_fs_close(XFILE file); + #endif + #endif + + #ifndef NO_FILESYSTEM + #ifndef XFILE + #define XFILE struct fs_file_t* + #endif + #ifndef XFFLUSH + #define XFFLUSH fs_sync + #endif + #ifndef XFSEEK + #define XFSEEK fs_seek + #endif + #ifndef XFTELL + #define XFTELL fs_tell + #endif + #ifndef XFREWIND + #define XFREWIND fs_rewind + #endif + #ifndef XFREAD + #define XFREAD(P,S,N,F) fs_read(F, P, S*N) + #endif + #ifndef XFWRITE + #define XFWRITE(P,S,N,F) fs_write(F, P, S*N) + #endif + #ifndef XSEEK_SET + #define XSEEK_SET FS_SEEK_SET + #endif + #ifndef XSEEK_END + #define XSEEK_END FS_SEEK_END + #endif + #endif + +/* Linux */ +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +/* Setup defaults */ +#ifndef NO_FILESYSTEM +#ifndef XFILE + #define XFILE FILE* +#endif +#ifndef XFOPEN + #define XFOPEN fopen +#endif +#ifndef XFCLOSE + #define XFCLOSE fclose +#endif +#ifndef XFSEEK + #define XFSEEK fseek +#endif +#ifndef XFTELL + #define XFTELL ftell +#endif +#ifndef XFREAD + #define XFREAD fread +#endif +#ifndef XFWRITE + #define XFWRITE fwrite +#endif +#ifndef XSEEK_SET + #define XSEEK_SET SEEK_SET +#endif +#ifndef XSEEK_END + #define XSEEK_END SEEK_END +#endif +#endif /* NO_FILESYSTEM */ +#ifndef SOCK_OPEN + #define SOCK_OPEN socket +#endif +#ifndef SOCKET_T + #define SOCKET_T int +#endif +#ifndef SOERROR_T + #define SOERROR_T int +#endif +#ifndef SELECT_FD + #define SELECT_FD(fd) ((fd) + 1) +#endif +#ifndef SOCKET_INVALID + #define SOCKET_INVALID ((SOCKET_T)0) +#endif +#ifndef SOCK_CONNECT + #define SOCK_CONNECT connect +#endif +#ifndef SOCK_SEND + #define SOCK_SEND(s,b,l,f) send((s), (b), (size_t)(l), (f)) +#endif +#ifndef SOCK_RECV + #define SOCK_RECV(s,b,l,f) recv((s), (b), (size_t)(l), (f)) +#endif +#ifndef SOCK_CLOSE + #define SOCK_CLOSE close +#endif +#ifndef SOCK_ADDR_IN + #define SOCK_ADDR_IN struct sockaddr_in +#endif +#ifdef SOCK_ADDRINFO + #define SOCK_ADDRINFO struct addrinfo +#endif +#ifndef GET_SOCK_ERROR + #define GET_SOCK_ERROR(f,s,o,e) \ + socklen_t len = sizeof(so_error); \ + (void)getsockopt((f), (s), (o), &(e), &len) +#endif +#ifndef SOCK_EQ_ERROR + #define SOCK_EQ_ERROR(e) (((e) == EWOULDBLOCK) || ((e) == EAGAIN)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFMQTT_PORT_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.crt b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.crt new file mode 100644 index 0000000..e67c3f2 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfmgAwIBAgIUAe4XxolplroT+prZoC45PJTCsb4wDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNTXlNb3NxdWl0dG9DQTAeFw0yNjAzMzEwNjQyNTZaFw0z +NjAzMjgwNjQyNTZaMBgxFjAUBgNVBAMMDU15TW9zcXVpdHRvQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvCU+CoYGd9y2GFaEyhoU8ofgphW5ITUD6 +CbAETYTH7YL4zvPPncH5/9IyY+jqny8UkiFwvLZV14jkdDFiP+l0uHZPuh/t0Ljd +6bgXzlBTnOtDUh3LJEoL0W+LY5p4/tCTixFwASciBK39ork6zXKmKA2vXuzSeKy4 +qzs6TA8o1BfzPjKi8Fm/2OVH3VM4TVT3R7+S48Vy5tBivl2MOXqb/cWUNFGSDSk4 +bAXuoDPx9DT/HKnzU88IjsZvn2o5k1kcvhVIrnGjHfIdu01JXV/HAtKS0hsBNhKo +ABA3zw88yhqTNMfhhAnAkTcv3jjLMyZAzTw3OyT8JQ5Lmo9VHgkvAgMBAAGjUzBR +MB0GA1UdDgQWBBQQvMu5iFTWI8B3JXG/SFk+pf0wBjAfBgNVHSMEGDAWgBQQvMu5 +iFTWI8B3JXG/SFk+pf0wBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4IBAQCoit4jB5NZokCUKifi2VX2BOjBBDbHK98M7EIMj6rOwyTv9+ZjK9604Q/5 +nZVXp00talpVmiF4OozbFscEFDX6No5rHiwh3ljKYVz+ZZEL+2a4ZyPKom+ATH0Q +vRO4cw4AwA2u4Bgsp1pCMiWtPrpQccv949GJ7I1f3eM6gaHAJkhZOo9ngwvvBC7p +4uNmyqsXxCL9g+0AlA+wdQb0OSaO0t2i0p85P1VdnMnbt+MFS6oo5C0/A+T54Jic +T7k+rvO9aES0qb1Gp9LeiC5dXXvBC0+7KegBberIMcXsw6xIZq8EcAkavdBDLbqs +ocWeORrbboWyiGN7Cd6oBkKnAcfb +-----END CERTIFICATE----- diff --git a/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.key b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.key new file mode 100644 index 0000000..b57e1bf --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCvCU+CoYGd9y2G +FaEyhoU8ofgphW5ITUD6CbAETYTH7YL4zvPPncH5/9IyY+jqny8UkiFwvLZV14jk +dDFiP+l0uHZPuh/t0Ljd6bgXzlBTnOtDUh3LJEoL0W+LY5p4/tCTixFwASciBK39 +ork6zXKmKA2vXuzSeKy4qzs6TA8o1BfzPjKi8Fm/2OVH3VM4TVT3R7+S48Vy5tBi +vl2MOXqb/cWUNFGSDSk4bAXuoDPx9DT/HKnzU88IjsZvn2o5k1kcvhVIrnGjHfId +u01JXV/HAtKS0hsBNhKoABA3zw88yhqTNMfhhAnAkTcv3jjLMyZAzTw3OyT8JQ5L +mo9VHgkvAgMBAAECggEAKO7Gd0gJpcVOEhMw1T38HofKHvujJKlcTpzBWuA98vsq +K8FJ5v1/hVU79KjEqUl8mxTpWRHomf6DdtltSwqjL7aLhG2EVfr/oHWtpXOL+WGy +6tVjPkRIRKc/rJsoXq9IW5ikBUtfuWbawXuyUXwGGbE4MhDC1R1/VthES4fJ68P6 +gzJoAuZ5KlB7g3dMXICRQwJbGK9qqIv3jde1swsACM/0NZ30PDj25ufk7OIgnKwI +sJvsUYa4ai3gaI3m8mobQjodz8Mv7v3waE62ATOgOOg/cdnOGcTwX9tWYHqev/j7 +lpRo39wEcvmo6QJh8SfGe5arvB6/DUBrgKhCXgJskQKBgQDgEw0MkqQIzbiS6Cd1 +XUUmITjhXvbj57I5kTgBKLGfQlwgoAmx5wXegPnXV+P6J1zgZjMzYTCNGVOwXLNu +j8/IEO6tL74wjedvPbSjGxdC+s08naWi2qXQT3GKH72aLQ5jVWwqgAGbecyw2e1k +bvSRvqtAukzFBQ+WvBD1eUcObQKBgQDH+aILjMP64CAoWS60Ihtt8xhrst1KcXir +5tFNE1/75Fax2yzUv9I46aTUiw776GEwGxcD5JHNoPPir7JuPq0ON7fA7e04IHCd +1KYczXc7FtVVYTmzDgZzpEu9Hm6tLQCkcLrToTxWToy8i2d57z3AP8RAumR7wpOR +IfcxdN2EiwKBgDxVJNhTy5MLQT1dAPLa8Dl6DISyOyvWQOi7Lycwu9Kz3XZzYOjj +2XK1q4/N/HZojttk6ThWXOpA/YWpEC8ezcWCuaEsD7unGb+cD70zGCpSuVwu3mBh +GvBh06JrNXQD2bHpWDyBRu2D+BdONlsqfPOZMSc2gmEyIaZmcPKHexhpAoGBAMeK +tgKWZNyKFiyMyQif3AT7hIu7AAuPbJ0DhihFxApCj6uLi5GDEw8NPReE5t/AwnHR +JkcRJdgs2g+ByCcvzVkZifcusv3FiPpEvbiR4X6JyzgV+xBLV26/K4A7xK5y8Ggl +wJb6FY39dXuSEx/d9R1Hw9ne4/OGdd9lKBcmiP//AoGBAIDuubT9Sjok3vNI1snD +2B+OJuu6JpUFkd4T19IVWUjFes3y4OcClWMaf8v7ft5vKrQFADDWTvVgwbHerf5b +a3IpNWlSYQEI08FgsEnLNOy608HLoD/vOsEWPcg715Nb/RRju2I8l+s3WHuCrzs+ +h9f9kG/Eflj9z+0FxjJt+X+w +-----END PRIVATE KEY----- diff --git a/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.srl b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.srl new file mode 100644 index 0000000..7dcd833 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/ca.srl @@ -0,0 +1 @@ +58BBD45BEB690783BB7ABCCF40DA1CDABA4154C5 diff --git a/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.crt b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.crt new file mode 100644 index 0000000..8a69482 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIC/zCCAeegAwIBAgIUWLvUW+tpB4O7erzPQNoc2rpBVMUwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNTXlNb3NxdWl0dG9DQTAeFw0yNjAzMzEwNjQ0MDBaFw0y +NzAzMzEwNjQ0MDBaMBcxFTATBgNVBAMMDDE5Mi4xNjguMS4xMDCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAPNLwJbk7+tcFByu4L9asxETP63ReRBYHnF8 +HPeRIF4OV42mNiUPRO12D0sa0o0nhDU6JgW2uDdke2QE8O/Mxlj8kybiWSorF5uW +g8V1f8+yVSaoamlaE6TRQA4bjSOlXDKIlJInfqMT4E0JqfA2q0+Q9Vv5KDuhFfin +zDdupeqOKsMjWdMvZuckIOzMgySRbGbvcm6TOp0f/zcyJRUSUEwl5vE/U9X76XM0 +8qilScHMhiDNSt5b6xvCFLnzFlWO44AH3KjZwkQT8kIrg8lOQlupFmyLJ3xQAFjQ +elBiOjPgKW4ad7tHBlJwK2vgwQL9axez+DHOWyAoYlcc2fp0XXMCAwEAAaNCMEAw +HQYDVR0OBBYEFL9ntu9514EdCnYq8qkcoPkwWyn5MB8GA1UdIwQYMBaAFBC8y7mI +VNYjwHclcb9IWT6l/TAGMA0GCSqGSIb3DQEBCwUAA4IBAQADQWlUIAiPMD84NogC +7yAxu4sjG6fm3adnTwKeQqGNcocrKL75b3qGqBCPzyk92nVLrybyBIBSgVHHDNAg +fBmBaBAFcXUxKZlzjzfDGlvFS0A6N1uxFd0dbuOqrieC0SvvbLElmyd4qzPuFxf+ +H3TyClCneQkvr2Oqrq8CCfrTQLavI7kg3ZrfkSS/Y9RHWYFG1PvGJqlNRzaZXhBi +1yZ3sld4F07vJ7UeueRCbkCI4l/aYDZlyBGhprrRhUsMRhlxP1nAu5WzhFXljKpM +7wEhmCGdx0hQH8965NJ56SG9acQTn5qxPc7RlxEMV9fAtslrX3Nh1Fe1E0TGZsFp +SZfi +-----END CERTIFICATE----- diff --git a/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.csr b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.csr new file mode 100644 index 0000000..d6352ab --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICXDCCAUQCAQAwFzEVMBMGA1UEAwwMMTkyLjE2OC4xLjEwMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA80vAluTv61wUHK7gv1qzERM/rdF5EFgecXwc +95EgXg5XjaY2JQ9E7XYPSxrSjSeENTomBba4N2R7ZATw78zGWPyTJuJZKisXm5aD +xXV/z7JVJqhqaVoTpNFADhuNI6VcMoiUkid+oxPgTQmp8DarT5D1W/koO6EV+KfM +N26l6o4qwyNZ0y9m5yQg7MyDJJFsZu9ybpM6nR//NzIlFRJQTCXm8T9T1fvpczTy +qKVJwcyGIM1K3lvrG8IUufMWVY7jgAfcqNnCRBPyQiuDyU5CW6kWbIsnfFAAWNB6 +UGI6M+Apbhp3u0cGUnAra+DBAv1rF7P4Mc5bIChiVxzZ+nRdcwIDAQABoAAwDQYJ +KoZIhvcNAQELBQADggEBAA7EcElUF36bDq0SgTo7FwjceOSSIZYjZsys81njX7v0 +yNhnVzEoPdSEdSUHUGE9xpqmXMLPgkDIY/ShmicXw50eQu/b3puZa00QxFNjQys/ +zW7xWdSBpWhSuut69ZUBmf0QLFiUpIf2uCj5cNfCnOSao8SYZTYYnxXzRmprnYd9 +gpQfE7EIKI6/GNZWFBRBcITG5sr3ftQGG/mzdeINUsXkG0krHQ8sLxswXnXBN5eP +bm/txVNGgn7iMNgpiQdfClHk16orGP2/jZNDWHKVnrM7GSAM3mWWup0m3UFkG/oS +z77ymzdBLyU10V0O6tn5r129aTuyxcRjabMBTSQuLmA= +-----END CERTIFICATE REQUEST----- diff --git a/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.key b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.key new file mode 100644 index 0000000..715e195 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/certs/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDzS8CW5O/rXBQc +ruC/WrMREz+t0XkQWB5xfBz3kSBeDleNpjYlD0Ttdg9LGtKNJ4Q1OiYFtrg3ZHtk +BPDvzMZY/JMm4lkqKxebloPFdX/PslUmqGppWhOk0UAOG40jpVwyiJSSJ36jE+BN +CanwNqtPkPVb+Sg7oRX4p8w3bqXqjirDI1nTL2bnJCDszIMkkWxm73JukzqdH/83 +MiUVElBMJebxP1PV++lzNPKopUnBzIYgzUreW+sbwhS58xZVjuOAB9yo2cJEE/JC +K4PJTkJbqRZsiyd8UABY0HpQYjoz4CluGne7RwZScCtr4MEC/WsXs/gxzlsgKGJX +HNn6dF1zAgMBAAECggEALu0G/fO1RMeTR38BcVcMn7f/Ocbj21+rq4bdTXDL9eg9 +QeBzpA1AZa62P22ye7BKAzwudj6qZGC4ne+ICzCLd0FsHn1c5lG+cW+EBfNmTgie +/OBR+fT6QflsKextehkcYdDPGyuL9OdSBXneMatRunyCnpU9idbFlprCvFRWoxkd +ZaW1CK+E4wyYhM2S1Buv0t5zERKnUsEs9C/wnqSM1EjNdOWlghRsGi4Gc7+eNhCL +oQ2IHtYAdCFr2AjgtwE0GSEvJqkqcWGtX+KFO009eqjCz29oLlmPJ6WxHdUZzEKs +dy4xdHNIvjlJFKTsGmEe3aWxvfeEkWR2IAZL+fXtoQKBgQD8v/cTWcp73nKUwywE +6vMem0BHSdidWkyOx5EOhzBihhaNapJObQwjB/42gHVjQJlCWiq4bKOcUsYatpmE +HPAJCZZTjSrontx7E6DmEzeG7mR20+IDnh2D9GnL5mzttnHs/8VMFUlW/Xmpex4X +EqJmFH/WbthK1eKpJ/em2I+5kwKBgQD2bKpXs3Tn51FofXhHgnc/o1s0Gs0EA2VD +GpoTFuLmx6MQySHEjqh2ewxvbORPLOJsa6C3J4CwW7muV00zudzVab/qqrqqUyQ7 +Ek/SMuoxBjLsaNnA6OK2NWDSsg1dt6gfc+tMbIe4mODuwgH86rXynjGaQoaLFP93 +sswhO/W4oQKBgEzbry2SO3GpdlxocUVZkO64tTY4bYeEAw3T//dUAtGjBs6gHrd8 +j5lDf/lg3NF+vlwgASet2Z7ayOUsq8gx6IhHAxCLOAYe8Nr4YBA2vcbpTWGZEvyY +o08SDu3zNyKePUydhoiZXID4eXuhNcg8YWf7SdiSQnGXUuKvha6V4jbDAoGABZ4Y +jANFSPwW+QDTOx4YRdUdgSSTQ+In7s2ucXz2Ezi92OYc5PQzlu/bKrcaxUtKphoW +AdbesiaFJxpKYkF8bUkJ3PmjY7qdetNCCpKqkA+KHaWOQT2N3YV9/Y+dimNja8Qi +uw1GUsj5Z7f4f6GCIFPG4bNeidEiucHTsXZCdcECgYBmxGsLFqyX3cnu7OF3bdmm +dVtB9tRhND+POmHW4q0XpfwdXJpTiir4lfiuOj3A60O8Z44IHmTxUl90t2FxrKft +lETNugOKPmoMivRi2TR9KiLH2B0AMbYfDckgxLogxbqacu9xhvkH1zOkGvUnKxB0 +ur1TS8xFi2ZRwm2Eg3jljw== +-----END PRIVATE KEY----- diff --git a/dm-wolfssl-ota-client-with-zephyr/mqttBroker/config/mosquitto.conf b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/config/mosquitto.conf new file mode 100644 index 0000000..511a01e --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/config/mosquitto.conf @@ -0,0 +1,1127 @@ +# Config file for mosquitto +# +# See mosquitto.conf(5) for more information. +# +# Default values are shown, uncomment to change. +# +# Use the # character to indicate a comment, but only if it is the +# very first character on the line. + +# ================================================================= +# General configuration +# ================================================================= + +# Use per listener security settings. +# +# It is recommended this option be set before any other options. +# +# If this option is set to true, then all authentication and access control +# options are controlled on a per listener basis. The following options are +# affected: +# +# acl_file +# allow_anonymous - use listener_allow_anonymous instead +# allow_zero_length_clientid +# auto_id_prefix - use listener_auto_id_prefix instead +# password_file +# plugin - use plugin_load and plugin_use instead +# plugin_opt_* +# psk_file +# +# Note that if set to true, then a durable client (i.e. with clean session set +# to false) that has disconnected will use the ACL settings defined for the +# listener that it was most recently connected to. +# +# The default behaviour is for this to be set to false, which maintains the +# setting behaviour from previous versions of mosquitto. +#per_listener_settings false + + +# This option controls whether a client is allowed to connect with a zero +# length client id or not. This option only affects clients using MQTT v3.1.1 +# and later. If set to false, clients connecting with a zero length client id +# are disconnected. If set to true, clients will be allocated a client id by +# the broker. This means it is only useful for clients with clean session set +# to true. +#allow_zero_length_clientid true + +# If allow_zero_length_clientid is true, this option allows you to set a prefix +# to automatically generated client ids to aid visibility in logs. +# Defaults to 'auto-' +#auto_id_prefix auto- + +# This option affects the scenario when a client subscribes to a topic that has +# retained messages. It is possible that the client that published the retained +# message to the topic had access at the time they published, but that access +# has been subsequently removed. If check_retain_source is set to true, the +# default, the source of a retained message will be checked for access rights +# before it is republished. When set to false, no check will be made and the +# retained message will always be published. This affects all listeners. +#check_retain_source true + +# The maximum number of client sessions to allow across the whole broker. In +# this context a client session means either a client currently connected via +# the network, or a client that has clean_session = False (MQTT v3.x) and is +# disconnected, or has disconnected and still hasn't exceeded its session +# expiry interval (MQTT v5). +# +# See also the global_max_connections setting, and the max_connections setting, +# which applies to listeners. If you set global_max_clients to 1000 and +# max_connections on a listener to 10, then that means only 10 simultaneous +# connections will be allowed at once, with an overall maximum of 1000 client +# sessions. +# +#global_max_clients -1 + +# The maximum number of currently connected clients to allow across the whole +# broker. +# See also the global_max_clients and max_connections settings. +#global_max_connections -1 + +# QoS 1 and 2 messages will be allowed inflight per client until this limit +# is exceeded. Defaults to 0. (No maximum) +# See also max_inflight_messages +#max_inflight_bytes 0 + +# The maximum number of QoS 1 and 2 messages currently inflight per +# client. +# This includes messages that are partway through handshakes and +# those that are being retried. Defaults to 20. Set to 0 for no +# maximum. Setting to 1 will guarantee in-order delivery of QoS 1 +# and 2 messages. +#max_inflight_messages 20 + +# For MQTT v5 clients, it is possible to have the server send a "server +# keepalive" value that will override the keepalive value set by the client. +# This is intended to be used as a mechanism to say that the server will +# disconnect the client earlier than it anticipated, and that the client should +# use the new keepalive value. The max_keepalive option allows you to specify +# that clients may only connect with keepalive less than or equal to this +# value, otherwise they will be sent a server keepalive telling them to use +# max_keepalive. This only applies to MQTT v5 clients. The maximum value is +# 65535. Set to 0 to allow infinite keepalive. Defaults to 0. +# +# Set to 0 to allow clients to set keepalive = 0, which means no keepalive +# checks are made and the client will never be disconnected by the broker if no +# messages are received. You should be very sure this is the behaviour that you +# want. +# +# For MQTT v3.1.1 and v3.1 clients, there is no mechanism to tell the client +# what keepalive value they should use. If an MQTT v3.1.1 or v3.1 client +# specifies a keepalive time greater than max_keepalive they will be sent a +# CONNACK message with the "identifier rejected" reason code, and disconnected. +# +#max_keepalive 0 + +# For MQTT v5 clients, it is possible to have the server send a "maximum packet +# size" value that will instruct the client it will not accept MQTT packets +# with size greater than max_packet_size bytes. This applies to the full MQTT +# packet, not just the payload. Setting this option to a positive value will +# set the maximum packet size to that number of bytes. If a client sends a +# packet which is larger than this value, it will be disconnected. This applies +# to all clients regardless of the protocol version they are using, but v3.1.1 +# and earlier clients will of course not have received the maximum packet size +# information. Defaults to no limit. Setting below 20 bytes is forbidden +# because it is likely to interfere with ordinary client operation, even with +# very small payloads. +#max_packet_size 0 + +# QoS 1 and 2 messages above those currently in-flight will be queued per +# client until this limit is exceeded. Defaults to 0. (No maximum) +# See also max_queued_messages. +# If both max_queued_messages and max_queued_bytes are specified, packets will +# be queued until the first limit is reached. +#max_queued_bytes 0 + +# Set the maximum QoS supported. Clients publishing at a QoS higher than +# specified here will be disconnected. +#max_qos 2 + +# The maximum number of QoS 1 and 2 messages to hold in a queue per client +# above those that are currently in-flight. Defaults to 1000. Set +# to 0 for no maximum (not recommended). +# See also queue_qos0_messages. +# See also max_queued_bytes. +#max_queued_messages 1000 +# +# This option sets the maximum number of heap memory bytes that the broker will +# allocate, and hence sets a hard limit on memory use by the broker. Memory +# requests that exceed this value will be denied. The effect will vary +# depending on what has been denied. If an incoming message is being processed, +# then the message will be dropped and the publishing client will be +# disconnected. If an outgoing message is being sent, then the individual +# message will be dropped and the receiving client will be disconnected. +# Defaults to no limit. +#memory_limit 0 + +# This option sets the maximum publish payload size that the broker will allow. +# Received messages that exceed this size will not be accepted by the broker. +# The default value is 0, which means that all valid MQTT messages are +# accepted. MQTT imposes a maximum payload size of 268435455 bytes. +#message_size_limit 0 + +# This option allows the session of persistent clients (those with clean +# session set to false) that are not currently connected to be removed if they +# do not reconnect within a certain time frame. This is a non-standard option +# in MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions. +# +# Badly designed clients may set clean session to false whilst using a randomly +# generated client id. This leads to persistent clients that connect once and +# never reconnect. This option allows these clients to be removed. This option +# allows persistent clients (those with clean session set to false) to be +# removed if they do not reconnect within a certain time frame. +# +# The expiration period should be an integer followed by one of h d w m y for +# hour, day, week, month and year respectively. For example +# +# persistent_client_expiration 2m +# persistent_client_expiration 14d +# persistent_client_expiration 1y +# +# The default if not set is to never expire persistent clients. +#persistent_client_expiration + +# Write process id to a file. Default is a blank string which means +# a pid file shouldn't be written. +# This should be set to /var/run/mosquitto/mosquitto.pid if mosquitto is +# being run automatically on boot with an init script and +# start-stop-daemon or similar. +#pid_file + +# Set to true to queue messages with QoS 0 when a persistent client is +# disconnected. These messages are included in the limit imposed by +# max_queued_messages and max_queued_bytes +# Defaults to false. +# This is a non-standard option for the MQTT v3.1 spec but is allowed in +# v3.1.1. +#queue_qos0_messages false + +# Set to false to disable retained message support. If a client publishes a +# message with the retain bit set, it will be disconnected if this is set to +# false. +#retain_available true + +# Disable Nagle's algorithm on client sockets. This has the effect of reducing +# latency of individual messages at the potential cost of increasing the number +# of packets being sent. +#set_tcp_nodelay false + +# Time in seconds between updates of the $SYS tree. +# Set to 0 to disable the publishing of the $SYS tree. +#sys_interval 10 + +# The MQTT specification requires that the QoS of a message delivered to a +# subscriber is never upgraded to match the QoS of the subscription. Enabling +# this option changes this behaviour. If upgrade_outgoing_qos is set true, +# messages sent to a subscriber will always match the QoS of its subscription. +# This is a non-standard option explicitly disallowed by the spec. +#upgrade_outgoing_qos false + +# When run as root, drop privileges to this user and its primary +# group. +# Set to root to stay as root, but this is not recommended. +# If set to "mosquitto", or left unset, and the "mosquitto" user does not exist +# then it will drop privileges to the "nobody" user instead. +# If run as a non-root user, this setting has no effect. +# Note that on Windows this has no effect and so mosquitto should be started by +# the user you wish it to run as. +#user mosquitto + +# ================================================================= +# Listeners +# ================================================================= + +# Listen on a port/ip address combination. By using this variable +# multiple times, mosquitto can listen on more than one port. If +# this variable is used and neither bind_address nor port given, +# then the default listener will not be started. +# The port number to listen on must be given. Optionally, an ip +# address or host name may be supplied as a second argument. In +# this case, mosquitto will attempt to bind the listener to that +# address and so restrict access to the associated network and +# interface. By default, mosquitto will listen on all interfaces. +# Note that for a websockets listener it is not possible to bind to a host +# name. +# +# On systems that support Unix Domain Sockets, it is also possible +# to create a # Unix socket rather than opening a TCP socket. In +# this case, the port number should be set to 0 and a unix socket +# path must be provided, e.g. +# listener 0 /tmp/mosquitto.sock +# +# listener port-number [ip address/host name/unix socket path] +#listener + +# By default, a listener will attempt to listen on all supported IP protocol +# versions. If you do not have an IPv4 or IPv6 interface you may wish to +# disable support for either of those protocol versions. In particular, note +# that due to the limitations of the websockets library, it will only ever +# attempt to open IPv6 sockets if IPv6 support is compiled in, and so will fail +# if IPv6 is not available. +# +# Set to `ipv4` to force the listener to only use IPv4, or set to `ipv6` to +# force the listener to only use IPv6. If you want support for both IPv4 and +# IPv6, then do not use the socket_domain option. +# +#socket_domain + +# Bind the listener to a specific interface. This is similar to +# the [ip address/host name] part of the listener definition, but is useful +# when an interface has multiple addresses or the address may change. If used +# with the [ip address/host name] part of the listener definition, then the +# bind_interface option will take priority. +# Not available on Windows. +# +# Example: bind_interface eth0 +#bind_interface + +# When a listener is using the websockets protocol, it is possible to serve +# http data as well. Set http_dir to a directory which contains the files you +# wish to serve. If this option is not specified, then no normal http +# connections will be possible. +#http_dir + +# The maximum number of client connections to allow. This is +# a per listener setting. Use global_max_clients if you wish to enforce a +# client limit across the whole broker. +# Default is -1, which means unlimited connections, unless otherwise limited by +# global_max_clients. +# Note that other process limits mean that unlimited connections +# are not really possible. Typically the default maximum number of +# connections possible is around 1024. +#max_connections -1 + +# The listener can be restricted to operating within a topic hierarchy using +# the mount_point option. This is achieved be prefixing the mount_point string +# to all topics for any clients connected to this listener. This prefixing only +# happens internally to the broker; the client will not see the prefix. +#mount_point + +# Choose the protocol to use when listening. +# This can be either mqtt, websockets, or http_api. +# +# mqtt: A standard MQTT based listener +# websockets: MQTT tunnelled over WebSockets. If the legacy websockets support +# using libwebsockets is used, then only a reduced TLS feature set +# is available. +# http_api: This starts the listener as a very simple webserver that can also +# serve some HTTP API requests. TLS is supported for this listener, +# however only the certfile and keyfile options are allowed. +# Authentication is not currently possible - to use this listener it +# is strongly recommended to bind the listener to the loopback +# interface and then configure a reverse proxy with the appropriate +# encryption and authentication. +#protocol mqtt + +# Accepted protocol versions. This sets what versions of the MQTT protocol will +# be accepted on this listener. Can be any combination of 3, 4, 5 in a comma +# separated list, e.g. +# +# # Allow v5.0 only: +listener 8883 +# accept_protocol_versions 5 +# +# # Allow v3.1 and v3.1.1: +# listener 1884 +# accept_protocol_versions 3, 4 +# +# Defaults to allowing all versions. +accept_protocol_versions 3,4,5 + +# If you wish to allow unauthenticated connections for a specific listener, use +# the listener_allow_anonymous option set to true. If not set, the value +# determined by the global allow_anonymous option will be used. If +# listener_allow_anonymous is set at the same time as allow_anonymous, the +# value set by listener_allow_anonymous will always take priority. +listener_allow_anonymous true + +# This option allows you to set a prefix to automatically generated client ids +# (i.e. for when a client connects without providing a client id) to aid +# visibility in logs. +# Defaults to 'auto-' +#listener_auto_id_prefix auto- + +# Set use_username_as_clientid to true to replace the clientid that a client +# connected with with its username. This allows authentication to be tied to +# the clientid, which means that it is possible to prevent one client +# disconnecting another by using the same clientid. +# If a client connects with no username it will be disconnected as not +# authorised when this option is set to true. +# Do not use in conjunction with clientid_prefixes. +# See also use_identity_as_username. +# This does not apply globally, but on a per-listener basis. +#use_username_as_clientid + +# Change the size of the buffer used when reading from the network before the +# size of the MQTT packet is known. Defaults to 4096. Packets received that are +# smaller than this value in principle only need a single read() call, making +# reading packets more efficient. +# +# This also operates as the option that sets the size of the buffer used by +# websockets when reading the initial header. If you are passing large +# header data such as cookies then you may need to increase this value. +#packet_buffer_size + +# Enforce origin checking for websockets connections. This should be set to the +# string of the host that you wish to allow connections from, as set as the +# Origin header in the http request. +# For example: https://example.com:8080 +# If left unset will allow connections from any origin. +# May be set multiple times. +#websockets_origin + +# ----------------------------------------------------------------- +# Certificate based SSL/TLS support +# ----------------------------------------------------------------- +# The following options can be used to enable certificate based SSL/TLS support +# for this listener. Note that the recommended port for MQTT over TLS is 8883, +# but this must be set manually. +# +# See also the mosquitto-tls man page and the "Pre-shared-key based SSL/TLS +# support" section. Only one of certificate or PSK encryption support can be +# enabled for any listener. + +# Both of certfile and keyfile must be defined to enable certificate based +# TLS encryption. + +# Path to the PEM encoded server certificate. +#certfile + +# Path to the PEM encoded keyfile. +#keyfile + +# Configure the minimum version of the TLS protocol to be used for this listener. +# Possible values are tlsv1.3 and tlsv1.2. +#tls_version tlsv1.2 + +# If you wish to control which encryption ciphers are used, use the ciphers +# option. The list of available ciphers can be obtained using the "openssl +# ciphers" command and should be provided in the same format as the output of +# that command. This applies to TLS 1.2 and earlier versions only. Use +# ciphers_tls1.3 for TLS v1.3. +#ciphers + +# Choose which TLS v1.3 ciphersuites are used for this listener. +# Defaults to "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" +#ciphers_tls1.3 + +# If you have require_certificate set to true, you can create a certificate +# revocation list file to revoke access to particular client certificates. If +# you have done this, use crlfile to point to the PEM encoded revocation file. +#crlfile + +# To allow the use of ephemeral DH key exchange, which provides forward +# security, the listener must load DH parameters. This can be specified with +# the dhparamfile option. The dhparamfile can be generated with the command +# e.g. "openssl dhparam -out dhparam.pem 2048" +#dhparamfile + +# By default an TLS enabled listener will operate in a similar fashion to a +# https enabled web server, in that the server has a certificate signed by a CA +# and the client will verify that it is a trusted certificate. The overall aim +# is encryption of the network traffic. By setting require_certificate to true, +# the client must provide a valid certificate in order for the network +# connection to proceed. This allows access to the broker to be controlled +# outside of the mechanisms provided by MQTT. +#require_certificate false + +# If set true, disable_client_cert_date_checks will change the certificate +# verification behaviour to allow client certificates that are expired or are +# not yet valid, when using `require_certificate true`. +#disable_client_cert_date_checks false + +# cafile and capath define methods of accessing the PEM encoded +# Certificate Authority certificates that will be considered trusted when +# checking incoming client certificates. +# cafile defines the path to a file containing the CA certificates. +# capath defines a directory that will be searched for files +# containing the CA certificates. For capath to work correctly, the +# certificate files must have ".crt" as the file ending and you must run +# "openssl rehash " each time you add/remove a certificate. +# capath is not supported for websockets. +cafile /mosquitto/certs/ca.crt +certfile /mosquitto/certs/server.crt +keyfile /mosquitto/certs/server.key +require_certificate false +#capath + + +# If require_certificate is true, you may set use_identity_as_username to true +# to use the CN value from the client certificate as a username. If this is +# true, the password_file option will not be used for this listener. +#use_identity_as_username false + +# ----------------------------------------------------------------- +# Pre-shared-key based SSL/TLS support +# ----------------------------------------------------------------- +# The following options can be used to enable PSK based SSL/TLS support for +# this listener. Note that the recommended port for MQTT over TLS is 8883, but +# this must be set manually. +# +# See also the mosquitto-tls man page and the "Certificate based SSL/TLS +# support" section. Only one of certificate or PSK encryption support can be +# enabled for any listener. + +# The psk_hint option enables pre-shared-key support for this listener and also +# acts as an identifier for this listener. The hint is sent to clients and may +# be used locally to aid authentication. The hint is a free form string that +# doesn't have much meaning in itself, so feel free to be creative. +# If this option is provided, see psk_file to define the pre-shared keys to be +# used or create a security plugin to handle them. +#psk_hint + +# When using PSK, the encryption ciphers used will be chosen from the list of +# available PSK ciphers. If you want to control which ciphers are available, +# use the "ciphers" option. The list of available ciphers can be obtained +# using the "openssl ciphers" command and should be provided in the same format +# as the output of that command. +#ciphers + +# Set use_identity_as_username to have the psk identity sent by the client used +# as its username. Authentication will be carried out using the PSK rather than +# the MQTT username/password and so password_file will not be used for this +# listener. +#use_identity_as_username false + + +# ================================================================= +# Persistence +# ================================================================= + +# If persistence is enabled, save the in-memory database to disk +# every autosave_interval seconds. If set to 0, the persistence +# database will only be written when mosquitto exits. See also +# autosave_on_changes. +# Note that writing of the persistence database can be forced by +# sending mosquitto a SIGUSR1 signal. +#autosave_interval 1800 + +# If true, mosquitto will count the number of subscription changes, retained +# messages received and queued messages and if the total exceeds +# autosave_interval then the in-memory database will be saved to disk. +# If false, mosquitto will save the in-memory database to disk by treating +# autosave_interval as a time in seconds. +#autosave_on_changes false + +# Save persistent message data to disk (true/false). +# This saves information about all messages, including +# subscriptions, currently in-flight messages and retained +# messages. +# retained_persistence is a synonym for this option. +#persistence false + +# The filename to use for the persistent database, not including +# the path. +#persistence_file mosquitto.db + +# Location for persistent database. +# Default is an empty string (current directory). +# Set to e.g. /var/lib/mosquitto if running as a proper service on Linux or +# similar. +# +# Can also be defined by setting the MOSQUITTO_PERSISTENCE_LOCATION environment +# variable prior to running the broker. If both the config file option and the +# environment variable are set, the environment variable takes precedent. +# +# This can also be used by plugins using the mosquitto_persistence_location() function. +#persistence_location + + +# ================================================================= +# Logging +# ================================================================= + +# Places to log to. Use multiple log_dest lines for multiple +# logging destinations. +# Possible destinations are: stdout stderr syslog topic file dlt +# +# stdout and stderr log to the console on the named output. +# +# syslog uses the userspace syslog facility which usually ends up +# in /var/log/messages or similar. +# +# topic logs to the broker topic '$SYS/broker/log/', +# where severity is one of D, E, W, N, I, M which are debug, error, +# warning, notice, information and message. Message type severity is used by +# the subscribe/unsubscribe log_types and publishes log messages to +# $SYS/broker/log/M/susbcribe or $SYS/broker/log/M/unsubscribe. +# +# The file destination requires an additional parameter which is the file to be +# logged to, e.g. "log_dest file /var/log/mosquitto.log". The file will be +# closed and reopened when the broker receives a HUP signal. Only a single file +# destination may be configured. +# +# The dlt destination is for the automotive `Diagnostic Log and Trace` tool. +# This requires that Mosquitto has been compiled with DLT support. +# +# The android destination is for logging into the Android logd logging daemon. +# This options is only available when building for the Android target. +# +# Note that if the broker is running as a Windows service it will default to +# "log_dest none" and neither stdout nor stderr logging is available. +# Use "log_dest none" if you wish to disable logging. +#log_dest stderr + +# Types of messages to log. Use multiple log_type lines for logging +# multiple types of messages. +# Possible types are: debug, error, warning, notice, information, +# none, subscribe, unsubscribe, websockets, all. +# Note that debug type messages are for decoding the incoming/outgoing +# network packets. They are not logged in "topics". +#log_type error +#log_type warning +#log_type notice +#log_type information + + +# If set to true, client connection and disconnection messages will be included +# in the log. +#connection_messages true + +# If using syslog logging (not on Windows), messages will be logged to the +# "daemon" facility by default. Use the log_facility option to choose which of +# local0 to local7 to log to instead. The option value should be an integer +# value, e.g. "log_facility 5" to use local5. +#log_facility + +# If set to true, add a timestamp value to each log message. +#log_timestamp true + +# Set the format of the log timestamp. If left unset, this is the number of +# seconds since the Unix epoch. +# This is a free text string which will be passed to the strftime function. To +# get an ISO 8601 datetime, for example: +# log_timestamp_format %Y-%m-%dT%H:%M:%S +#log_timestamp_format + +# Change the websockets logging level. This is a global option, it is not +# possible to set per listener. This is an integer that is interpreted by +# libwebsockets as a bit mask for its lws_log_levels enum. See the +# libwebsockets documentation for more details. "log_type websockets" must also +# be enabled. +#websockets_log_level 0 + + +# ================================================================= +# Security +# ================================================================= + +# If set, only clients that have a matching prefix on their +# clientid will be allowed to connect to the broker. By default, +# all clients may connect. +# For example, setting "secure-" here would mean a client "secure- +# client" could connect but another with clientid "mqtt" couldn't. +#clientid_prefixes + +# Boolean value that determines whether clients that connect +# without providing a username are allowed to connect. If set to +# false then a password file should be created (see the +# password_file option) to control authenticated client access. +# +# Defaults to false, unless there are no listeners defined in the configuration +# file, in which case it is set to true, but connections are only allowed from +# the local machine. +allow_anonymous true + +# ----------------------------------------------------------------- +# Default authentication and topic access control +# ----------------------------------------------------------------- + +# Control access to the broker using a password file. This file can be +# generated using the mosquitto_passwd utility. If TLS support is not compiled +# into mosquitto (it is recommended that TLS support should be included) then +# plain text passwords are used, in which case the file should be a text file +# with lines in the format: +# username:password +# The password (and colon) may be omitted if desired, although this +# offers very little in the way of security. +# +# See the TLS client require_certificate and use_identity_as_username options +# for alternative authentication options. If a plugin is used as well as +# password_file, the plugin check will be made first. +#password_file + +# Access may also be controlled using a pre-shared-key file. This requires +# TLS-PSK support and a listener configured to use it. The file should be text +# lines in the format: +# identity:key +# The key should be in hexadecimal format without a leading "0x". +# If an plugin is used as well, the plugin check will be made first. +#psk_file + +# Control access to topics on the broker using an access control list +# file. If this parameter is defined then only the topics listed will +# have access. +# If the first character of a line of the ACL file is a # it is treated as a +# comment. +# Topic access is added with lines of the format: +# +# topic [read|write|readwrite|deny] +# +# The access type is controlled using "read", "write", "readwrite" or "deny". +# This parameter is optional (unless contains a space character) - if +# not given then the access is read/write. can contain the + or # +# wildcards as in subscriptions. +# +# The "deny" option can used to explicitly deny access to a topic that would +# otherwise be granted by a broader read/write/readwrite statement. Any "deny" +# topics are handled before topics that grant read/write access. +# +# The first set of topics are applied to anonymous clients, assuming +# allow_anonymous is true. User specific topic ACLs are added after a +# user line as follows: +# +# user +# +# The username referred to here is the same as in password_file. It is +# not the clientid. +# +# +# If is also possible to define ACLs based on pattern substitution within the +# topic. The patterns available for substitution are: +# +# %c to match the client id of the client +# %u to match the username of the client +# +# The substitution pattern must be the only text for that level of hierarchy. +# +# The form is the same as for the topic keyword, but using pattern as the +# keyword. +# Pattern ACLs apply to all users even if the "user" keyword has previously +# been given. +# +# If using bridges with usernames and ACLs, connection messages can be allowed +# with the following pattern: +# pattern write $SYS/broker/connection/%c/state +# +# pattern [read|write|readwrite] +# +# Example: +# +# pattern write sensor/%u/data +# +# If an plugin is used as well as acl_file, the plugin check will be +# made first. +#acl_file + +# ----------------------------------------------------------------- +# External authentication and topic access plugin options +# ----------------------------------------------------------------- + +# External authentication and access control can be supported with the +# plugin option. This is a path to a loadable plugin. See also the +# plugin_opt_* options described below. +# +# The plugin option can be specified multiple times to load multiple +# plugins. The plugins will be processed in the order that they are specified +# here. If the plugin option is specified alongside either of +# password_file or acl_file then the plugin checks will be made first. +# +# If the per_listener_settings option is false, the plugin will be apply to all +# listeners. If per_listener_settings is true, then the plugin will apply to +# the current listener being defined only. +# +# This option is also available as `auth_plugin`, but this use is deprecated +# and will be removed in the future. +# +#plugin + +# If the plugin option above is used, define options to pass to the +# plugin here as described by the plugin instructions. All options named +# using the format plugin_opt_* will be passed to the plugin, for example: +# +# This option is also available as `auth_opt_*`, but this use is deprecated +# and will be removed in the future. +# +# plugin_opt_db_host +# plugin_opt_db_port +# plugin_opt_db_username +# plugin_opt_db_password + +# The `plugin` option is affected by the `per_listener_settings` option. If you +# want to set `per_listener_settings true` but still want to use a plugin work +# across all listeners, then use the `global_plugin` option. This option +# functions the same as the `plugin` option in all other ways. +# +# If you set `per_listener_settings true`, then define both a `global_plugin` +# and a `plugin` (which will be attached to a specific listener), then the +# global plugin will always be processed first. +# +# If you set `per_listener_settings false`, then `global_plugin` behaves +# identically to `plugin`. +# +#global_plugin + +# ================================================================= +# Bridges +# ================================================================= + +# A bridge is a way of connecting multiple MQTT brokers together. +# Create a new bridge using the "connection" option as described below. Set +# options for the bridges using the remaining parameters. You must specify the +# address and at least one topic to subscribe to. +# +# Each connection must have a unique name. +# +# The address line may have multiple host address and ports specified. See +# below in the round_robin description for more details on bridge behaviour if +# multiple addresses are used. Note that if you use an IPv6 address, then you +# are required to specify a port. +# +# The direction that the topic will be shared can be chosen by +# specifying out, in or both, where the default value is out. +# The QoS level of the bridged communication can be specified with the next +# topic option. The default QoS level is 0, to change the QoS the topic +# direction must also be given. +# +# The local and remote prefix options allow a topic to be remapped when it is +# bridged to/from the remote broker. This provides the ability to place a topic +# tree in an appropriate location. +# +# For more details see the mosquitto.conf man page. +# +# Multiple topics can be specified per connection, but be careful +# not to create any loops. +# +# If you are using bridges with cleansession set to false (the default), then +# you may get unexpected behaviour from incoming topics if you change what +# topics you are subscribing to. This is because the remote broker keeps the +# subscription for the old topic. If you have this problem, connect your bridge +# with cleansession set to true, then reconnect with cleansession set to false +# as normal. +#connection +#address [:] [[:]] +#topic [[[out | in | both] qos-level] local-prefix remote-prefix] + +# If you need to have the bridge connect over a particular network interface, +# use bridge_bind_address to tell the bridge which local IP address the socket +# should bind to, e.g. `bridge_bind_address 192.168.1.10` +#bridge_bind_address + +# If a bridge has topics that have "out" direction, the default behaviour is to +# send an unsubscribe request to the remote broker on that topic. This means +# that changing a topic direction from "in" to "out" will not keep receiving +# incoming messages. Sending these unsubscribe requests is not always +# desirable, setting bridge_attempt_unsubscribe to false will disable sending +# the unsubscribe request. +#bridge_attempt_unsubscribe true + +# Set the version of the MQTT protocol to use with for this bridge. Can be one +# of mqttv50, mqttv311 or mqttv31. Defaults to mqttv311. +#bridge_protocol_version mqttv311 + +# If the bridge is using MQTT v5.0 then use bridge_receive_maximum to +# limit the number QoS 1 or 2 messages that can be in-flight at once. +# Must be 1-65535. Defaults to the same value as max_inflight_messages, +# which defaults to 20. +#bridge_receive_maximum 20 + +# Set the clean session variable for this bridge. +# When set to true, when the bridge disconnects for any reason, all +# messages and subscriptions will be cleaned up on the remote +# broker. Note that with cleansession set to true, there may be a +# significant amount of retained messages sent when the bridge +# reconnects after losing its connection. +# When set to false, the subscriptions and messages are kept on the +# remote broker, and delivered when the bridge reconnects. +#cleansession false + +# If the bridge is using MQTT v5.0 then use bridge_session_expiry_interval to +# set the session expiry interval. Set to 0 to have the session expire +# immediately when the connection drops. Set to 0xFFFFFFFF to have an infinite +# expiry interval. Otherwise the expiry interval is set to the number of +# seconds that you specify. +# Defaults to 0. +#bridge_session_expiry_interval 0 + +# Set the amount of time a bridge using the lazy start type must be idle before +# it will be stopped. Defaults to 60 seconds. +#idle_timeout 60 + +# Set the MQTT keepalive interval (PING) for this bridge connection, in +# seconds. +#keepalive_interval 60 + +# Set TCP keepalive parameters for this bridge connection. +# Disabled by default. +#bridge_tcp_keepalive + +# Change the maximum amount of time (in milliseconds) that transmitted data +# may remain unacknowledged at TCP level. +# Popular Linux distributions seem to set this time to "up to 20 minutes with +# system defaults" (from Linux tcp man page). Reducing this value helps +# detecting dropped connections faster. +# When used together with TCP Keepalive, it might change it's behavior. +# More details at: https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/ +# Only available on Linux. +#bridge_tcp_user_timeout + +# Set the clientid to use on the local broker. If not defined, this defaults to +# 'local.'. If you are bridging a broker to itself, it is important +# that local_clientid and clientid do not match. +#local_clientid + +# If set to true, publish notification messages to the local and remote brokers +# giving information about the state of the bridge connection. Retained +# messages are published to the topic $SYS/broker/connection//state +# unless the notification_topic option is used. +# If the message is 1 then the connection is active, or 0 if the connection has +# failed. +# This uses the last will and testament feature. +#notifications true + +# If set to true, only publish notification messages to the local broker giving +# information about the state of the bridge connection. Defaults to false. +#notifications_local_only false + +# Choose the topic on which notification messages for this bridge are +# published. If not set, messages are published on the topic +# $SYS/broker/connection//state +#notification_topic + +# Set the client id to use on the remote end of this bridge connection. If not +# defined, this defaults to 'name.hostname' where name is the connection name +# and hostname is the hostname of this computer. +# This replaces the old "clientid" option to avoid confusion. "clientid" +# remains valid for the time being. +#remote_clientid + +# Set the password to use when connecting to a broker that requires +# authentication. This option is only used if remote_username is also set. +# This replaces the old "password" option to avoid confusion. "password" +# remains valid for the time being. +#remote_password + +# Set the username to use when connecting to a broker that requires +# authentication. +# This replaces the old "username" option to avoid confusion. "username" +# remains valid for the time being. +#remote_username + +# Set the amount of time a bridge using the automatic start type will wait +# until attempting to reconnect. +# This option can be configured to use a constant delay time in seconds, or to +# use a backoff mechanism based on "Decorrelated Jitter", which adds a degree +# of randomness to when the restart occurs. +# Minimum of 1, maximum of 3600 +# +# Set a constant timeout of 20 seconds: +# restart_timeout 20 +# +# Set backoff with a base (start value) of 10 seconds and a cap (upper limit) of +# 60 seconds: +# restart_timeout 10 60 +# +# Same as previous example, but wait for the connection to be stable for +# at least 30 seconds before resetting the backoff: +# restart_timeout 10 60 30 +# +# Defaults to jitter with a base of 5 and cap of 30 +#restart_timeout 5 30 + +# If the bridge has more than one address given in the address/addresses +# configuration, the round_robin option defines the behaviour of the bridge on +# a failure of the bridge connection. If round_robin is false, the default +# value, then the first address is treated as the main bridge connection. If +# the connection fails, the other secondary addresses will be attempted in +# turn. Whilst connected to a secondary bridge, the bridge will periodically +# attempt to reconnect to the main bridge until successful. +# If round_robin is true, then all addresses are treated as equals. If a +# connection fails, the next address will be tried and if successful will +# remain connected until it fails +#round_robin false + +# Set the start type of the bridge. This controls how the bridge starts and +# can be one of three types: automatic, lazy and once. Note that RSMB provides +# a fourth start type "manual" which isn't currently supported by mosquitto. +# +# "automatic" is the default start type and means that the bridge connection +# will be started automatically when the broker starts and also restarted +# after a short delay (30 seconds) if the connection fails. +# +# Bridges using the "lazy" start type will be started automatically when the +# number of queued messages exceeds the number set with the "threshold" +# parameter. It will be stopped automatically after the time set by the +# "idle_timeout" parameter. Use this start type if you wish the connection to +# only be active when it is needed. +# +# A bridge using the "once" start type will be started automatically when the +# broker starts but will not be restarted if the connection fails. +#start_type automatic + +# Set the number of messages that need to be queued for a bridge with lazy +# start type to be restarted. Defaults to 10 messages. +# Must be less than max_queued_messages. +#threshold 10 + +# If try_private is set to true, the bridge will attempt to indicate to the +# remote broker that it is a bridge not an ordinary client. If successful, this +# means that loop detection will be more effective and that retained messages +# will be propagated correctly. Not all brokers support this feature so it may +# be necessary to set try_private to false if your bridge does not connect +# properly. +#try_private true + +# Some MQTT brokers do not allow retained messages. MQTT v5 gives a mechanism +# for brokers to tell clients that they do not support retained messages, but +# this is not possible for MQTT v3.1.1 or v3.1. If you need to bridge to a +# v3.1.1 or v3.1 broker that does not support retained messages, set the +# bridge_outgoing_retain option to false. This will remove the retain bit on +# all outgoing messages to that bridge, regardless of any other setting. +#bridge_outgoing_retain true + +# If you wish to restrict the size of messages sent to a remote bridge, use the +# bridge_max_packet_size option. This sets the maximum number of bytes for +# the total message, including headers and payload. +# Note that MQTT v5 brokers may provide their own maximum-packet-size property. +# In this case, the smaller of the two limits will be used. +# Set to 0 for "unlimited". +#bridge_max_packet_size 0 + +# If the bridge is using MQTT v5, this option sets the maximum number of topic +# aliases that the bridge will allow the remote broker to configure. Defaults +# to 10, maximum of 65535. Set to 0 to disable incoming topic aliases +# completely. +#bridge_max_topic_alias 10 + +# If you change bridge options in the configuration file, those configuration +# changes are applied during a bridge reconnection. The bridge_reload_type +# option determines when that reconnection happens, and can be set to either +# lazy or immediate. +# +# lazy is the default, and means that any connected bridge will remain in its +# current state until a natural reconnection happens, at which point the new +# configuration is used. +# +# immediate forces a reconnection and so uses the new configuration straight +# away. +#bridge_reload_type lazy + +# When publishing to MQTT v5 clients, Mosquitto can create topic aliases on a +# first come first serve basis, i.e. the topics that are published to a client +# first have aliases created. The max_topic_alias_broker option controls the +# number of aliases per client. It applies per listener. +# +# Note that this behaviour is not guaranteed to remain the same. It is possible +# that future versions introduce mechanisms for controlling which topics +# receive aliases. +# +# This option sets the maximum number topic aliases that Mosquitto will create +# for an MQTT v5 client, even if the client allows more. +# +# For example, if the client sets topic-alias-maximum to 100 and this option is +# set to 10, the broker will create at most 10 topic aliases. Likewise, if the +# client sets topic-alias-maximum to 20 and this option is set to 100, then the +# broker will create at most 20 topic aliases. +# +# Defaults to 10. Maximum of 65535. Set to 0 to disable broker to client topic +# aliases completely. +#max_topic_alias_broker 10 + +# The max_topic_alias option sets the maximum number topic aliases that an MQTT +# v5 client is allowed to create. This option applies per listener. Defaults to +# 10. Set to 0 to disallow topic aliases from clients. The maximum value +# possible is 65535. +#max_topic_alias 10 + + +# ----------------------------------------------------------------- +# Certificate based SSL/TLS support +# ----------------------------------------------------------------- +# To enable TLS support, the bridge must be configured to trust some +# certificate authority certificates. This can be done in three ways, by +# defining at least one of bridge_cafile, bridge_capath, or +# bridge_tls_use_os_certs. + +# Use bridge_cafile or bridge_capath to explicitly choose which certificates to +# trust for this bridge. +# bridge_cafile defines the path to a file containing the +# Certificate Authority certificates that have signed the remote broker +# certificate. +# bridge_capath defines a directory that will be searched for files containing +# the CA certificates. For bridge_capath to work correctly, the certificate +# files must have ".crt" as the file ending and you must run "openssl rehash +# " each time you add/remove a certificate. +#bridge_cafile +#bridge_capath + +# Set bridge_tls_use_os_certs to true (default is false) to configure this +# bridge to use the default certificates as configured in openssl. +#bridge_tls_use_os_certs false + + +# If the remote broker has more than one protocol available on its port, e.g. +# MQTT and WebSockets, then use bridge_alpn to configure which protocol is +# requested. Note that WebSockets support for bridges is not yet available. +#bridge_alpn + +# Require the use of Online Certificate Status Protocol (OCSP) for this bridge +#bridge_require_ocsp false + +# When using certificate based encryption, bridge_insecure disables +# verification of the server hostname in the server certificate. This can be +# useful when testing initial server configurations, but makes it possible for +# a malicious third party to impersonate your server through DNS spoofing, for +# example. Use this option in testing only. If you need to resort to using this +# option in a production environment, your setup is at fault and there is no +# point using encryption. +#bridge_insecure false + +# Path to the PEM encoded client certificate, if required by the remote broker. +#bridge_certfile + +# Path to the PEM encoded client private key, if required by the remote broker. +#bridge_keyfile + +# Configure the version of the TLS protocol to be used for this bridge. +# Possible values are tlsv1.3 and tlsv1.2. Defaults to tlsv1.2. +# The remote broker must support the same version of TLS for the connection to succeed. +#bridge_tls_version + +# If you wish to control which encryption ciphers are used, use the ciphers +# option. The list of available ciphers can be optained using the "openssl +# ciphers" command and should be provided in the same format as the output of +# that command. This applies to TLS 1.2 and earlier versions only. Use +# bridge_ciphers_tls1.3 for TLS v1.3. +#bridge_ciphers + +# Choose which TLS v1.3 ciphersuites are used for this bridge. +# Defaults to "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" +#bridge_ciphers_tls1.3 + +# ----------------------------------------------------------------- +# PSK based SSL/TLS support +# ----------------------------------------------------------------- +# Pre-shared-key encryption provides an alternative to certificate based +# encryption. A bridge can be configured to use PSK with the bridge_identity +# and bridge_psk options. These are the client PSK identity, and pre-shared-key +# in hexadecimal format with no "0x". Only one of certificate and PSK based +# encryption can be used on one +# bridge at once. +#bridge_identity +#bridge_psk + + +# ================================================================= +# Broker control +# ================================================================= + +# Enable the $CONTROL/broker/# topic API +#enable_control_api false + +# ================================================================= +# External config files +# ================================================================= + +# External configuration files may be included by using the +# include_dir option. This defines a directory that will be searched +# for config files. All files that end in '.conf' will be loaded as +# a configuration file. It is best to have this as the last option +# in the main file. This option will only be processed from the main +# configuration file. The directory specified must not contain the +# main configuration file. +# Files within include_dir will be loaded sorted in case-sensitive +# alphabetical order, with capital letters ordered first. If this option is +# given multiple times, all of the files from the first instance will be +# processed before the next instance. See the man page for examples. +#include_dir diff --git a/dm-wolfssl-ota-client-with-zephyr/mqttBroker/dockerfile b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/dockerfile new file mode 100644 index 0000000..4e8394c --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/mqttBroker/dockerfile @@ -0,0 +1,6 @@ +FROM eclipse-mosquitto:2 + +COPY config/mosquitto.conf /mosquitto/config/mosquitto.conf +COPY certs/ /mosquitto/certs/ + +EXPOSE 1883 8883 \ No newline at end of file diff --git a/dm-wolfssl-ota-client-with-zephyr/prj.conf b/dm-wolfssl-ota-client-with-zephyr/prj.conf new file mode 100644 index 0000000..16711e8 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/prj.conf @@ -0,0 +1,89 @@ +CONFIG_UART_CONSOLE=y + +# Link and flash the application into the board-defined code partition +# (slot0_partition at 0x10010000 on FRDM-MCXN947). +CONFIG_USE_DT_CODE_PARTITION=y + +# Reserve 0x200 bytes at the start of the code partition for an image header. +CONFIG_ROM_START_OFFSET=0x200 + +# Linker base remap: use alias addresses instead of physical bus addresses. +CONFIG_FLASH_BASE_ADDRESS=0x0 +CONFIG_SRAM_BASE_ADDRESS=0x20016000 + +# Clock for time() +CONFIG_CLOCK_CONTROL=y + +# Log settings. Using immediate mode to not drop bursty logs +CONFIG_LOG=y +CONFIG_LOG_PRINTK=n + +# Enable float support for printf/printk +CONFIG_CBPRINTF_FP_SUPPORT=y + +CONFIG_MAIN_STACK_SIZE=32768 + +CONFIG_ASSERT=y + +# POSIX Settings +CONFIG_POSIX_API=y + +# Network settings +CONFIG_NETWORKING=y +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_AUTO_INIT=n +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_DHCPV4=n +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_IPV6=n +CONFIG_NET_CONFIG_NEED_IPV6=n +CONFIG_NET_IPV4=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_TCP=y +CONFIG_DNS_RESOLVER=y + +# Disable Flash API as the flash operations are handled by wolfBoot NSC APIs. +CONFIG_FLASH=n +CONFIG_SOC_FLASH_MCUX=n +# TLS configuration + +CONFIG_WOLFSSL=y +CONFIG_WOLFSSL_BUILTIN=y +CONFIG_WOLFSSL_DEBUG=n +CONFIG_WOLFSSL_TLS_VERSION_1_2=y +CONFIG_WOLFSSL_TLS_VERSION_1_3=y + +# MQTT configuration +CONFIG_WOLFMQTT=y +CONFIG_WOLFMQTT_TLS=y +# Enable wolfMQTT user settings to adjust the configuration as same as wolfSSL +CONFIG_WOLFMQTT_SETTINGS_FILE="user_settings.h" + +# CONFIG_NET_BUF_DATA_SIZE=512 +CONFIG_NET_BUF_DATA_SIZE=1536 + +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y + +#ETHERNET +CONFIG_ETH_NXP_ENET_QOS=y +CONFIG_ETH_NXP_ENET_QOS_MAC=y + +CONFIG_WOLFSSL_SETTINGS_FILE="user_settings.h" + +# For debug +CONFIG_DEBUG_OPTIMIZATIONS=n + +CONFIG_REBOOT=y + +# TrustZone settings +# Build this Zephyr image for Non-Secure state execution. +CONFIG_TRUSTED_EXECUTION_SECURE=n +CONFIG_TRUSTED_EXECUTION_NONSECURE=y + +# Early SoC init hook: FPU/PowerQuad coprocessor access, ECC, flash cache, +# and aGDET setup for NS world (replaces SystemInit() which now skips NS). +CONFIG_SOC_EARLY_INIT_HOOK=y + +# NS board defconfig enables TF-M; this project uses wolfBoot on the secure side. +CONFIG_BUILD_WITH_TFM=n \ No newline at end of file diff --git a/dm-wolfssl-ota-client-with-zephyr/sample.yaml b/dm-wolfssl-ota-client-with-zephyr/sample.yaml new file mode 100644 index 0000000..653cbc1 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/sample.yaml @@ -0,0 +1,10 @@ +sample: + description: An OTA client that uses the ethernet available for the FRDM-MCXN947 + name: OTA Client with wolfSSL and Zephyr +tests: + sample.basic.wolfssl: + tags: + - Ethernet + - Networking + - TLS 1.3 + - OTA diff --git a/dm-wolfssl-ota-client-with-zephyr/src/main.c b/dm-wolfssl-ota-client-with-zephyr/src/main.c new file mode 100644 index 0000000..7cccbca --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/main.c @@ -0,0 +1,386 @@ +/* main.c + * + * Copyright (C) 2006-2024 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* wolfSSL Includes Start */ +#include "user_settings.h" /* For wolfSSL Zephyr configuration */ +#include +#include /* Basic functionality for TLS */ +#include /* Needed for Cert Buffers */ +#include +/* wolfSSL Includes End */ + +#ifdef WOLFCRYPT_TEST +#include +#endif + +#ifdef WOLFCRYPT_BENCHMARK +#include +#endif + +/* Standard Packages Start */ +#include +#include +#include +/* Standard Packages End */ + +/* Zephyr Includes Start */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* Zephyr Includes End */ + +/* mqttClient Includes Start */ +#include "mqttClient/fwclient.h" +/* mqttClient Includes End */ + +/* wolfBoot Includes Start */ +#include "wolfboot/wolfboot.h" +#include "wolfboot_status.h" +/* wolfBoot Includes End */ + +volatile boot_state_t g_boot_state = BOOT_STATE_UNKNOWN; +volatile uint32_t g_boot_version = 0; +volatile uint32_t g_update_version = 0; +volatile uint8_t g_boot_state_byte = 0xAA; +volatile int g_boot_state_rc = -1; +volatile uint8_t g_update_state_byte = 0xAA; +volatile int g_update_state_rc = -1; + +/* Program Defines Start */ + +#define DEFAULT_PORT 11111 /* Define the port we want to use for the network */ + +#define LOCAL_DEBUG 0 /* Use for wolfSSL's internal Debugging */ + +/* Use DHCP auto IP assignment or static assignment. + * Follows CONFIG_NET_DHCPV4 from prj.conf so both stay in sync. */ +#undef DHCP_ON +#ifdef CONFIG_NET_DHCPV4 + #define DHCP_ON 1 +#else + #define DHCP_ON 0 +#endif + +#if DHCP_ON == 0 +/* Define Static IP, Gateway, and Netmask */ + #define STATIC_IPV4_ADDR "192.168.1.70" + #define STATIC_IPV4_GATEWAY "192.168.1.1" + #define STATIC_IPV4_NETMASK "255.255.255.0" +#endif + +/* Set the TLS Version. Currently only 2 or 3 is available for this */ +/* application, defaults to TLSv3 */ +#undef TLS_VERSION +#define TLS_VERSION 3 + +/* This sets up the correct function for the application via macros */ +#undef TLS_METHOD +#if TLS_VERSION == 3 + #define TLS_METHOD wolfTLSv1_3_server_method() +#elif TLS_VERSION == 2 + #define TLS_METHOD wolfTLSv1_2_server_method() +#else + #define TLS_METHOD wolfTLSv1_3_server_method() +#endif + +/* Set up the network using the Zephyr network stack */ +int startNetwork() { + + struct net_if *iface = net_if_get_default(); + char buf[NET_IPV4_ADDR_LEN]; + + #if DHCP_ON == 0 + struct in_addr addr, netmask, gw; + #endif + + if (!(iface)) { /* See if a network interface (ethernet) is available */ + printf("No network interface determined"); + return 1; + } + + if (net_if_flag_is_set(iface, NET_IF_DORMANT)) { + printf("Waiting on network interface to be available"); + while(!net_if_is_up(iface)){ + k_sleep(K_MSEC(100)); + } + } + + #if DHCP_ON == 1 + printf("\nStarting DHCP to obtain IP address\n"); + net_dhcpv4_start(iface); + (void)net_mgmt_event_wait_on_iface(iface, NET_EVENT_IPV4_DHCP_BOUND, \ + NULL, NULL, NULL, K_FOREVER); + #elif DHCP_ON == 0 + /* Static IP Configuration */ + if (net_addr_pton(AF_INET, STATIC_IPV4_ADDR, &addr) < 0 || + net_addr_pton(AF_INET, STATIC_IPV4_NETMASK, &netmask) < 0 || + net_addr_pton(AF_INET, STATIC_IPV4_GATEWAY, &gw) < 0) { + printf("Invalid IP address settings.\n"); + return -1; + } + net_if_ipv4_set_netmask_by_addr(iface, &addr, &netmask); + net_if_ipv4_set_gw(iface, &gw); + net_if_ipv4_addr_add(iface, &addr, NET_ADDR_MANUAL, 0); + + #else + #error "Please set DHCP_ON to true (1) or false (0), if unsure set to true (1)" + #endif + + /* Display IP address that was assigned when done */ + printf("IP Address is: %s", net_addr_ntop(AF_INET, \ + &iface->config.ip.ipv4->unicast[0].ipv4.address.in_addr, \ + buf, sizeof(buf))); + + return 0; +} + +/* Initialize Server for a client connection */ +int startServer(void) { + int sockfd = SOCKET_INVALID; + int connd = SOCKET_INVALID; + struct sockaddr_in servAddr; + struct sockaddr_in clientAddr; + socklen_t size = sizeof(clientAddr); + char buff[256]; + size_t len; + int shutdown = 0; + int ret; + const char* reply = "I hear ya fa shizzle!\n"; + + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_CIPHER* cipher; + + #if LOCAL_DEBUG + wolfSSL_Debugging_ON(); + #endif + + wolfSSL_Init(); + + /* Create a socket that uses an internet IPv4 address, + * Sets the socket to be stream based (TCP), + * 0 means choose the default protocol. */ + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + printf("\nERROR: failed to create the socket\n"); + return 1; + } + + ctx = wolfSSL_CTX_new(TLS_METHOD); + if (ctx == NULL) { + printf("\nERROR: Failed to create WOLFSSL_CTX\n"); + return 1; + } + + if (wolfSSL_CTX_use_certificate_chain_buffer_format(ctx, + server_cert_der_2048, sizeof_server_cert_der_2048, + WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS){ + printf("\nERROR: Cannot load server cert buffer\n"); + return 1; + } + + if (wolfSSL_CTX_use_PrivateKey_buffer(ctx, server_key_der_2048, + sizeof_server_key_der_2048, SSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS){ + printf("\nERROR: Can't load server private key buffer"); + return 1; + } + + /* Initialize the server address struct with zeros */ + memset(&servAddr, 0, sizeof(servAddr)); + + /* Fill in the server address */ + servAddr.sin_family = AF_INET; /* using IPv4 */ + servAddr.sin_port = htons(DEFAULT_PORT); /* on DEFAULT_PORT */ + servAddr.sin_addr.s_addr = INADDR_ANY; /* from anywhere */ + + /* Bind the server socket to our port */ + if (bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) { + printf("\nERROR: failed to bind\n"); + return 1; + } + + /* Listen for a new connection, allow 5 pending connections */ + if (listen(sockfd, 5) == -1) { + printf("\nERROR: failed to listen\n"); + return 1; + } + + printf("\nServer Started\n"); + + /* Continue to accept clients until shutdown is issued */ + while (!shutdown) { + printf("Waiting for a connection...\n"); + + /* Accept client connections */ + if ((connd = accept(sockfd, (struct sockaddr*)&clientAddr, &size)) + == -1) { + printf("\nERROR: failed to accept the connection\n"); + return 1; + } + + /* Create a WOLFSSL object */ + if ((ssl = wolfSSL_new(ctx)) == NULL) { + printf("\nERROR: failed to create WOLFSSL object\n"); + return 1; + } + + /* Attach wolfSSL to the socket */ + wolfSSL_set_fd(ssl, connd); + + /* Establish TLS connection */ + ret = wolfSSL_accept(ssl); + if (ret != WOLFSSL_SUCCESS) { + printf("\nERROR: wolfSSL_accept error = %d\n", + wolfSSL_get_error(ssl, ret)); + return 1; + } + + + printf("Client connected successfully\n"); + + cipher = wolfSSL_get_current_cipher(ssl); + printf("SSL cipher suite is %s\n", wolfSSL_CIPHER_get_name(cipher)); + + + /* Read the client data into our buff array */ + memset(buff, 0, sizeof(buff)); + if ((ret = wolfSSL_read(ssl, buff, sizeof(buff)-1)) == -1) { + printf("\nERROR: failed to read\n"); + return 1; + } + + /* Print to stdout any data the client sends */ + printf("Client: %s\n", buff); + + /* Check for server shutdown command */ + if (strncmp(buff, "shutdown", 8) == 0) { + printf("Shutdown command issued!\n"); + shutdown = 1; + } + + + + /* Write our reply into buff */ + memset(buff, 0, sizeof(buff)); + memcpy(buff, reply, strlen(reply)); + len = strnlen(buff, sizeof(buff)); + + /* Reply back to the client */ + if ((ret = wolfSSL_write(ssl, buff, len)) != len) { + printf("\nERROR: failed to write\n"); + return 1; + } + + /* Notify the client that the connection is ending */ + wolfSSL_shutdown(ssl); + printf("Shutdown complete\n"); + + /* Cleanup after this connection */ + wolfSSL_free(ssl); /* Free the wolfSSL object */ + ssl = NULL; + close(connd); /* Close the connection to the client */ + } + + return 0; +} + + + +int main(void) +{ + { + uint8_t bst = 0xAA, ust = 0xAA; + int brc = wolfBoot_nsc_get_partition_state(PART_BOOT, &bst); + int urc = wolfBoot_nsc_get_partition_state(PART_UPDATE, &ust); + uint32_t bv = wolfBoot_nsc_get_image_version(PART_BOOT); + uint32_t uv = wolfBoot_nsc_get_image_version(PART_UPDATE); + boot_state_t cls = boot_state_classify(bv, uv, bst, brc, ust, urc); + + g_boot_version = bv; + g_update_version = uv; + g_boot_state_byte = bst; + g_boot_state_rc = brc; + g_update_state_byte = ust; + g_update_state_rc = urc; + g_boot_state = cls; + + printf("[wolfBoot] BOOT v=%u state=0x%02x(rc=%d) UPDATE v=%u state=0x%02x(rc=%d) -> %s\n", + (unsigned)bv, (unsigned)bst, brc, + (unsigned)uv, (unsigned)ust, urc, + boot_state_name(cls)); + + if (cls == BOOT_STATE_AWAITING_CONFIRM) { + printf("[wolfBoot] running new image, will call success() after self-test\n"); + /* Demo: confirm immediately. Real apps should self-test first. */ + wolfBoot_nsc_success(); + g_boot_state = BOOT_STATE_NORMAL; + printf("[wolfBoot] success() called -> state confirmed\n"); + } + } + + /* Start up the network */ + if (startNetwork() != 0){ + printf("Network Initialization via DHCP Failed"); + return 1; + } + +#ifdef WOLFCRYPT_TEST + printf("\nRunning wolfCrypt test\n"); + wolfcrypt_test(NULL); +#endif + +#ifdef WOLFCRYPT_BENCHMARK + printf("\nRunning wolfCrypt benchmark\n"); + benchmark_test(NULL); +#endif + +#if defined(CONFIG_WOLFMQTT) + printf("\nRunning wolfMQTT firmware client for OTA\n"); + if (fwclient_main() != 0) { + printf("Firmware client has Failed!"); + return 1; + } else { + unsigned int irq_key; + + printf("Firmware client completed successfully!\n"); + + k_sched_lock(); + irq_key = irq_lock(); + printf("Triggering wolfBoot update\n"); + wolfBoot_nsc_update_trigger(); + irq_unlock(irq_key); + k_sched_unlock(); + sys_reboot(SYS_REBOOT_COLD); + } +#else + if (startServer() != 0){ + printf("Server has Failed!"); + return 1; + } +#endif + return 0; +} \ No newline at end of file diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/firmware.h b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/firmware.h new file mode 100644 index 0000000..2639922 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/firmware.h @@ -0,0 +1,56 @@ +/* firmware.h + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_FIRMWARE_H +#define WOLFMQTT_FIRMWARE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define FIRMWARE_TOPIC_NAME "wolfMQTT/example/firmware" +#define COMMAND_TOPIC_NAME "wolfMQTT/example/command" +#define FIRMWARE_MAX_BUFFER 2048 +#define FIRMWARE_MAX_PACKET (int)(FIRMWARE_MAX_BUFFER + sizeof(MqttPacket) + XSTRLEN(FIRMWARE_TOPIC_NAME) + MQTT_DATA_LEN_SIZE) +#define FIRMWARE_MQTT_QOS MQTT_QOS_2 + +typedef struct messageHeader { + word16 chunkNumber; + word16 chunkSize; + word32 totalLen; +} WOLFMQTT_PACK MessageHeader; + +typedef struct commandHeader { + word16 commandId; + word16 commandLen; +} WOLFMQTT_PACK CommandHeader; + +enum CommandIds { + COMMAND_ID_REBOOT = 1, + COMMAND_ID_ERASE = 2, +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFMQTT_FIRMWARE_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/fwclient.c b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/fwclient.c new file mode 100644 index 0000000..62e294e --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/fwclient.c @@ -0,0 +1,955 @@ +/* fwclient.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_client.h" + +/* This example only works with ENABLE_MQTT_TLS (wolfSSL library). */ +#if defined(ENABLE_MQTT_TLS) + #if !defined(WOLFSSL_USER_SETTINGS) && !defined(USE_WINDOWS_API) + #include + #endif + #include + #include + + /* Signature wrapper required; added in wolfSSL after 3.7.1. + * app and libwolfssl use wolfssl-setting/user_settings.h (no NO_SIG_WRAPPER). */ + #if defined(LIBWOLFSSL_VERSION_HEX) && LIBWOLFSSL_VERSION_HEX > 0x03007001 \ + && defined(HAVE_ECC) + #undef ENABLE_FIRMWARE_SIG + #define ENABLE_FIRMWARE_SIG + #endif +#endif + + +#if defined(ENABLE_FIRMWARE_SIG) +#include +#include +#include +#include +#endif + +#include "fwclient.h" +#include "firmware.h" +#include "mqttexample.h" +#include "mqttnet.h" + +#if defined(WOLFMQTT_ZEPHYR) +#include +#include +#include "wolfboot/wolfboot.h" +#include "wolfboot_status.h" +#define SLOT1_NODE DT_NODELABEL(slot1_partition) +#define SLOT1_OFFSET DT_REG_ADDR(SLOT1_NODE) +#define SLOT1_SIZE DT_REG_SIZE(SLOT1_NODE) +#define SLOT1_WRITE_BLOCK_SIZE DT_PROP(DT_CHOSEN(zephyr_flash), write_block_size) +/* The MCXN947 ROM API flash_program requires 512-byte aligned start address + * (kFLASH_AlignementUnitProgram = 512). CONFIG_FLASH_FILL_BUFFER_SIZE is set + * to 16 by Zephyr when CONFIG_FLASH=n, which is too small for the ROM API. */ +#define WOLFBOOT_FLASH_PROGRAM_ALIGN 512U +#if defined(CONFIG_FLASH_FILL_BUFFER_SIZE) && \ + (CONFIG_FLASH_FILL_BUFFER_SIZE >= WOLFBOOT_FLASH_PROGRAM_ALIGN) + #define FLASH_WRITE_BLOCK_MAX CONFIG_FLASH_FILL_BUFFER_SIZE +#else + #define FLASH_WRITE_BLOCK_MAX WOLFBOOT_FLASH_PROGRAM_ALIGN +#endif +#endif + +/* Configuration */ +#ifndef MAX_BUFFER_SIZE +#define MAX_BUFFER_SIZE FIRMWARE_MAX_PACKET +#endif + +/* Locals */ +static int mStopRead = 0; +static int mTestDone = 0; +static byte mMsgBuf[FIRMWARE_MAX_BUFFER] __attribute__((aligned(16))); +static int mSubCommandComplete = 0; +static byte mMsgIsCommand = 0; + +static void fw_transfer_reset(void); +static int fwfile_save(const byte* fileBuf, int fileLen, word32 flash_offset); + +static int fw_topic_matches(const MqttMessage* msg, const char* topic) +{ + word32 topic_len; + + if (msg == NULL || topic == NULL) { + return 0; + } + + topic_len = (word32)XSTRLEN(topic); + if (msg->topic_name_len != topic_len) { + return 0; + } + + return (XSTRNCMP(msg->topic_name, topic, topic_len) == 0); +} + +static int fw_command_process(const byte* buffer, word32 len) +{ + const CommandHeader* command; + + if (buffer == NULL || len < sizeof(CommandHeader)) { + PRINTF("Command message too small: %u", len); + return EXIT_FAILURE; + } + + command = (const CommandHeader*)buffer; + + switch (command->commandId) { + case COMMAND_ID_ERASE: + { +#if defined(WOLFMQTT_ZEPHYR) + int rc; + unsigned int irq_key; + + PRINTF("Erase command received. Erasing update partition..."); + PRINTF("OTA slot1 layout: offset=0x%08x size=0x%08x write_block=%u", + (unsigned int)SLOT1_OFFSET, + (unsigned int)SLOT1_SIZE, + (unsigned int)SLOT1_WRITE_BLOCK_SIZE); + PRINTF("Erase request: rel_off=0x%08x len=0x%08x abs_start=0x%08x", + 0U, + (unsigned int)SLOT1_SIZE, + (unsigned int)SLOT1_OFFSET); + fw_transfer_reset(); + + k_sched_lock(); + irq_key = irq_lock(); + rc = wolfBoot_nsc_erase_update(0U, (uint32_t)SLOT1_SIZE); + irq_unlock(irq_key); + k_sched_unlock(); + + if (rc != 0) { + PRINTF("Flash erase failed! rc=%d (slot1_off=0x%08x slot1_size=0x%08x)", + rc, + (unsigned int)SLOT1_OFFSET, + (unsigned int)SLOT1_SIZE); + return EXIT_FAILURE; + } + PRINTF("Update partition erased"); +#else + PRINTF("Erase command received (ignored on this target)"); +#endif + break; + } + + default: + PRINTF("Unknown command id: %u", command->commandId); + break; + } + + return 0; +} + +typedef struct FwClientTransfer_s { + word32 total_len; + word32 bytes_written; + word16 expected_chunk; + int active; +#if !defined(WOLFMQTT_ZEPHYR) + FILE* fp; +#else + word32 flash_written; + word32 write_block_size; + word32 pending_len; + byte pending[FLASH_WRITE_BLOCK_MAX]; +#endif +} FwClientTransfer; + +static FwClientTransfer mTransfer; + +static void fw_transfer_reset(void) +{ +#if !defined(WOLFMQTT_ZEPHYR) + if (mTransfer.fp != NULL) { + fclose(mTransfer.fp); + mTransfer.fp = NULL; + } +#endif + XMEMSET(&mTransfer, 0, sizeof(mTransfer)); +} + +static int fw_transfer_begin(MQTTCtx* mqttCtx, word32 total_len) +{ + if (mqttCtx == NULL || total_len == 0) { + return EXIT_FAILURE; + } + + fw_transfer_reset(); + mTransfer.total_len = total_len; + mTransfer.active = 1; + +#if !defined(WOLFMQTT_ZEPHYR) + mTransfer.fp = fopen(mqttCtx->pub_file, "wb"); + if (mTransfer.fp == NULL) { + PRINTF("File %s open error", mqttCtx->pub_file); + fw_transfer_reset(); + return EXIT_FAILURE; + } +#else + int rc = EXIT_SUCCESS; + + if (total_len > SLOT1_SIZE) { + PRINTF("Firmware image too large for slot1! len=%u slot=%u", + total_len, (unsigned int)SLOT1_SIZE); + fw_transfer_reset(); + return EXIT_FAILURE; + } + + if (rc == EXIT_SUCCESS) { + mTransfer.write_block_size = FLASH_WRITE_BLOCK_MAX; + mTransfer.flash_written = 0; + mTransfer.pending_len = 0; + + if (mTransfer.write_block_size == 0 || + mTransfer.write_block_size > FLASH_WRITE_BLOCK_MAX) { + PRINTF("Unsupported flash write block size: %u", mTransfer.write_block_size); + rc = EXIT_FAILURE; + } + } + + if (rc != EXIT_SUCCESS) { + fw_transfer_reset(); + return EXIT_FAILURE; + } +#endif + + PRINTF("Firmware transfer started: total %u bytes", total_len); + return 0; +} + +static int fw_transfer_write_chunk(const byte* chunk_data, word16 chunk_len) +{ +#if !defined(WOLFMQTT_ZEPHYR) + int written; +#else + int rc = 0; + const byte* p = chunk_data; + word32 remaining = chunk_len; + word32 copy_len; + word32 direct_len; +#endif + + + if (!mTransfer.active || chunk_data == NULL || chunk_len == 0) { + return EXIT_FAILURE; + } + + if (((word32)chunk_len > mTransfer.total_len) || + (mTransfer.bytes_written > (mTransfer.total_len - (word32)chunk_len))) { + PRINTF("Chunk exceeds expected total length"); + return EXIT_FAILURE; + } + +#if !defined(WOLFMQTT_ZEPHYR) + written = (int)fwrite(chunk_data, 1, chunk_len, mTransfer.fp); + if (written != chunk_len) { + PRINTF("Chunk file write error: %d", written); + return EXIT_FAILURE; + } +#else + if (mTransfer.pending_len > 0) { + copy_len = mTransfer.write_block_size - mTransfer.pending_len; + if (copy_len > remaining) { + copy_len = remaining; + } + + XMEMCPY(&mTransfer.pending[mTransfer.pending_len], p, copy_len); + mTransfer.pending_len += copy_len; + p += copy_len; + remaining -= copy_len; + + if (mTransfer.pending_len == mTransfer.write_block_size) { + rc = fwfile_save(mTransfer.pending, (int)mTransfer.pending_len, + mTransfer.flash_written); + if (rc != 0) { + PRINTF("Chunk file save error: %d", rc); + return EXIT_FAILURE; + } + mTransfer.flash_written += mTransfer.pending_len; + mTransfer.pending_len = 0; + } + } + + direct_len = remaining - (remaining % mTransfer.write_block_size); + if (direct_len > 0) { + rc = fwfile_save(p, (int)direct_len, mTransfer.flash_written); + if (rc != 0) { + PRINTF("Chunk file save error: %d", rc); + return EXIT_FAILURE; + } + mTransfer.flash_written += direct_len; + p += direct_len; + remaining -= direct_len; + } + + if (remaining > 0) { + XMEMCPY(mTransfer.pending, p, remaining); + mTransfer.pending_len = remaining; + } +#endif + + mTransfer.bytes_written += chunk_len; + + return 0; +} + +static int fw_transfer_finish(MQTTCtx* mqttCtx) +{ + int rc = 0; + + if (mqttCtx == NULL) { + return EXIT_FAILURE; + } + +#if defined(WOLFMQTT_ZEPHYR) + if (mTransfer.pending_len > 0) { + byte aligned_buf[FLASH_WRITE_BLOCK_MAX]; + + XMEMSET(aligned_buf, 0xFF, mTransfer.write_block_size); + XMEMCPY(aligned_buf, mTransfer.pending, mTransfer.pending_len); + + rc = fwfile_save(aligned_buf, (int)mTransfer.write_block_size, + mTransfer.flash_written); + if (rc != 0) { + PRINTF("Final chunk flush error: %d", rc); + fw_transfer_reset(); + return EXIT_FAILURE; + } + mTransfer.flash_written += mTransfer.write_block_size; + mTransfer.pending_len = 0; + } +#endif + + PRINTF("Firmware transfer complete: %u bytes", mTransfer.bytes_written); + fw_transfer_reset(); + + if (mqttCtx->test_mode) { + mTestDone = 1; + } else { + mStopRead = 1; + } + + return 0; +} + + +static int fwfile_save(const byte* fileBuf, int fileLen, word32 flash_offset) +{ + int rc = EXIT_SUCCESS; +#if defined(WOLFMQTT_ZEPHYR) + word32 write_block_size = mTransfer.write_block_size; + unsigned int irq_key; + + if (fileBuf == NULL || fileLen <= 0) { + PRINTF("Invalid firmware file buffer or length!"); + rc = EXIT_FAILURE; + } + + if (rc == EXIT_SUCCESS && + ((word32)fileLen > SLOT1_SIZE || flash_offset > (SLOT1_SIZE - (word32)fileLen))) { + PRINTF("Firmware chunk write exceeds slot1: off=%u len=%d slot=%u", + flash_offset, fileLen, (unsigned int)SLOT1_SIZE); + rc = EXIT_FAILURE; + } + + if (rc == EXIT_SUCCESS && + ((flash_offset % write_block_size) != 0 || + ((word32)fileLen % write_block_size) != 0)) { + PRINTF("Flash write alignment error: off=%u len=%d block=%u", + flash_offset, fileLen, (unsigned int)write_block_size); + rc = EXIT_FAILURE; + } + + if (rc == EXIT_SUCCESS) { + k_sched_lock(); + irq_key = irq_lock(); + + /* Write firmware file to flash through HAL */ + rc = wolfBoot_nsc_write_update((uint32_t)(flash_offset), + (const uint8_t*)fileBuf, fileLen); + + irq_unlock(irq_key); + k_sched_unlock(); + if (rc != 0) { + PRINTF("Flash write failed! rc=%d rel_off=0x%08x len=0x%08x abs=0x%08x", + rc, + (unsigned int)flash_offset, + (unsigned int)fileLen, + (unsigned int)(SLOT1_OFFSET + flash_offset)); + } + } + + if (rc == EXIT_SUCCESS) { + PRINTF("Firmware File Saved to Flash: Len=%d", fileLen); + } +#else + PRINTF("Firmware File Save: Len=%d (No Filesystem)", fileLen); +#endif + return rc; +} + +static int fw_message_process(MQTTCtx *mqttCtx, const byte* buffer, word32 len) +{ + const MessageHeader* header; + const byte* payload; + word32 payload_len; + + if (mqttCtx == NULL || buffer == NULL) { + return EXIT_FAILURE; + } + + if (len < sizeof(MessageHeader)) { + PRINTF("Chunk too small: %u", len); + return EXIT_FAILURE; + } + + header = (const MessageHeader*)buffer; + payload = buffer + sizeof(MessageHeader); + payload_len = len - sizeof(MessageHeader); + + if (header->chunkSize != payload_len) { + PRINTF("Chunk size mismatch: header %u, payload %u", + header->chunkSize, payload_len); + return EXIT_FAILURE; + } + + if (header->totalLen == 0) { + PRINTF("Invalid total length 0"); + return EXIT_FAILURE; + } + + if (header->chunkNumber == 0) { + if (fw_transfer_begin(mqttCtx, header->totalLen) != 0) { + return EXIT_FAILURE; + } + } + + if (!mTransfer.active) { + PRINTF("Received chunk without active transfer"); + return EXIT_FAILURE; + } + + if (header->totalLen != mTransfer.total_len) { + PRINTF("Transfer total length changed: %u -> %u", + mTransfer.total_len, header->totalLen); + fw_transfer_reset(); + return EXIT_FAILURE; + } + + if (header->chunkNumber < mTransfer.expected_chunk) { + /* Duplicate chunk from retransmit; ignore if already committed. */ + PRINTF("Ignoring duplicate chunk %u", header->chunkNumber); + return 0; + } + + if (header->chunkNumber != mTransfer.expected_chunk) { + PRINTF("Out-of-order chunk: expected %u, got %u", + mTransfer.expected_chunk, header->chunkNumber); + fw_transfer_reset(); + return EXIT_FAILURE; + } + + if (fw_transfer_write_chunk(payload, header->chunkSize) != 0) { + fw_transfer_reset(); + return EXIT_FAILURE; + } + + PRINTF("Firmware chunk %u: %u bytes (%u/%u)", + header->chunkNumber, + header->chunkSize, + mTransfer.bytes_written, + mTransfer.total_len); + + mTransfer.expected_chunk++; + + if (mTransfer.bytes_written == mTransfer.total_len) { + return fw_transfer_finish(mqttCtx); + } + + return 0; +} + +static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, + byte msg_new, byte msg_done) +{ + MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx; + byte is_fw_topic; + byte is_cmd_topic; + + is_fw_topic = (byte)fw_topic_matches(msg, FIRMWARE_TOPIC_NAME); + is_cmd_topic = (byte)fw_topic_matches(msg, COMMAND_TOPIC_NAME); + + if (msg_new) { + if (!is_fw_topic && !is_cmd_topic) { + return MQTT_CODE_SUCCESS; + } + + if (msg->total_len > sizeof(mMsgBuf)) { + PRINTF("Incoming publish exceeds firmware message buffer: %u", + msg->total_len); + return MQTT_CODE_ERROR_OUT_OF_BUFFER; + } + + mMsgIsCommand = is_cmd_topic; + + if (is_cmd_topic) { + PRINTF("MQTT Command Message: Qos %d, Len %u", + msg->qos, msg->total_len); + } + else { + PRINTF("MQTT Firmware Chunk Message: Qos %d, Len %u", + msg->qos, msg->total_len); + } + } + + if (msg->buffer_pos > sizeof(mMsgBuf)) { + PRINTF("Incoming payload position exceeds message buffer"); + return MQTT_CODE_ERROR_MALFORMED_DATA; + } + + if (msg->buffer_len > (sizeof(mMsgBuf) - msg->buffer_pos)) { + PRINTF("Incoming payload chunk exceeds message buffer"); + return MQTT_CODE_ERROR_MALFORMED_DATA; + } + + XMEMCPY(&mMsgBuf[msg->buffer_pos], msg->buffer, msg->buffer_len); + + if (msg_done) { + if (mMsgIsCommand) { + if (fw_command_process(mMsgBuf, msg->total_len) != 0) { + return MQTT_CODE_ERROR_MALFORMED_DATA; + } + } + else { + if (fw_message_process(mqttCtx, mMsgBuf, msg->total_len) != 0) { + return MQTT_CODE_ERROR_MALFORMED_DATA; + } + } + + mMsgIsCommand = 0; + } + + /* Return negative to terminate publish processing */ + return MQTT_CODE_SUCCESS; +} + +int fwclient_test(MQTTCtx *mqttCtx) +{ + int rc = MQTT_CODE_SUCCESS, i; + + switch(mqttCtx->stat) { + case WMQ_BEGIN: + { + PRINTF("MQTT Firmware Client: QoS %d, Use TLS %d", mqttCtx->qos, mqttCtx->use_tls); + } + FALL_THROUGH; + + case WMQ_NET_INIT: + { + mqttCtx->stat = WMQ_NET_INIT; + + /* Initialize Network */ + rc = MqttClientNet_Init(&mqttCtx->net, mqttCtx); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Net Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* setup tx/rx buffers */ + mqttCtx->tx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + mqttCtx->rx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + } + FALL_THROUGH; + + case WMQ_INIT: + { + mqttCtx->stat = WMQ_INIT; + + /* Initialize MqttClient structure */ + rc = MqttClient_Init(&mqttCtx->client, &mqttCtx->net, + mqtt_message_cb, + mqttCtx->tx_buf, MAX_BUFFER_SIZE, + mqttCtx->rx_buf, MAX_BUFFER_SIZE, + mqttCtx->cmd_timeout_ms); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + mqttCtx->client.ctx = mqttCtx; + } + FALL_THROUGH; + + case WMQ_TCP_CONN: + { + mqttCtx->stat = WMQ_TCP_CONN; + + /* Connect to broker */ + rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host, + mqttCtx->port, DEFAULT_CON_TIMEOUT_MS, + mqttCtx->use_tls, mqtt_tls_cb); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Socket Connect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* Build connect packet */ + XMEMSET(&mqttCtx->connect, 0, sizeof(MqttConnect)); + mqttCtx->connect.keep_alive_sec = mqttCtx->keep_alive_sec; + mqttCtx->connect.clean_session = mqttCtx->clean_session; + mqttCtx->connect.client_id = mqttCtx->client_id; + if (mqttCtx->enable_lwt) { + /* Send client id in LWT payload */ + mqttCtx->lwt_msg.qos = mqttCtx->qos; + mqttCtx->lwt_msg.retain = 0; + mqttCtx->lwt_msg.topic_name = FIRMWARE_TOPIC_NAME"lwttopic"; + mqttCtx->lwt_msg.buffer = (byte*)mqttCtx->client_id; + mqttCtx->lwt_msg.total_len = (word16)XSTRLEN(mqttCtx->client_id); + } + + /* Optional authentication */ + mqttCtx->connect.username = mqttCtx->username; + mqttCtx->connect.password = mqttCtx->password; + } + FALL_THROUGH; + + case WMQ_MQTT_CONN: + { + mqttCtx->stat = WMQ_MQTT_CONN; + + /* Send Connect and wait for Connect Ack */ + rc = MqttClient_Connect(&mqttCtx->client, &mqttCtx->connect); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Connect: Proto (%s), %s (%d)", + MqttClient_GetProtocolVersionString(&mqttCtx->client), + MqttClient_ReturnCodeToString(rc), rc); + + /* Validate Connect Ack info */ + PRINTF("MQTT Connect Ack: Return Code %u, Session Present %d", + mqttCtx->connect.ack.return_code, + (mqttCtx->connect.ack.flags & MQTT_CONNECT_ACK_FLAG_SESSION_PRESENT) ? + 1 : 0 + ); + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + + mSubCommandComplete = 0; + + /* Build list of topics */ + mqttCtx->topics[0].topic_filter = COMMAND_TOPIC_NAME; + mqttCtx->topics[0].qos = mqttCtx->qos; + + /* Subscribe Topic */ + XMEMSET(&mqttCtx->subscribe, 0, sizeof(MqttSubscribe)); + mqttCtx->subscribe.packet_id = mqtt_get_packetid(); + mqttCtx->subscribe.topic_count = 1; + mqttCtx->subscribe.topics = mqttCtx->topics; + } + FALL_THROUGH; + + case WMQ_SUB: + { + mqttCtx->stat = WMQ_SUB; + + rc = MqttClient_Subscribe(&mqttCtx->client, &mqttCtx->subscribe); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Subscribe: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + for (i = 0; i < mqttCtx->subscribe.topic_count; i++) { + MqttTopic *topic = &mqttCtx->subscribe.topics[i]; + PRINTF(" Topic %s, Qos %u, Return Code %u", + topic->topic_filter, + topic->qos, + topic->return_code); + } + + if (!mSubCommandComplete) { + mSubCommandComplete = 1; + mqttCtx->topics[0].topic_filter = mqttCtx->topic_name; + mqttCtx->subscribe.packet_id = mqtt_get_packetid(); + PRINTF("MQTT Subscribing to firmware topic..."); + return MQTT_CODE_CONTINUE; + } + + /* Read Loop */ + PRINTF("MQTT Waiting for message..."); + + { + static int s_status_published = 0; + if (!s_status_published) { + static char status_buf[192]; + static MqttPublish status_pub; + int n; + int prc; + + s_status_published = 1; + n = snprintf(status_buf, sizeof(status_buf), + "{\"boot\":%u,\"update\":%u,\"state\":\"%s\"," + "\"raw\":%u,\"rc\":%d}", + (unsigned)g_boot_version, + (unsigned)g_update_version, + boot_state_name(g_boot_state), + (unsigned)g_update_state_byte, + g_update_state_rc); + XMEMSET(&status_pub, 0, sizeof(status_pub)); + status_pub.qos = MQTT_QOS_0; + status_pub.retain = 1; + status_pub.topic_name = "wolfMQTT/example/status"; + status_pub.packet_id = mqtt_get_packetid(); + status_pub.buffer = (byte *)status_buf; + status_pub.total_len = (word32)n; + prc = MqttClient_Publish(&mqttCtx->client, &status_pub); + PRINTF("MQTT Status Publish: %s (%d) -> %s", + MqttClient_ReturnCodeToString(prc), prc, + status_buf); + } + } + } + FALL_THROUGH; + + case WMQ_WAIT_MSG: + { + mqttCtx->stat = WMQ_WAIT_MSG; + + do { + /* Try and read packet */ + rc = MqttClient_WaitMessage(&mqttCtx->client, + mqttCtx->cmd_timeout_ms); + + #ifdef WOLFMQTT_NONBLOCK + /* Track elapsed time with no activity and trigger timeout */ + rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, + mqttCtx->cmd_timeout_ms/1000); + #endif + + /* check return code */ + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + + /* check for test mode */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + break; + } + + if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (mqttCtx->test_mode) { + PRINTF("Timeout in test mode, exit early!"); + mTestDone = 1; + } + /* Keep Alive */ + PRINTF("Keep-alive timeout, sending ping"); + + rc = MqttClient_Ping_ex(&mqttCtx->client, &mqttCtx->ping); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + else if (rc != MQTT_CODE_SUCCESS) { + PRINTF("MQTT Ping Keep Alive Error: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + break; + } + } + else if (rc != MQTT_CODE_SUCCESS) { + /* There was an error */ + PRINTF("MQTT Message Wait: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + break; + } + + /* Exit if test mode */ + if (mqttCtx->test_mode) { + break; + } + } while (1); + + /* Check for error */ + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + } + FALL_THROUGH; + + case WMQ_DISCONNECT: + { + /* Disconnect */ + rc = MqttClient_Disconnect(&mqttCtx->client); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + } + FALL_THROUGH; + + case WMQ_NET_DISCONNECT: + { + mqttCtx->stat = WMQ_NET_DISCONNECT; + + rc = MqttClient_NetDisconnect(&mqttCtx->client); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + PRINTF("MQTT Socket Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + FALL_THROUGH; + + case WMQ_DONE: + { + mqttCtx->stat = WMQ_DONE; + rc = mqttCtx->return_code; + goto exit; + } + + case WMQ_PUB: + case WMQ_UNSUB: + case WMQ_PING: + default: + rc = MQTT_CODE_ERROR_STAT; + goto exit; + } /* switch */ + +disconn: + mqttCtx->stat = WMQ_NET_DISCONNECT; + mqttCtx->return_code = rc; + rc = MQTT_CODE_CONTINUE; + +exit: + + if (rc != MQTT_CODE_CONTINUE) { + fw_transfer_reset(); + + /* Free resources */ + if (mqttCtx->tx_buf) WOLFMQTT_FREE(mqttCtx->tx_buf); + if (mqttCtx->rx_buf) WOLFMQTT_FREE(mqttCtx->rx_buf); + + /* Cleanup network */ + MqttClientNet_DeInit(&mqttCtx->net); + + MqttClient_DeInit(&mqttCtx->client); + } + + return rc; +} + + +/* so overall tests can pull in test function */ +#ifdef USE_WINDOWS_API + #include /* for ctrl handler */ + + static BOOL CtrlHandler(DWORD fdwCtrlType) + { + if (fdwCtrlType == CTRL_C_EVENT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received Ctrl+c"); + return TRUE; + } + return FALSE; + } +#elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received SIGINT"); + } + } +#endif + +int fwclient_main(void) +{ + int rc; + MQTTCtx mqttCtx; + +#if defined(DEBUG_WOLFSSL) + printf("Debug enabled\n"); + wolfSSL_Debugging_ON(); +#endif + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + mqttCtx.app_name = "fwclient"; + mqttCtx.client_id = mqtt_append_random(FIRMWARE_CLIENT_ID, + (word32)XSTRLEN(FIRMWARE_CLIENT_ID)); + mqttCtx.dynamicClientId = 1; + mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; + mqttCtx.qos = FIRMWARE_MQTT_QOS; + mqttCtx.pub_file = FIRMWARE_DEF_SAVE_AS; + mqttCtx.use_tls = 1; + mqttCtx.username = "fwclient"; + mqttCtx.password = "fwclient_pw"; + +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif + + do { + rc = fwclient_test(&mqttCtx); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); + + mqtt_free_ctx(&mqttCtx); + + return (rc == 0) ? 0 : EXIT_FAILURE; +} diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/fwclient.h b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/fwclient.h new file mode 100644 index 0000000..34df349 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/fwclient.h @@ -0,0 +1,37 @@ +/* fwclient.h + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_FWCLIENT_H +#define WOLFMQTT_FWCLIENT_H + +#include "mqttexample.h" + +#define FIRMWARE_CLIENT_ID "WolfMQTTFWClient" +#define FIRMWARE_DEF_SAVE_AS "./app/image_updated.bin" + + +/* Exposed functions */ +int fwclient_test(MQTTCtx *mqttCtx); + + +int fwclient_main(void); + +#endif /* WOLFMQTT_FWCLIENT_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttexample.c b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttexample.c new file mode 100644 index 0000000..5c5a882 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttexample.c @@ -0,0 +1,594 @@ +/* mqttexample.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mqttexample.h" +#include "mqttnet.h" +#include "mqttport.h" + + +/* locals */ +static volatile word16 mPacketIdLast; +static const char* kDefTopicName = DEFAULT_TOPIC_NAME; +static const char* kDefClientId = DEFAULT_CLIENT_ID; + +#ifdef ENABLE_MQTT_TLS +#ifdef HAVE_SNI +static int useSNI; +static const char* mTlsSniHostName = NULL; +#endif +#ifdef HAVE_PQC +static const char* mTlsPQAlg = NULL; +#endif +#endif /* ENABLE_MQTT_TLS */ + +/* used for testing only, requires wolfSSL RNG */ +#ifdef ENABLE_MQTT_TLS +#include +#include /* Needed for Cert Buffers */ +#endif + +static int mqtt_get_rand(byte* data, word32 len) +{ + int ret = -1; +#ifdef ENABLE_MQTT_TLS + WC_RNG rng; + ret = wc_InitRng(&rng); + if (ret == 0) { + ret = wc_RNG_GenerateBlock(&rng, data, len); + wc_FreeRng(&rng); + } +#elif defined(HAVE_RAND) + word32 i; + for (i = 0; i (int)sizeof(rndBytes)) + sz = (int)sizeof(rndBytes); + sz /= 2; /* 1 byte expands to 2 bytes */ + + rc = mqtt_get_rand(rndBytes, sz); + if (rc == 0) { + /* Convert random to hex string */ + for (i=0; i> 4]; + buf[pos + (i*2)+1] = kHexChar[in & 0xf]; + } + pos += sz*2; + } + else { + PRINTF("MQTT Fill Random Failed! %d", rc); + } + } + return rc; +} + +#ifndef TEST_RAND_SZ +#define TEST_RAND_SZ 4 +#endif +char* mqtt_append_random(const char* inStr, word32 inLen) +{ + int rc = 0; + char *tmp; + + tmp = (char*)WOLFMQTT_MALLOC(inLen + 1 + (TEST_RAND_SZ*2) + 1); + if (tmp == NULL) { + rc = MQTT_CODE_ERROR_MEMORY; + } + if (rc == 0) { + /* Format: inStr + `_` randhex + null term */ + XMEMCPY(tmp, inStr, inLen); + tmp[inLen] = '_'; + rc = mqtt_fill_random_hexstr(tmp + inLen + 1, (TEST_RAND_SZ*2)); + tmp[inLen + 1 + (TEST_RAND_SZ*2)] = '\0'; /* null term */ + } + if (rc != 0) { + WOLFMQTT_FREE(tmp); + tmp = NULL; + } + return tmp; +} + +void mqtt_init_ctx(MQTTCtx* mqttCtx) +{ + XMEMSET(mqttCtx, 0, sizeof(MQTTCtx)); + mqttCtx->host = DEFAULT_MQTT_HOST; + mqttCtx->qos = DEFAULT_MQTT_QOS; + mqttCtx->clean_session = 1; + mqttCtx->keep_alive_sec = DEFAULT_KEEP_ALIVE_SEC; + mqttCtx->client_id = kDefClientId; + mqttCtx->topic_name = kDefTopicName; + mqttCtx->cmd_timeout_ms = DEFAULT_CMD_TIMEOUT_MS; + mqttCtx->debug_on = 1; +#ifdef WOLFMQTT_V5 + mqttCtx->max_packet_size = DEFAULT_MAX_PKT_SZ; + mqttCtx->topic_alias = 1; + mqttCtx->topic_alias_max = 1; +#endif +#ifdef WOLFMQTT_DEFAULT_TLS + mqttCtx->use_tls = WOLFMQTT_DEFAULT_TLS; +#endif +#ifdef ENABLE_MQTT_TLS + mqttCtx->ca_file = NULL; + mqttCtx->mtls_keyfile = NULL; + mqttCtx->mtls_certfile = NULL; +#endif + mqttCtx->app_name = "mqttclient"; + mqttCtx->message = DEFAULT_MESSAGE; +} + +void mqtt_free_ctx(MQTTCtx* mqttCtx) +{ + if (mqttCtx == NULL) { + return; + } + + if (mqttCtx->dynamicTopic && mqttCtx->topic_name) { + WOLFMQTT_FREE((char*)mqttCtx->topic_name); + mqttCtx->topic_name = NULL; + } + if (mqttCtx->dynamicClientId && mqttCtx->client_id) { + WOLFMQTT_FREE((char*)mqttCtx->client_id); + mqttCtx->client_id = NULL; + } +} + +#if defined(__GNUC__) && !defined(NO_EXIT) && !defined(WOLFMQTT_ZEPHYR) + __attribute__ ((noreturn)) +#endif +int err_sys(const char* msg) +{ + if (msg) { + PRINTF("wolfMQTT error: %s", msg); + } + exit(EXIT_FAILURE); +#ifdef WOLFMQTT_ZEPHYR + /* Zephyr compiler produces below warning. Let's silence it. + * warning: 'noreturn' function does return + * 477 | } + * | ^ + */ + return 0; +#endif +} + + +word16 mqtt_get_packetid(void) +{ + /* Check rollover */ + if (mPacketIdLast >= MAX_PACKET_ID) { + mPacketIdLast = 0; + } + + return ++mPacketIdLast; +} + +#ifdef WOLFMQTT_NONBLOCK +#if defined(MICROCHIP_MPLAB_HARMONY) + #include +#else + #include +#endif + +static word32 mqtt_get_timer_seconds(void) +{ + word32 timer_sec = 0; + +#if defined(MICROCHIP_MPLAB_HARMONY) + timer_sec = (word32)(SYS_TMR_TickCountGet() / + SYS_TMR_TickCounterFrequencyGet()); +#else + /* Posix style time */ + timer_sec = (word32)time(0); +#endif + + return timer_sec; +} + +int mqtt_check_timeout(int rc, word32* start_sec, word32 timeout_sec) +{ + word32 elapsed_sec; + + /* if start seconds not set or is not continue */ + if (*start_sec == 0 || rc != MQTT_CODE_CONTINUE) { + *start_sec = mqtt_get_timer_seconds(); + return rc; + } + + /* Default to 2s timeout. This function sometimes incorrectly + * triggers if 1s is used because of rounding. */ + if (timeout_sec == 0) { + timeout_sec = DEFAULT_CHK_TIMEOUT_S; + } + + elapsed_sec = mqtt_get_timer_seconds(); + if (*start_sec < elapsed_sec) { + elapsed_sec -= *start_sec; + if (elapsed_sec >= timeout_sec) { + *start_sec = mqtt_get_timer_seconds(); + PRINTF("Timeout timer %d seconds", timeout_sec); + return MQTT_CODE_ERROR_TIMEOUT; + } + } + + return rc; +} +#endif /* WOLFMQTT_NONBLOCK */ + + +#if defined(ENABLE_MQTT_TLS) && !defined(EXTERNAL_MQTT_TLS_CALLBACK) + +#ifdef WOLFSSL_ENCRYPTED_KEYS +int mqtt_password_cb(char* passwd, int sz, int rw, void* userdata) +{ + (void)rw; + (void)userdata; + if (userdata != NULL) { + XSTRNCPY(passwd, (char*)userdata, sz); + return (int)XSTRLEN((char*)userdata); + } + else { + XSTRNCPY(passwd, "yassl123", sz); + return (int)XSTRLEN(passwd); + } +} +#endif + +static int mqtt_tls_verify_cb(int preverify, WOLFSSL_X509_STORE_CTX* store) +{ + char buffer[WOLFSSL_MAX_ERROR_SZ]; + MQTTCtx *mqttCtx = NULL; + char appName[PRINT_BUFFER_SIZE] = {0}; + + if (store->userCtx != NULL) { + /* The client.ctx was stored during MqttSocket_Connect. */ + mqttCtx = (MQTTCtx *)store->userCtx; + XSTRNCPY(appName, " for ", PRINT_BUFFER_SIZE-1); + XSTRNCAT(appName, mqttCtx->app_name, + PRINT_BUFFER_SIZE-XSTRLEN(appName)-1); + } + + PRINTF("MQTT TLS Verify Callback%s: PreVerify %d, Error %d (%s)", + appName, preverify, + store->error, store->error != 0 ? + wolfSSL_ERR_error_string(store->error, buffer) : "none"); + PRINTF(" Subject's domain name is %s", store->domain); + + if (store->error != 0) { + /* Allowing to continue */ + /* Should check certificate and return 0 if not okay */ + PRINTF(" Allowing cert anyways"); + } + + return 1; +} + +/* Use this callback to setup TLS certificates and verify callbacks */ +int mqtt_tls_cb(MqttClient* client) +{ + int rc = WOLFSSL_FAILURE; + SocketContext * sock = (SocketContext *)client->net->context; + + /* Use highest available and allow downgrade. If wolfSSL is built with + * old TLS support, it is possible for a server to force a downgrade to + * an insecure version. */ + client->tls.ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); + if (client->tls.ctx) { + wolfSSL_CTX_set_verify(client->tls.ctx, WOLFSSL_VERIFY_PEER, + mqtt_tls_verify_cb); + + /* default to success */ + rc = WOLFSSL_SUCCESS; + +#if !defined(NO_CERT) + #if !defined(NO_FILESYSTEM) + if (sock->mqttCtx->ca_file) { + /* Load CA certificate file */ + rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx, + sock->mqttCtx->ca_file, NULL); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading CA %s: %d (%s)", sock->mqttCtx->ca_file, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + } + if (sock->mqttCtx->mtls_certfile && sock->mqttCtx->mtls_keyfile) { + /* Load If using a mutual authentication */ + rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx, + sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading certificate %s: %d (%s)", + sock->mqttCtx->mtls_certfile, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + + #ifdef WOLFSSL_ENCRYPTED_KEYS + /* Setup password callback for pkcs8 key */ + wolfSSL_CTX_set_default_passwd_cb(client->tls.ctx, + mqtt_password_cb); + #endif + + rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx, + sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading key %s: %d (%s)", + sock->mqttCtx->mtls_keyfile, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + } + #else + /* Note: Zephyr example uses NO_FILESYSTEM */ + #ifdef WOLFSSL_ENCRYPTED_KEYS + /* Setup password callback for pkcs8 key */ + wolfSSL_CTX_set_default_passwd_cb(client->tls.ctx, + mqtt_password_cb); + #endif + /* Examples for loading buffer directly */ + /* Load CA certificate buffer */ + rc = wolfSSL_CTX_load_verify_buffer_ex(client->tls.ctx, + (const byte*)ca_cert_chain_der, (long)sizeof_ca_cert_chain_der, + WOLFSSL_FILETYPE_ASN1, 0, WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY); + + /* Load Client Cert */ + if (rc == WOLFSSL_SUCCESS) { + rc = wolfSSL_CTX_use_certificate_buffer(client->tls.ctx, + (const byte*)client_cert_der_2048, (long)sizeof_client_cert_der_2048, + WOLFSSL_FILETYPE_ASN1); + } + + /* Load Private Key */ + if (rc == WOLFSSL_SUCCESS) { + rc = wolfSSL_CTX_use_PrivateKey_buffer(client->tls.ctx, + (const byte*)client_key_der_2048, (long)sizeof_client_key_der_2048, + WOLFSSL_FILETYPE_ASN1); + } + #endif /* !NO_FILESYSTEM */ +#endif /* !NO_CERT */ +#ifdef HAVE_SNI + if ((rc == WOLFSSL_SUCCESS) && (mTlsSniHostName != NULL)) { + rc = wolfSSL_CTX_UseSNI(client->tls.ctx, WOLFSSL_SNI_HOST_NAME, + mTlsSniHostName, (word16) XSTRLEN(mTlsSniHostName)); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("UseSNI failed"); + } + } +#endif /* HAVE_SNI */ +#ifdef HAVE_PQC + if ((rc == WOLFSSL_SUCCESS) && (mTlsPQAlg != NULL)) { + int group = 0; + if (XSTRCMP(mTlsPQAlg, "ML_KEM_768") == 0) { + group = WOLFSSL_ML_KEM_768; + } else if (XSTRCMP(mTlsPQAlg, "SecP384r1MLKEM768") == 0) { + group = WOLFSSL_SECP384R1MLKEM768; + } else { + PRINTF("Invalid post-quantum KEM specified"); + } + + if (group != 0) { + client->tls.ssl = wolfSSL_new(client->tls.ctx); + if (client->tls.ssl == NULL) { + rc = WOLFSSL_FAILURE; + } + + if (rc == WOLFSSL_SUCCESS) { + rc = wolfSSL_UseKeyShare(client->tls.ssl, group); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Use key share failed"); + } + } + } + } +#endif /* HAVE_PQC */ + } + +#if defined(NO_CERT) || defined(NO_FILESYSTEM) + (void)sock; +#endif + + PRINTF("MQTT TLS Setup (%d)", rc); + + return rc; +} + +#ifdef WOLFMQTT_SN +int mqtt_dtls_cb(MqttClient* client) { +#ifdef WOLFSSL_DTLS + int rc = WOLFSSL_FAILURE; + SocketContext * sock = (SocketContext *)client->net->context; + + client->tls.ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method()); + if (client->tls.ctx) { + wolfSSL_CTX_set_verify(client->tls.ctx, WOLFSSL_VERIFY_PEER, + mqtt_tls_verify_cb); + + /* default to success */ + rc = WOLFSSL_SUCCESS; + +#if !defined(NO_CERT) && !defined(NO_FILESYSTEM) + if (sock->mqttCtx->ca_file) { + /* Load CA certificate file */ + rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx, + sock->mqttCtx->ca_file, NULL); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading CA %s: %d (%s)", sock->mqttCtx->ca_file, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + } + if (sock->mqttCtx->mtls_certfile && sock->mqttCtx->mtls_keyfile) { + /* Load If using a mutual authentication */ + rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx, + sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading certificate %s: %d (%s)", + sock->mqttCtx->mtls_certfile, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + + rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx, + sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM); + if (rc != WOLFSSL_SUCCESS) { + PRINTF("Error loading key %s: %d (%s)", + sock->mqttCtx->mtls_keyfile, + rc, wolfSSL_ERR_reason_error_string(rc)); + return rc; + } + } +#else + (void)sock; +#endif + + client->tls.ssl = wolfSSL_new(client->tls.ctx); + if (client->tls.ssl == NULL) { + rc = WOLFSSL_FAILURE; + return rc; + } + } + + PRINTF("MQTT DTLS Setup (%d)", rc); +#else /* WOLFSSL_DTLS */ + (void)client; + int rc = 0; + PRINTF("MQTT DTLS Setup - Must enable DTLS in wolfSSL!"); +#endif + return rc; +} +#endif /* WOLFMQTT_SN */ +#else +int mqtt_tls_cb(MqttClient* client) +{ + (void)client; + return 0; +} +#ifdef WOLFMQTT_SN +int mqtt_dtls_cb(MqttClient* client) +{ + (void)client; + return 0; +} +#endif +#endif /* ENABLE_MQTT_TLS */ + +int mqtt_file_load(const char* filePath, byte** fileBuf, int *fileLen) +{ +#if !defined(NO_FILESYSTEM) + int rc = 0; + XFILE file = NULL; + long int pos = -1L; + + /* Check arguments */ + if (filePath == NULL || XSTRLEN(filePath) == 0 || fileLen == NULL || + fileBuf == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* Open file */ + file = XFOPEN(filePath, "rb"); + if (file == NULL) { + PRINTF("File '%s' does not exist!", filePath); + rc = EXIT_FAILURE; + goto exit; + } + + /* Determine length of file */ + if (XFSEEK(file, 0, XSEEK_END) != 0) { + PRINTF("fseek() failed"); + rc = EXIT_FAILURE; + goto exit; + } + + pos = (int)XFTELL(file); + if (pos == -1L) { + PRINTF("ftell() failed"); + rc = EXIT_FAILURE; + goto exit; + } + + *fileLen = (int)pos; + if (XFSEEK(file, 0, XSEEK_SET) != 0) { + PRINTF("fseek() failed"); + rc = EXIT_FAILURE; + goto exit; + } +#ifdef DEBUG_WOLFMQTT + PRINTF("File %s is %d bytes", filePath, *fileLen); +#endif + + /* Allocate buffer for image */ + *fileBuf = (byte*)WOLFMQTT_MALLOC(*fileLen); + if (*fileBuf == NULL) { + PRINTF("File buffer malloc failed!"); + rc = MQTT_CODE_ERROR_MEMORY; + goto exit; + } + + /* Load file into buffer */ + rc = (int)XFREAD(*fileBuf, 1, *fileLen, file); + if (rc != *fileLen) { + PRINTF("Error reading file! %d", rc); + rc = EXIT_FAILURE; + goto exit; + } + rc = 0; /* Success */ + +exit: + if (file) { + XFCLOSE(file); + } + if (rc != 0) { + if (*fileBuf) { + WOLFMQTT_FREE(*fileBuf); + *fileBuf = NULL; + } + } + return rc; + +#else + (void)filePath; + (void)fileBuf; + (void)fileLen; + PRINTF("File system support is not configured."); + return EXIT_FAILURE; +#endif +} diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttexample.h b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttexample.h new file mode 100644 index 0000000..b897c94 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttexample.h @@ -0,0 +1,244 @@ +/* mqttexample.h + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_EXAMPLE_H +#define WOLFMQTT_EXAMPLE_H + +#if defined(CONFIG_WOLFMQTT) && !defined(WOLFMQTT_ZEPHYR) + #define WOLFMQTT_ZEPHYR +#endif + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* Compatibility Options */ +#ifdef NO_EXIT + #undef exit + #define exit(rc) return rc +#endif + +#ifndef MY_EX_USAGE +#define MY_EX_USAGE 2 /* Exit reason code */ +#endif + +/* STDIN / FGETS for examples */ +#ifndef WOLFMQTT_NO_STDIO + /* For Linux/Mac */ + #if !defined(FREERTOS) && !defined(USE_WINDOWS_API) && \ + !defined(FREESCALE_MQX) && !defined(FREESCALE_KSDK_MQX) && \ + !defined(MICROCHIP_MPLAB_HARMONY) && !defined(WOLFMQTT_ZEPHYR) + /* Make sure its not explicitly disabled and not already defined */ + #if !defined(WOLFMQTT_NO_STDIN_CAP) && \ + !defined(WOLFMQTT_ENABLE_STDIN_CAP) + /* Wake on stdin activity */ + #define WOLFMQTT_ENABLE_STDIN_CAP + #endif + #endif + + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + #ifndef XFGETS + #define XFGETS fgets + #endif + #ifndef STDIN + #define STDIN 0 + #endif + #endif +#endif /* !WOLFMQTT_NO_STDIO */ + + +/* Default Configurations */ + +#ifndef DEFAULT_MQTT_HOST + /* Default MQTT host broker to use, + * when none is specified in the examples */ + #define DEFAULT_MQTT_HOST "192.168.1.10" + /* "iot.eclipse.org" */ + /* "broker.emqx.io" */ + /* "broker.hivemq.com" */ +#endif + +#define DEFAULT_CMD_TIMEOUT_MS 30000 +#define DEFAULT_CON_TIMEOUT_MS 5000 +#define DEFAULT_CHK_TIMEOUT_S 2 +#define DEFAULT_MQTT_QOS MQTT_QOS_0 +#define DEFAULT_KEEP_ALIVE_SEC 60 +#define DEFAULT_CLIENT_ID "WolfMQTTClient" +#ifndef WOLFMQTT_TOPIC_NAME + #define WOLFMQTT_TOPIC_NAME "wolfMQTT/example/" + #define DEFAULT_TOPIC_NAME WOLFMQTT_TOPIC_NAME"testTopic" +#else + #define DEFAULT_TOPIC_NAME WOLFMQTT_TOPIC_NAME +#endif +#define DEFAULT_AUTH_METHOD "EXTERNAL" +#define PRINT_BUFFER_SIZE 80 +#define DEFAULT_MESSAGE "test" + +#ifdef WOLFMQTT_V5 +#define DEFAULT_MAX_PKT_SZ 1024*1024 /* The max MQTT control packet size + the client is willing to accept. */ +#define DEFAULT_SUB_ID 1 /* Sub ID starts at 1 */ +#define DEFAULT_SESS_EXP_INT 0xFFFFFFFF +#endif + +/* certs are either static or extern, depending on the specific example */ +#ifndef EXTERNAL_MQTT_TLS_CALLBACK +#ifdef WOLFMQTT_EXTERN_CERT + #undef WOLFMQTT_EXAMPLE_CERT + #define WOLFMQTT_EXAMPLE_CERT /* init extern from mqttexample.h */ + extern const char* root_ca; + extern const char* device_cert; + extern const char* device_priv_key; +#else + #undef WOLFMQTT_EXAMPLE_CERT + #define WOLFMQTT_EXAMPLE_CERT static +#endif +#endif /* !EXTERNAL_MQTT_TLS_CALLBACK */ +/* MQTT Client state */ +typedef enum _MQTTCtxState { + WMQ_BEGIN = 0, + WMQ_NET_INIT, + WMQ_INIT, + WMQ_TCP_CONN, + WMQ_MQTT_CONN, + WMQ_SUB, + WMQ_PUB, + WMQ_WAIT_MSG, + WMQ_PING, + WMQ_UNSUB, + WMQ_DISCONNECT, + WMQ_NET_DISCONNECT, + WMQ_DONE +} MQTTCtxState; + +/* MQTT Client context */ +/* This is used for the examples as reference */ +/* Use of this structure allow non-blocking context */ +typedef struct _MQTTCtx { + MQTTCtxState stat; + + void* app_ctx; /* For storing application specific data */ + + /* client and net containers */ + MqttClient client; + MqttNet net; + + /* temp mqtt containers */ + MqttConnect connect; + MqttMessage lwt_msg; + MqttSubscribe subscribe; + MqttUnsubscribe unsubscribe; + MqttTopic topics[1]; + MqttPublish publish; + MqttDisconnect disconnect; + MqttPing ping; +#ifdef WOLFMQTT_SN + SN_Publish publishSN; +#endif + + /* configuration */ + MqttQoS qos; + const char* app_name; + const char* host; + const char* username; + const char* password; + const char* topic_name; + const char* message; + const char* pub_file; + const char* client_id; +#if defined (ENABLE_MQTT_TLS) + const char* ca_file; + const char* mtls_keyfile; + const char* mtls_certfile; +#endif + byte *tx_buf, *rx_buf; + int return_code; + int use_tls; + int retain; + int enable_lwt; +#ifdef WOLFMQTT_V5 + int max_packet_size; +#endif + word32 cmd_timeout_ms; +#ifdef WOLFMQTT_NONBLOCK + word32 start_sec; /* used for timeout and keep-alive */ +#endif + word16 keep_alive_sec; + word16 port; +#ifdef WOLFMQTT_V5 + word16 topic_alias; + word16 topic_alias_max; /* Server property */ +#endif + byte clean_session; + byte test_mode; + byte debug_on:1; /* enable debug messages in example */ +#ifdef WOLFMQTT_V5 + byte subId_not_avail; /* Server property */ + byte enable_eauth; /* Enhanced authentication */ +#endif + unsigned int dynamicTopic:1; + unsigned int dynamicClientId:1; + unsigned int skip_subscribe:1; + const char* ready_file; /* touch file when ready (e.g., after SUBACK) */ +#ifdef WOLFMQTT_NONBLOCK + unsigned int useNonBlockMode:1; /* set to use non-blocking mode. + network callbacks can return MQTT_CODE_CONTINUE to indicate "would block" */ +#endif +#ifdef WOLFMQTT_WOLFIP + struct wolfIP *stack; /* wolfIP TCP/IP stack instance */ +#endif +} MQTTCtx; + + +void mqtt_show_usage(MQTTCtx* mqttCtx); +void mqtt_init_ctx(MQTTCtx* mqttCtx); +void mqtt_free_ctx(MQTTCtx* mqttCtx); +int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv); +int err_sys(const char* msg); + +int mqtt_tls_cb(MqttClient* client); + +#ifdef WOLFMQTT_SN +int mqtt_dtls_cb(MqttClient* client); +#endif + +word16 mqtt_get_packetid(void); + +#ifdef WOLFMQTT_NONBLOCK +int mqtt_check_timeout(int rc, word32* start_sec, word32 timeout_sec); +#endif + +int mqtt_fill_random_hexstr(char* buf, word32 bufLen); +char* mqtt_append_random(const char* inStr, word32 inLen); + +int mqtt_file_load(const char* filePath, byte** fileBuf, int *fileLen); + +#ifdef WOLFSSL_ENCRYPTED_KEYS +int mqtt_password_cb(char* passwd, int sz, int rw, void* userdata); +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_EXAMPLE_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttnet.c b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttnet.c new file mode 100644 index 0000000..e7da29c --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttnet.c @@ -0,0 +1,2032 @@ +/* mqttnet.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "mqttnet.h" + +#ifdef WOLFSSL_ZEPHYR + #include +#endif + +#if 0 /* TODO: add multicast support */ +typedef struct MulticastCtx { + +} MulticastCtx; +#endif + +#ifndef WOLFMQTT_TEST_NONBLOCK_TIMES + #define WOLFMQTT_TEST_NONBLOCK_TIMES 1 +#endif + +/* Private functions */ + +/* -------------------------------------------------------------------------- */ +/* FREERTOS TCP NETWORK CALLBACK EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#ifdef FREERTOS_TCP + +#ifndef WOLFMQTT_NO_TIMEOUT + static SocketSet_t gxFDSet = NULL; +#endif +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + uint32_t hostIp = 0; + int rc = -1; + MQTTCtx* mqttCtx = sock->mqttCtx; + + switch (sock->stat) { + case SOCK_BEGIN: + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", + host, port, timeout_ms, mqttCtx->use_tls); + } + + hostIp = FreeRTOS_gethostbyname_a(host, NULL, 0, 0); + if (hostIp == 0) + break; + + sock->addr.sin_family = FREERTOS_AF_INET; + sock->addr.sin_port = FreeRTOS_htons(port); + sock->addr.sin_addr = hostIp; + + /* Create socket */ + sock->fd = FreeRTOS_socket(sock->addr.sin_family, FREERTOS_SOCK_STREAM, + FREERTOS_IPPROTO_TCP); + + if (sock->fd == FREERTOS_INVALID_SOCKET) + break; + +#ifndef WOLFMQTT_NO_TIMEOUT + /* Set timeouts for socket */ + timeout_ms = pdMS_TO_TICKS(timeout_ms); + FreeRTOS_setsockopt(sock->fd, 0, FREERTOS_SO_SNDTIMEO, + (void*)&timeout_ms, sizeof(timeout_ms)); + FreeRTOS_setsockopt(sock->fd, 0, FREERTOS_SO_RCVTIMEO, + (void*)&timeout_ms, sizeof(timeout_ms)); +#else + (void)timeout_ms; +#endif + sock->stat = SOCK_CONN; + + FALL_THROUGH; + case SOCK_CONN: + /* Start connect */ + rc = FreeRTOS_connect(sock->fd, (SOCK_ADDR_IN*)&sock->addr, + sizeof(sock->addr)); + break; + } + + return rc; +} + +static int NetRead(void *context, byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc = -1, timeout = 0; + word32 bytes = 0; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#ifndef WOLFMQTT_NO_TIMEOUT + /* Create the set of sockets that will be passed into FreeRTOS_select(). */ + if (gxFDSet == NULL) + gxFDSet = FreeRTOS_CreateSocketSet(); + if (gxFDSet == NULL) + return MQTT_CODE_ERROR_OUT_OF_BUFFER; + timeout_ms = pdMS_TO_TICKS(timeout_ms); /* convert ms to ticks */ +#else + (void)timeout_ms; +#endif + + /* Loop until buf_len has been read, error or timeout */ + while ((bytes < buf_len) && (timeout == 0)) { + +#ifndef WOLFMQTT_NO_TIMEOUT + /* set the socket to do used */ + FreeRTOS_FD_SET(sock->fd, gxFDSet, eSELECT_READ | eSELECT_EXCEPT); + + /* Wait for any event within the socket set. */ + rc = FreeRTOS_select(gxFDSet, timeout_ms); + if (rc != 0) { + if (FreeRTOS_FD_ISSET(sock->fd, gxFDSet)) +#endif + { + /* Try and read number of buf_len provided, + minus what's already been read */ + rc = (int)FreeRTOS_recv(sock->fd, &buf[bytes], + buf_len - bytes, 0); + + if (rc <= 0) { + break; /* Error */ + } + else { + /* Clamp return value: defensive check against + * platform API returning more than requested */ + if (rc > buf_len - (int)bytes) { + rc = buf_len - (int)bytes; + } + bytes += rc; /* Data */ + } + } +#ifndef WOLFMQTT_NO_TIMEOUT + } + else { + timeout = 1; + } +#endif + } + + if (rc == 0 || timeout) { + rc = MQTT_CODE_ERROR_TIMEOUT; + } + else if (rc < 0) { + #ifdef WOLFMQTT_NONBLOCK + if (rc == -pdFREERTOS_ERRNO_EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + #endif + PRINTF("NetRead: Error %d", rc); + rc = MQTT_CODE_ERROR_NETWORK; + } + else { + rc = bytes; + } + + return rc; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc = -1; + + (void)timeout_ms; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = (int)FreeRTOS_send(sock->fd, buf, buf_len, 0); + + if (rc < 0) { + #ifdef WOLFMQTT_NONBLOCK + if (rc == -pdFREERTOS_ERRNO_EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + #endif + PRINTF("NetWrite: Error %d", rc); + rc = MQTT_CODE_ERROR_NETWORK; + } + + return rc; +} + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + FreeRTOS_closesocket(sock->fd); + sock->stat = SOCK_BEGIN; + } + +#ifndef WOLFMQTT_NO_TIMEOUT + if (gxFDSet != NULL) { + FreeRTOS_DeleteSocketSet(gxFDSet); + gxFDSet = NULL; + } +#endif + + return 0; +} + + +/* -------------------------------------------------------------------------- */ +/* MICROCHIP HARMONY TCP NETWORK CALLBACK EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(MICROCHIP_MPLAB_HARMONY) + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + closesocket(sock->fd); + sock->fd = SOCKET_INVALID; + } + + sock->stat = SOCK_BEGIN; + } + return 0; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int type = SOCK_STREAM; + int rc = MQTT_CODE_ERROR_NETWORK; + struct addrinfo hints; + struct hostent *hostInfo; + MQTTCtx* mqttCtx = sock->mqttCtx; + + /* Get address information for host and locate IPv4 */ + switch(sock->stat) { + case SOCK_BEGIN: + { + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } + XMEMSET(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + XMEMSET(&sock->addr, 0, sizeof(sock->addr)); + sock->addr.sin_family = AF_INET; + + hostInfo = gethostbyname((char *)host); + if (hostInfo != NULL) { + sock->addr.sin_port = port; /* htons(port); */ + sock->addr.sin_family = AF_INET; + XMEMCPY(&sock->addr.sin_addr.S_un, + *(hostInfo->h_addr_list), sizeof(IPV4_ADDR)); + } + else { + return MQTT_CODE_CONTINUE; + } + + /* Create socket */ + sock->fd = SOCK_OPEN(sock->addr.sin_family, type, 0); + if (sock->fd == SOCKET_INVALID) + goto exit; + + sock->stat = SOCK_CONN; + } + FALL_THROUGH; + + case SOCK_CONN: + { + /* Start connect */ + rc = SOCK_CONNECT(sock->fd, (struct sockaddr*)&sock->addr, + sizeof(sock->addr)); + break; + } + + default: + rc = MQTT_CODE_ERROR_BAD_ARG; + break; + } /* switch */ + + (void)timeout_ms; + +exit: + + /* check for error */ + if (rc != 0) { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + NetDisconnect(context); + + /* Show error */ + PRINTF("NetConnect: Rc=%d, ErrNo=%d", rc, errno); + } + + return rc; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = (int)send(sock->fd, buf, (size_t)buf_len, 0); + if (rc <= 0) { + /* Check for in progress */ + if (errno == EINPROGRESS || errno == EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + + PRINTF("NetWrite Error: Rc %d, BufLen %d, ErrNo %d", rc, buf_len, errno); + rc = MQTT_CODE_ERROR_NETWORK; + } + + (void)timeout_ms; + + return rc; +} + +static int NetRead(void *context, byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc = MQTT_CODE_ERROR_NETWORK; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = (int)recv(sock->fd, + &buf[sock->bytes], + (size_t)(buf_len - sock->bytes), + 0); + if (rc < 0) { + if (errno == EINPROGRESS || errno == EWOULDBLOCK) { + return MQTT_CODE_CONTINUE; + } + + PRINTF("NetRead Error: Rc %d, BufLen %d, ErrNo %d", rc, buf_len, errno); + rc = MQTT_CODE_ERROR_NETWORK; + } + else { + /* Clamp return value: defensive check against + * platform API returning more than requested */ + if (rc > buf_len - sock->bytes) { + rc = buf_len - sock->bytes; + } + sock->bytes += rc; + if (sock->bytes < buf_len) { + return MQTT_CODE_CONTINUE; + } + rc = sock->bytes; + sock->bytes = 0; + } + + (void)timeout_ms; + + return rc; +} + + +/* -------------------------------------------------------------------------- */ +/* NETX SOCKET BACKEND EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(HAVE_NETX) + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + nx_tcp_socket_disconnect(&sock->fd, NX_NO_WAIT); + nx_tcp_socket_delete(&sock->fd); + + sock->stat = SOCK_BEGIN; + } + return 0; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc = MQTT_CODE_ERROR_NETWORK; + MQTTCtx* mqttCtx = sock->mqttCtx; + UINT status; + NXD_ADDRESS ipAddress; + UINT ticks; + + /* Get address information for host and locate IPv4 */ + switch(sock->stat) { + case SOCK_BEGIN: + { + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } + #ifndef WOLFMQTT_NO_NETX_DNS + if (sock->dnsPtr == NULL) { + PRINTF("DNS pointer was NULL and needs set, sock->dnsPtr"); + return MQTT_CODE_ERROR_NETWORK; + } + + /* Convert hostname to IP address using NETX DUO DNS */ + status = nxd_dns_host_by_name_get(sock->dnsPtr, (UCHAR *)host, + &ipAddress, timeout_ms, NX_IP_VERSION_V4); + if (status != NX_SUCCESS) { + PRINTF("DNS lookup failed: %d", status); + return MQTT_CODE_ERROR_NETWORK; + } + #else + PRINTF("DNS lookup not available"); + return MQTT_CODE_ERROR_NETWORK; + #endif + status = nx_tcp_socket_create(sock->ipPtr, &sock->fd, + (CHAR*)"MQTT Socket", NX_IP_NORMAL, + NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, + 1024, NX_NULL, NX_NULL); + if (status != NX_SUCCESS) { + PRINTF("Socket create failed: %d", status); + return MQTT_CODE_ERROR_NETWORK; + } + + /* Bind the socket to a local port */ + status = nx_tcp_client_socket_bind(&sock->fd, port, timeout_ms); + if (status != NX_SUCCESS) { + PRINTF("Socket bind failed: %d", status); + return MQTT_CODE_ERROR_NETWORK; + } + + sock->stat = SOCK_CONN; + } + FALL_THROUGH; + + case SOCK_CONN: + { + /* Convert timeout_ms to ticks and round up */ + ticks = (timeout_ms * TX_TIMER_TICKS_PER_SECOND + 999) / 1000; + + /* Connect to server using NETX DUO */ + status = nxd_tcp_client_socket_connect(&sock->fd, &ipAddress, port, + ticks); + if (status != NX_SUCCESS) { + PRINTF("Socket connect failed: %d", status); + NetDisconnect(context); + return MQTT_CODE_ERROR_NETWORK; + } + return MQTT_CODE_SUCCESS; + } + + default: + rc = MQTT_CODE_ERROR_BAD_ARG; + break; + } /* switch */ + + return rc; +} + + +static int NetWrite(void *context, const byte* buf, int buf_len, int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + NX_PACKET* packet; + NX_PACKET_POOL* pool; /* shorthand */ + UINT status; + UINT ticks; + + if (sock == NULL) { + PRINTF("NetX Send NULL parameters"); + return MQTT_CODE_ERROR_BAD_ARG; + } + + pool = sock->fd.nx_tcp_socket_ip_ptr->nx_ip_default_packet_pool; + status = nx_packet_allocate(pool, &packet, NX_TCP_PACKET, timeout_ms); + if (status != NX_SUCCESS) { + PRINTF("NetX Send packet alloc error"); + return MQTT_CODE_ERROR_NETWORK; + } + + status = nx_packet_data_append(packet, (VOID*)buf, buf_len, pool, + timeout_ms); + if (status != NX_SUCCESS) { + nx_packet_release(packet); + PRINTF("NetX Send data append error"); + return MQTT_CODE_ERROR_NETWORK; + } + + + /* Convert timeout_ms to ticks and round up */ + ticks = (timeout_ms * TX_TIMER_TICKS_PER_SECOND + 999) / 1000; + + status = nx_tcp_socket_send(&sock->fd, packet, ticks); + if (status != NX_SUCCESS) { + nx_packet_release(packet); + PRINTF("NetX Send socket send error"); + return MQTT_CODE_ERROR_NETWORK; + } + + return buf_len; +} + + +static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + ULONG left; + ULONG total; + ULONG copied = 0; + UINT status; + UINT ticks; + + if (sock == NULL) { + PRINTF("NetX Recv NULL parameters"); + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->nxPacket == NULL) { + /* Convert timeout_ms to ticks and round up */ + ticks = (timeout_ms * TX_TIMER_TICKS_PER_SECOND + 999) / 1000; + status = nx_tcp_socket_receive(&sock->fd, &sock->nxPacket, ticks); + if (status != NX_SUCCESS) { + if (status == NX_NO_PACKET) { + PRINTF("NetX Recv timeout"); + return MQTT_CODE_ERROR_TIMEOUT; + } + PRINTF("NetX Recv receive error"); + return MQTT_CODE_ERROR_NETWORK; + } + } + + if (sock->nxPacket) { + status = nx_packet_length_get(sock->nxPacket, &total); + if (status != NX_SUCCESS) { + PRINTF("NetX Recv length get error"); + return MQTT_CODE_ERROR_NETWORK; + } + + left = total - sock->nxOffset; + status = nx_packet_data_extract_offset(sock->nxPacket, + sock->nxOffset, buf, buf_len, &copied); + if (status != NX_SUCCESS) { + PRINTF("NetX Recv data extract offset error"); + return MQTT_CODE_ERROR_NETWORK; + } + + sock->nxOffset += copied; + + if (copied == left) { + PRINTF("NetX Recv Drained packet"); + nx_packet_release(sock->nxPacket); + sock->nxPacket = NULL; + sock->nxOffset = 0; + } + } + + return copied; +} + + +/* -------------------------------------------------------------------------- */ +/* CURL EASY SOCKET BACKEND EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(ENABLE_MQTT_CURL) + +/* How many times to retry after a timeout. */ +#define MQTT_CURL_NUM_RETRY (3) + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) +/* Tells the calling function to either return early with + * MQTT_CODE_CONTINUE, or proceed with a smaller buffer read/write. + * Used for testing nonblocking. */ +static int +mqttcurl_test_nonblock_read(int* buf_len) +{ + static int testNbReadAlt = 0; + static int testSmallerRead = 0; + + if (testNbReadAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbReadAlt++; + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("mqttcurl_test_nonblock_read: returning early with CONTINUE"); + #endif + return MQTT_CODE_CONTINUE; + } + + testNbReadAlt = 0; + + if (!testSmallerRead) { + if (*buf_len > 2) { + *buf_len /= 2; + testSmallerRead = 1; + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("mqttcurl_test_nonblock_read: testing small buff: %d", + *buf_len); + #endif + } + } + else { + testSmallerRead = 0; + } + + return MQTT_CODE_SUCCESS; +} + +static int +mqttcurl_test_nonblock_write(int* buf_len) +{ + static int testNbWriteAlt = 0; + static int testSmallerWrite = 0; + + if (testNbWriteAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbWriteAlt++; + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("mqttcurl_test_nonblock_write: returning early with CONTINUE"); + #endif + return MQTT_CODE_CONTINUE; + } + + testNbWriteAlt = 0; + + if (!testSmallerWrite) { + if (*buf_len > 2) { + *buf_len /= 2; + testSmallerWrite = 1; + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("mqttcurl_test_nonblock_write: testing small buff: %d", + *buf_len); + #endif + } + } + else { + testSmallerWrite = 0; + } + + return MQTT_CODE_SUCCESS; +} + +#endif /* WOLFMQTT_NONBLOCK && WOLFMQTT_TEST_NONBLOCK */ + +static int +mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms, + int test_mode) +{ + struct timeval tv; + fd_set infd; + fd_set outfd; + fd_set errfd; + int rc = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (int)(timeout_ms % 1000) * 1000; + + FD_ZERO(&infd); + FD_ZERO(&outfd); + FD_ZERO(&errfd); + + FD_SET(sockfd, &errfd); + + if (for_recv) { + FD_SET(sockfd, &infd); + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + if (!test_mode) { + FD_SET(STDIN, &infd); + } + #endif /* WOLFMQTT_ENABLE_STDIN_CAP */ + } + else { + FD_SET(sockfd, &outfd); + } + + rc = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); + + if (rc > 0) { + if (for_recv && FD_ISSET(sockfd, &infd)) { + return MQTT_CODE_CONTINUE; + } + else if (!for_recv && FD_ISSET(sockfd, &outfd)) { + return MQTT_CODE_CONTINUE; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + else if (for_recv && !test_mode && FD_ISSET(STDIN, &infd)) { + return MQTT_CODE_STDIN_WAKE; + } + #endif /* WOLFMQTT_ENABLE_STDIN_CAP */ + else if (FD_ISSET(sockfd, &errfd)) { + return MQTT_CODE_ERROR_NETWORK; + } + } + else if (rc == 0) { + return MQTT_CODE_ERROR_TIMEOUT; + } + + #ifndef WOLFMQTT_ENABLE_STDIN_CAP + (void)test_mode; + #endif + + return MQTT_CODE_ERROR_NETWORK; +} + +static int +mqttcurl_connect(SocketContext* sock, const char* host, word16 port, + int timeout_ms) +{ + CURLcode res = CURLE_OK; + + if (sock == NULL || sock->curl == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#ifdef DEBUG_WOLFMQTT + res = curl_easy_setopt(sock->curl, CURLOPT_VERBOSE, 1L); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(VERBOSE, 1L) returned: %d, %s", + res, curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } +#endif + + if (timeout_ms != 0) { + res = curl_easy_setopt(sock->curl, CURLOPT_CONNECTTIMEOUT_MS, + (long)timeout_ms); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CONNECTTIMEOUT_MS, %d) " + "returned %d", timeout_ms, res); + return MQTT_CODE_ERROR_CURL; + } + /* Note: CURLOPT_TIMEOUT_MS is not used here because it sets a total + * transfer timeout, which is not applicable with CURLOPT_CONNECT_ONLY + * mode where we use curl_easy_send/recv manually after connect. */ + } + + res = curl_easy_setopt(sock->curl, CURLOPT_URL, host); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(URL, %s) returned: %d", + host, res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_setopt(sock->curl, CURLOPT_PORT, (long)port); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(PORT, %d) returned: %d", + port, res); + return MQTT_CODE_ERROR_CURL; + } + + #ifdef ENABLE_MQTT_TLS + if (sock->mqttCtx->use_tls) { + /* Set TLS specific options. */ + res = curl_easy_setopt(sock->curl, CURLOPT_SSLVERSION, + CURL_SSLVERSION_TLSv1_2); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSLVERSION) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* With CURLOPT_CONNECT_ONLY this means do TLS by default. */ + res = curl_easy_setopt(sock->curl, CURLOPT_DEFAULT_PROTOCOL, + "https"); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(DEFAULT_PROTOCOL) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Set path to Certificate Authority (CA) file bundle. */ + if (sock->mqttCtx->ca_file != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_CAINFO, + sock->mqttCtx->ca_file); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CAINFO) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to mutual TLS keyfile. */ + if (sock->mqttCtx->mtls_keyfile != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSLKEY, + sock->mqttCtx->mtls_keyfile); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_SSLKEY) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to mutual TLS certfile. */ + if (sock->mqttCtx->mtls_certfile != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSLCERT, + sock->mqttCtx->mtls_certfile); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_SSLCERT) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to dir holding CA files. + * Unused at the moment. */ + /* + if (sock->mqttCtx->ca_path != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_CAPATH, + sock->mqttCtx->ca_path); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CAPATH) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + */ + + /* Set peer and host verification. */ + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYPEER, 1L); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYPEER) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Only do server host verification when not running against + * localhost broker. */ + if (XSTRCMP(host, "localhost") == 0) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYHOST, 0L); + } + else { + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYHOST, 2L); + } + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYHOST) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + #endif /* ENABLE_MQTT_TLS */ + + #if 0 + /* Set proxy options. + * Unused at the moment. */ + if (sock->mqttCtx->use_proxy) { + /* Set the proxy hostname or ip address string. Append + * ":[port num]" to the string to specify a port. */ + res = curl_easy_setopt(sock->curl, CURLOPT_PROXY, + sock->mqttCtx->proxy_str); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_PROXY, %s) returned: %d", + res, sock->mqttCtx->proxy_str); + return MQTT_CODE_ERROR_CURL; + } + + /* Set the proxy type. E.g. CURLPROXY_HTTP, CURLPROXY_HTTPS, + * CURLPROXY_HTTPS2, etc. */ + res = curl_easy_setopt(sock->curl, CURLOPT_PROXYTYPE, + CURLPROXY_HTTP); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_PROXYTYPE) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + #endif + + res = curl_easy_setopt(sock->curl, CURLOPT_CONNECT_ONLY, 1L); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CONNECT_ONLY, 1) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Finally do the connection. */ + res = curl_easy_perform(sock->curl); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_perform returned: %d, %s", res, + curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + return MQTT_CODE_SUCCESS; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext * sock = (SocketContext*)context; + int rc = 0; + + if (context == NULL || host == NULL || *host == '\0') { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->mqttCtx == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", + host, port, timeout_ms, sock->mqttCtx->use_tls); +#endif + + sock->curl = curl_easy_init(); + + if (sock->curl == NULL) { + PRINTF("error: curl_easy_init returned NULL"); + return MQTT_CODE_ERROR_MEMORY; + } + + rc = mqttcurl_connect(sock, host, port, timeout_ms); + + if (rc != MQTT_CODE_SUCCESS) { + curl_easy_cleanup(sock->curl); + sock->curl = NULL; + return rc; + } + + sock->bytes = 0; + sock->stat = SOCK_CONN; + return MQTT_CODE_SUCCESS; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = CURLE_OK; + SocketContext * sock = (SocketContext*)context; + size_t sent = 0; + curl_socket_t sockfd = 0; + int wait_rc = 0; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (sock->mqttCtx->useNonBlockMode) { + if (mqttcurl_test_nonblock_write(&buf_len)) { + return MQTT_CODE_CONTINUE; + } + } +#endif /* WOLFMQTT_NONBLOCK && WOLFMQTT_TEST_NONBLOCK */ + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(sock->curl, CURLINFO_ACTIVESOCKET, &sockfd); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* check it makes sense */ + if (sockfd <= 0) { + PRINTF("error: libcurl sockfd: %d", sockfd); + return MQTT_CODE_ERROR_CURL; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("sock->curl = %p, sockfd = %d", (void *)sock->curl, sockfd); +#endif + + /* Loop until all data is sent or error */ + do { + #ifdef WOLFMQTT_MULTITHREAD + { + int rc = wm_SemLock(&sock->mqttCtx->client.lockCURL); + if (rc != 0) { + return rc; + } + } + #endif + + res = curl_easy_send(sock->curl, &buf[sock->bytes], + buf_len - sock->bytes, &sent); + + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&sock->mqttCtx->client.lockCURL); + #endif + + if (res == CURLE_OK) { + /* Clamp return value: defensive check against + * library API returning more than requested */ + if ((int)sent > buf_len - sock->bytes) { + sent = (size_t)(buf_len - sock->bytes); + } + sock->bytes += (int)sent; + if (sock->bytes == buf_len) { + /* Complete, reset for next operation */ + sent = sock->bytes; + sock->bytes = 0; + return (int)sent; + } + /* Partial write, continue loop */ + } + else if (res == CURLE_AGAIN) { + wait_rc = mqttcurl_wait(sockfd, 0, timeout_ms, + sock->mqttCtx->test_mode); + if (wait_rc != MQTT_CODE_CONTINUE) { + return wait_rc; + } + } + else { + PRINTF("error: curl_easy_send(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); + sock->bytes = 0; + return MQTT_CODE_ERROR_CURL; + } + } while (1); +} + +static int NetRead(void *context, byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = CURLE_OK; + SocketContext * sock = (SocketContext*)context; + size_t recvd = 0; + curl_socket_t sockfd = 0; + int wait_rc = 0; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (sock->mqttCtx->useNonBlockMode) { + if (mqttcurl_test_nonblock_read(&buf_len)) { + return MQTT_CODE_CONTINUE; + } + } +#endif /* WOLFMQTT_NONBLOCK && WOLFMQTT_TEST_NONBLOCK */ + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(sock->curl, CURLINFO_ACTIVESOCKET, &sockfd); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* check it makes sense */ + if (sockfd <= 0) { + PRINTF("error: libcurl sockfd: %d", sockfd); + return MQTT_CODE_ERROR_CURL; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("sock->curl = %p, sockfd = %d", (void *)sock->curl, sockfd); +#endif + + /* Loop until all data is received or error */ + do { + #ifdef WOLFMQTT_MULTITHREAD + { + int rc = wm_SemLock(&sock->mqttCtx->client.lockCURL); + if (rc != 0) { + return rc; + } + } + #endif + + res = curl_easy_recv(sock->curl, &buf[sock->bytes], + buf_len - sock->bytes, &recvd); + + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&sock->mqttCtx->client.lockCURL); + #endif + + if (res == CURLE_OK) { + if (recvd == 0) { + /* Connection closed */ + PRINTF("error: connection closed by peer"); + sock->bytes = 0; + return MQTT_CODE_ERROR_NETWORK; + } + sock->bytes += (int)recvd; + if (sock->bytes == buf_len) { + /* Complete, reset for next operation */ + recvd = sock->bytes; + sock->bytes = 0; + return (int)recvd; + } + /* Partial read, continue loop */ + } + else if (res == CURLE_AGAIN) { + wait_rc = mqttcurl_wait(sockfd, 1, timeout_ms, + sock->mqttCtx->test_mode); + if (wait_rc != MQTT_CODE_CONTINUE) { + return wait_rc; + } + } + else { + PRINTF("error: curl_easy_recv(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); + sock->bytes = 0; + return MQTT_CODE_ERROR_CURL; + } + } while (1); +} + +static int NetDisconnect(void *context) +{ + SocketContext * sock = (SocketContext*)context; + + if (sock == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->curl != NULL) { +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: curl_easy_cleanup"); +#endif + curl_easy_cleanup(sock->curl); + sock->curl = NULL; + } + sock->bytes = 0; + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* WOLFIP TCP/IP STACK NETWORK CALLBACK EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(WOLFMQTT_WOLFIP) + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + wolfIP_sock_close(sock->stack, sock->fd); + sock->fd = SOCKET_INVALID; + } + sock->stat = SOCK_BEGIN; + } + return 0; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + struct wolfIP_sockaddr_in addr; + int rc; + + (void)timeout_ms; + + if (context == NULL || host == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + switch (sock->stat) { + case SOCK_BEGIN: + { + /* Create TCP socket */ + sock->fd = wolfIP_sock_socket(sock->stack, AF_INET, + IPSTACK_SOCK_STREAM, 0); + if (sock->fd < 0) { + return MQTT_CODE_ERROR_NETWORK; + } + + sock->stat = SOCK_CONN; + } + FALL_THROUGH; + + case SOCK_CONN: + { + /* Set up address and initiate connect. + * Note: atoip4() only supports dotted-quad IPv4 strings + * (e.g., "192.168.1.1") and does not resolve DNS hostnames. */ + XMEMSET(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(port); + addr.sin_addr.s_addr = atoip4(host); + if (addr.sin_addr.s_addr == 0) { + NetDisconnect(context); + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = wolfIP_sock_connect(sock->stack, sock->fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + if (rc == 0) { + return MQTT_CODE_SUCCESS; + } + if (rc == -WOLFIP_EAGAIN) { + return MQTT_CODE_CONTINUE; + } + + /* Connection failed */ + NetDisconnect(context); + return MQTT_CODE_ERROR_NETWORK; + } + + default: + break; + } + + return MQTT_CODE_ERROR_NETWORK; +} + +static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc; + + (void)timeout_ms; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = wolfIP_sock_recv(sock->stack, sock->fd, buf, buf_len, 0); + /* -WOLFIP_EAGAIN: no data yet; -1: socket not yet in ESTABLISHED state */ + if (rc == -WOLFIP_EAGAIN || rc == -1) { + return MQTT_CODE_CONTINUE; + } + if (rc <= 0) { + return MQTT_CODE_ERROR_NETWORK; + } + return rc; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int rc; + + (void)timeout_ms; + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + rc = wolfIP_sock_send(sock->stack, sock->fd, buf, buf_len, 0); + /* -WOLFIP_EAGAIN: send buffer full; -1: socket not yet in ESTABLISHED state */ + if (rc == -WOLFIP_EAGAIN || rc == -1) { + return MQTT_CODE_CONTINUE; + } + if (rc <= 0) { + return MQTT_CODE_ERROR_NETWORK; + } + return rc; +} + + +/* -------------------------------------------------------------------------- */ +/* GENERIC BSD SOCKET TCP NETWORK CALLBACK EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#else + +#ifndef WOLFMQTT_NO_TIMEOUT +static void tcp_setup_timeout(struct timeval* tv, int timeout_ms) +{ + tv->tv_sec = timeout_ms / 1000; + tv->tv_usec = (timeout_ms % 1000) * 1000; + + /* Make sure there is a minimum value specified */ + if (tv->tv_sec < 0 || (tv->tv_sec == 0 && tv->tv_usec <= 0)) { + tv->tv_sec = 0; + tv->tv_usec = 100; + } +} + +static void tcp_set_fds(SocketContext* sock, fd_set* recvfds, fd_set* errfds) +{ + /* Setup select file descriptors to watch */ + FD_ZERO(errfds); + FD_SET(sock->fd, errfds); + FD_ZERO(recvfds); + FD_SET(sock->fd, recvfds); +#ifdef WOLFMQTT_ENABLE_STDIN_CAP + #ifdef WOLFMQTT_MULTITHREAD + FD_SET(sock->pfd[0], recvfds); + #endif + if (!sock->mqttCtx->test_mode) { + FD_SET(STDIN, recvfds); + } +#endif /* WOLFMQTT_ENABLE_STDIN_CAP */ +} + +#ifdef WOLFMQTT_NONBLOCK +static void tcp_set_nonblocking(SOCKET_T* sockfd) +{ +#ifdef USE_WINDOWS_API + unsigned long blocking = 1; + int ret = ioctlsocket(*sockfd, FIONBIO, &blocking); + if (ret == SOCKET_ERROR) + PRINTF("ioctlsocket failed!"); +#else + int flags = fcntl(*sockfd, F_GETFL, 0); + if (flags < 0) + PRINTF("fcntl get failed!"); + flags = fcntl(*sockfd, F_SETFL, flags | O_NONBLOCK); + if (flags < 0) + PRINTF("fcntl set failed!"); +#endif +} +#endif /* WOLFMQTT_NONBLOCK */ +#endif /* !WOLFMQTT_NO_TIMEOUT */ + +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + SOCK_CLOSE(sock->fd); + sock->fd = SOCKET_INVALID; + } + + sock->stat = SOCK_BEGIN; + } + return 0; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int type = SOCK_STREAM; + int rc = -1; + SOERROR_T so_error = 0; + struct addrinfo *result = NULL; + struct addrinfo hints; + MQTTCtx* mqttCtx = sock->mqttCtx; + + /* Get address information for host and locate IPv4 */ + switch(sock->stat) { + case SOCK_BEGIN: + { + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } + + XMEMSET(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + XMEMSET(&sock->addr, 0, sizeof(sock->addr)); + sock->addr.sin_family = AF_INET; + + rc = getaddrinfo(host, NULL, &hints, &result); + if (rc == 0) { + struct addrinfo* result_i = result; + + if (! result) { + rc = -1; + goto exit; + } + + /* prefer ip4 addresses */ + while (result_i) { + if (result_i->ai_family == AF_INET) + break; + result_i = result_i->ai_next; + } + + if (result_i) { + sock->addr.sin_port = htons(port); + sock->addr.sin_family = AF_INET; + sock->addr.sin_addr = + ((SOCK_ADDR_IN*)(result_i->ai_addr))->sin_addr; + } + else { + rc = -1; + } + + freeaddrinfo(result); + } + if (rc != 0) + goto exit; + + /* Default to error */ + rc = -1; + + /* Create socket */ + sock->fd = SOCK_OPEN(sock->addr.sin_family, type, 0); + if (sock->fd == SOCKET_INVALID) + goto exit; + + sock->stat = SOCK_CONN; + } + FALL_THROUGH; + + case SOCK_CONN: + { + #ifndef WOLFMQTT_NO_TIMEOUT + fd_set fdset; + struct timeval tv; + + /* Setup timeout and FD's */ + tcp_setup_timeout(&tv, timeout_ms); + FD_ZERO(&fdset); + FD_SET(sock->fd, &fdset); + #endif /* !WOLFMQTT_NO_TIMEOUT */ + + #if !defined(WOLFMQTT_NO_TIMEOUT) && defined(WOLFMQTT_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + /* Set socket as non-blocking */ + tcp_set_nonblocking(&sock->fd); + } + #endif + + /* Start connect */ + rc = SOCK_CONNECT(sock->fd, (struct sockaddr*)&sock->addr, + sizeof(sock->addr)); + if (rc < 0) { + /* set default error case */ + rc = MQTT_CODE_ERROR_NETWORK; + #ifdef WOLFMQTT_NONBLOCK + { + /* Check for error */ + GET_SOCK_ERROR(sock->fd, SOL_SOCKET, SO_ERROR, so_error); + } + if ( + #ifndef _WIN32 + (errno == EINPROGRESS) || + #endif + SOCK_EQ_ERROR(so_error)) + { + #ifndef WOLFMQTT_NO_TIMEOUT + /* Wait for connect */ + if (select((int)SELECT_FD(sock->fd), NULL, &fdset, + NULL, &tv) > 0) { + rc = MQTT_CODE_SUCCESS; + } + #else + rc = MQTT_CODE_CONTINUE; + #endif + } + #endif + } + break; + } + + default: + rc = -1; + } /* switch */ + + (void)timeout_ms; + +exit: + if ((rc != 0) && (rc != MQTT_CODE_CONTINUE)) { + NetDisconnect(context); + PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); /* Show error */ + } + + return rc; +} + +#ifdef WOLFMQTT_SN +static int SN_NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + int type = SOCK_DGRAM; + int rc; + SOERROR_T so_error = 0; + struct addrinfo *result = NULL; + struct addrinfo hints; + MQTTCtx* mqttCtx = sock->mqttCtx; + + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use DTLS %d", + host, port, timeout_ms, mqttCtx->use_tls); + + /* Get address information for host and locate IPv4 */ + XMEMSET(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + + XMEMSET(&sock->addr, 0, sizeof(sock->addr)); + sock->addr.sin_family = AF_INET; + + rc = getaddrinfo(host, NULL, &hints, &result); + if (rc == 0) { + struct addrinfo* result_i = result; + + if (! result) { + rc = -1; + goto exit; + } + + /* prefer ip4 addresses */ + while (result_i) { + if (result_i->ai_family == AF_INET) + break; + result_i = result_i->ai_next; + } + + if (result_i) { + sock->addr.sin_port = htons(port); + sock->addr.sin_family = AF_INET; + sock->addr.sin_addr = + ((SOCK_ADDR_IN*)(result_i->ai_addr))->sin_addr; + } + else { + rc = -1; + } + + freeaddrinfo(result); + } + if (rc != 0) + goto exit; + + if (rc == 0) { + /* Create the socket */ + sock->fd = SOCK_OPEN(sock->addr.sin_family, type, 0); + if (sock->fd == SOCKET_INVALID) { + rc = -1; + } + } + + if (rc == 0) + { + #ifndef WOLFMQTT_NO_TIMEOUT + fd_set fdset; + struct timeval tv; + + /* Setup timeout and FD's */ + tcp_setup_timeout(&tv, timeout_ms); + FD_ZERO(&fdset); + FD_SET(sock->fd, &fdset); + #else + (void)timeout_ms; + #endif /* !WOLFMQTT_NO_TIMEOUT */ + + /* Start connect */ + rc = SOCK_CONNECT(sock->fd, (struct sockaddr*)&sock->addr, + sizeof(sock->addr)); + } + + exit: + /* Show error */ + if ((rc != 0) && (rc != MQTT_CODE_CONTINUE)) { + NetDisconnect(context); + PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); + } + + return rc; +} +#endif + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + SocketContext *sock = (SocketContext*)context; + MQTTCtx* mqttCtx; + int rc; + SOERROR_T so_error = 0; +#ifndef WOLFMQTT_NO_TIMEOUT + struct timeval tv; +#endif +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbWriteAlt = 0; + static int testSmallerWrite = 0; +#endif + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->fd == SOCKET_INVALID) + return MQTT_CODE_ERROR_BAD_ARG; + + mqttCtx = sock->mqttCtx; + (void)mqttCtx; + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + if (testNbWriteAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbWriteAlt++; + return MQTT_CODE_CONTINUE; + } + testNbWriteAlt = 0; + if (!testSmallerWrite) { + if (buf_len > 2) { + buf_len /= 2; + } + testSmallerWrite = 1; + } + else { + testSmallerWrite = 0; + } + } +#endif + +#ifndef WOLFMQTT_NO_TIMEOUT + /* Setup timeout */ + tcp_setup_timeout(&tv, timeout_ms); + (void)setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, + sizeof(tv)); +#endif + + rc = (int)SOCK_SEND(sock->fd, buf, buf_len, 0); + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: SOCK_SEND(%d) returned %d, buf_len is %d", + buf_len, rc, buf_len); + #endif + if (rc == -1) { + { + /* Get error */ + GET_SOCK_ERROR(sock->fd, SOL_SOCKET, SO_ERROR, so_error); + } + if (so_error == 0) { + #if defined(USE_WINDOWS_API) && defined(WOLFMQTT_NONBLOCK) + /* assume non-blocking case */ + rc = MQTT_CODE_CONTINUE; + #else + rc = 0; /* Handle signal */ + #endif + } + else { + #ifdef WOLFMQTT_NONBLOCK + if (SOCK_EQ_ERROR(so_error)) { + return MQTT_CODE_CONTINUE; + } + #endif + rc = MQTT_CODE_ERROR_NETWORK; + PRINTF("NetWrite: Error %d", so_error); + } + } + + (void)timeout_ms; + + return rc; +} + +static int NetRead_ex(void *context, byte* buf, int buf_len, + int timeout_ms, byte peek) +{ + SocketContext *sock = (SocketContext*)context; + MQTTCtx* mqttCtx; + int rc = -1, timeout = 0; + SOERROR_T so_error = 0; + int bytes = 0; + int flags = 0; +#ifndef WOLFMQTT_NO_TIMEOUT + fd_set recvfds; + fd_set errfds; + struct timeval tv; +#else + (void)timeout_ms; +#endif +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbReadAlt = 0; + static int testSmallerRead = 0; +#endif + + if (context == NULL || buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->fd == SOCKET_INVALID) + return MQTT_CODE_ERROR_BAD_ARG; + + if (peek == 1) { + flags |= MSG_PEEK; + } + + mqttCtx = sock->mqttCtx; + (void)mqttCtx; + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + if (testNbReadAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbReadAlt++; + return MQTT_CODE_CONTINUE; + } + testNbReadAlt = 0; + if (!testSmallerRead) { + if (buf_len > 2) { + buf_len /= 2; + } + testSmallerRead = 1; + } + else { + testSmallerRead = 0; + } + } +#endif + + /* Loop until buf_len has been read, error or timeout */ + while (bytes < buf_len) { + int do_read = 0; + + #ifndef WOLFMQTT_NO_TIMEOUT + #ifdef WOLFMQTT_NONBLOCK + if (mqttCtx->useNonBlockMode) { + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + /* quick no timeout check if data is available on stdin */ + tcp_setup_timeout(&tv, 0); + + /* Setup select file descriptors to watch */ + tcp_set_fds(sock, &recvfds, &errfds); + + rc = select((int)SELECT_FD(sock->fd), &recvfds, NULL, &errfds, &tv); + if (rc > 0) { + if (FD_ISSET(sock->fd, &recvfds)) { + do_read = 1; + } + else if ((!mqttCtx->test_mode && FD_ISSET(STDIN, &recvfds))) { + return MQTT_CODE_STDIN_WAKE; + } + } + #else + do_read = 1; + #endif + } + else + #endif /* WOLFMQTT_NONBLOCK */ + { + /* Wait for rx data to be available */ + tcp_setup_timeout(&tv, timeout_ms); + + /* Setup select file descriptors to watch */ + tcp_set_fds(sock, &recvfds, &errfds); + + rc = select((int)SELECT_FD(sock->fd), &recvfds, NULL, &errfds, &tv); + if (rc > 0) { + if (FD_ISSET(sock->fd, &recvfds)) { + do_read = 1; + } + /* Check if rx or error */ + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + else if ((!mqttCtx->test_mode && FD_ISSET(STDIN, &recvfds)) + #ifdef WOLFMQTT_MULTITHREAD + || FD_ISSET(sock->pfd[0], &recvfds) + #endif + ) { + return MQTT_CODE_STDIN_WAKE; + } + #endif + if (FD_ISSET(sock->fd, &errfds)) { + rc = -1; + break; + } + } + else { + timeout = 1; + break; /* timeout or signal */ + } + } + #else + do_read = 1; + #endif /* !WOLFMQTT_NO_TIMEOUT */ + + if (do_read) { + /* Try and read number of buf_len provided, + * minus what's already been read */ + rc = (int)SOCK_RECV(sock->fd, + &buf[bytes], + buf_len - bytes, + flags); + #if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: SOCK_RECV(%d) returned %d, buf_len - bytes is %d", + bytes, rc, buf_len - bytes); + #endif + if (rc <= 0) { + rc = -1; + goto exit; /* Error */ + } + else { + /* Clamp return value: defensive check against + * platform API returning more than requested */ + if (rc > buf_len - bytes) { + rc = buf_len - bytes; + } + bytes += rc; /* Data */ + #ifdef ENABLE_MQTT_TLS + if (MqttClient_Flags(&mqttCtx->client, 0, 0) + & MQTT_CLIENT_FLAG_IS_TLS) { + break; + } + #endif + } + } + + /* no timeout and non-block should always exit loop */ + #ifdef WOLFMQTT_NONBLOCK + if (mqttCtx->useNonBlockMode) { + break; + } + #endif + #ifdef WOLFMQTT_NO_TIMEOUT + break; + #endif + } /* while */ + +exit: + + if (rc == 0 && timeout) { + rc = MQTT_CODE_ERROR_TIMEOUT; + } + else if (rc < 0) { + { + /* Get error */ + GET_SOCK_ERROR(sock->fd, SOL_SOCKET, SO_ERROR, so_error); + } + if (so_error == 0) { + rc = 0; /* Handle signal */ + } + else { + #ifdef WOLFMQTT_NONBLOCK + if (SOCK_EQ_ERROR(so_error)) { + return MQTT_CODE_CONTINUE; + } + #endif + rc = MQTT_CODE_ERROR_NETWORK; + PRINTF("NetRead: Error %d", so_error); + } + } + else { + rc = bytes; + } + + return rc; +} + +static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms) +{ + return NetRead_ex(context, buf, buf_len, timeout_ms, 0); +} + +#ifdef WOLFMQTT_SN +static int NetPeek(void *context, byte* buf, int buf_len, int timeout_ms) +{ + return NetRead_ex(context, buf, buf_len, timeout_ms, 1); +} +#endif + +#endif + + +/* Public Functions */ +int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx) +{ +#if defined(USE_WINDOWS_API) && !defined(FREERTOS_TCP) + WSADATA wsd; + WSAStartup(0x0002, &wsd); +#endif + +#ifdef MICROCHIP_MPLAB_HARMONY + static IPV4_ADDR dwLastIP[2] = { {-1}, {-1} }; + IPV4_ADDR ipAddr; + int Dummy; + int nNets; + int i; + SYS_STATUS stat; + TCPIP_NET_HANDLE netH; + + stat = TCPIP_STACK_Status(sysObj.tcpip); + if (stat < 0) { + return MQTT_CODE_CONTINUE; + } + + nNets = TCPIP_STACK_NumberOfNetworksGet(); + for (i = 0; i < nNets; i++) { + netH = TCPIP_STACK_IndexToNet(i); + ipAddr.Val = TCPIP_STACK_NetAddress(netH); + if (ipAddr.v[0] == 0) { + return MQTT_CODE_CONTINUE; + } + if (dwLastIP[i].Val != ipAddr.Val) { + dwLastIP[i].Val = ipAddr.Val; + PRINTF("%s", TCPIP_STACK_NetNameGet(netH)); + PRINTF(" IP Address: %d.%d.%d.%d", + ipAddr.v[0], ipAddr.v[1], ipAddr.v[2], ipAddr.v[3]); + } + } +#endif /* MICROCHIP_MPLAB_HARMONY */ + + if (net) { + SocketContext* sockCtx; + + XMEMSET(net, 0, sizeof(MqttNet)); + net->connect = NetConnect; + net->read = NetRead; + net->write = NetWrite; + net->disconnect = NetDisconnect; + + sockCtx = (SocketContext*)WOLFMQTT_MALLOC(sizeof(SocketContext)); + if (sockCtx == NULL) { + return MQTT_CODE_ERROR_MEMORY; + } + net->context = sockCtx; + XMEMSET(sockCtx, 0, sizeof(SocketContext)); +#if defined(ENABLE_MQTT_CURL) + sockCtx->curl = NULL; +#endif +#if !defined(HAVE_NETX) + sockCtx->fd = SOCKET_INVALID; +#endif + sockCtx->stat = SOCK_BEGIN; + sockCtx->mqttCtx = mqttCtx; + #ifdef WOLFMQTT_WOLFIP + if (mqttCtx != NULL) { + sockCtx->stack = mqttCtx->stack; + } + #endif + + #if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) + /* setup the pipe for waking select() */ + if (pipe(sockCtx->pfd) != 0) { + PRINTF("Failed to set up pipe for stdin"); + return -1; + } + #endif + } + + return MQTT_CODE_SUCCESS; +} + +#ifdef WOLFMQTT_SN +int SN_ClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx) +{ + if (net) { + SocketContext* sockCtx; + + XMEMSET(net, 0, sizeof(MqttNet)); + net->connect = SN_NetConnect; + net->read = NetRead; + net->write = NetWrite; + net->peek = NetPeek; + net->disconnect = NetDisconnect; + + sockCtx = (SocketContext*)WOLFMQTT_MALLOC(sizeof(SocketContext)); + if (sockCtx == NULL) { + return MQTT_CODE_ERROR_MEMORY; + } + net->context = sockCtx; + XMEMSET(sockCtx, 0, sizeof(SocketContext)); + sockCtx->stat = SOCK_BEGIN; + sockCtx->mqttCtx = mqttCtx; + + #if 0 /* TODO: add multicast support */ + MulticastCtx* multi_ctx; + multi_ctx = (MulticastCtx*)WOLFMQTT_MALLOC(sizeof(MulticastCtx)); + if (multi_ctx == NULL) { + return MQTT_CODE_ERROR_MEMORY; + } + net->multi_ctx = multi_ctx; + XMEMSET(multi_ctx, 0, sizeof(MulticastCtx)); + multi_ctx->stat = SOCK_BEGIN; + #endif + + #if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) + /* setup the pipe for waking select() */ + if (pipe(sockCtx->pfd) != 0) { + PRINTF("Failed to set up pipe for stdin"); + return -1; + } + #endif + } + + return MQTT_CODE_SUCCESS; +} +#endif + +int MqttClientNet_DeInit(MqttNet* net) +{ + if (net) { + if (net->context) { + WOLFMQTT_FREE(net->context); + } + XMEMSET(net, 0, sizeof(MqttNet)); + } + return 0; +} + +int MqttClientNet_Wake(MqttNet* net) +{ +#if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) + if (net) { + SocketContext* sockCtx = (SocketContext*)net->context; + if (sockCtx) { + /* wake the select() */ + if (write(sockCtx->pfd[1], "\n", 1) < 0) { + PRINTF("Failed to wake select"); + return -1; + } + } + } +#else + (void)net; +#endif + return 0; +} diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttnet.h b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttnet.h new file mode 100644 index 0000000..4b433fc --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttnet.h @@ -0,0 +1,93 @@ +/* mqttnet.h + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_NET_H +#define WOLFMQTT_NET_H + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef ENABLE_MQTT_CURL + #include +#endif + +#include "mqttexample.h" +#include "mqttport.h" + +#if defined(HAVE_NETX) && !defined(WOLFMQTT_NO_NETX_DNS) + /* include the NetX DNS addon header */ + #include "nxd_dns.h" +#endif + + +/* Local context for Net callbacks */ +typedef enum { + SOCK_BEGIN = 0, + SOCK_CONN +} NB_Stat; + +typedef struct _SocketContext { + SOCKET_T fd; + NB_Stat stat; + SOCK_ADDR_IN addr; +#ifdef MICROCHIP_MPLAB_HARMONY + word32 bytes; +#endif +#if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) + /* "self pipe" -> signal wake sleep() */ + SOCKET_T pfd[2]; +#endif +#ifdef ENABLE_MQTT_CURL + CURL * curl; + int bytes; /* track partial read/write */ +#endif +#ifdef ENABLE_MQTT_WEBSOCKET + void* websocket_ctx; +#endif +#ifdef HAVE_NETX +#ifndef WOLFMQTT_NO_NETX_DNS + NX_DNS *dnsPtr; +#endif + NX_IP *ipPtr; + NX_PACKET *nxPacket; + ULONG nxOffset; +#endif +#ifdef WOLFMQTT_WOLFIP + struct wolfIP *stack; +#endif + MQTTCtx* mqttCtx; +} SocketContext; + +/* Functions used to handle the MqttNet structure creation / destruction */ +int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx); +int MqttClientNet_DeInit(MqttNet* net); +#ifdef WOLFMQTT_SN +int SN_ClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx); +#endif + +int MqttClientNet_Wake(MqttNet* net); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_NET_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttport.c b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttport.c new file mode 100644 index 0000000..2a1763b --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttport.c @@ -0,0 +1,105 @@ +/* mqttport.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_client.h" +#include "mqttnet.h" +#include "mqttexample.h" +#include "mqttport.h" + +#ifdef WOLFMQTT_ZEPHYR + +#ifndef NO_FILESYSTEM +#ifndef WOLFSSL_ZEPHYR +XFILE z_fs_open(const char* filename, const char* mode) +{ + XFILE file; + fs_mode_t flags = 0; + + if (mode == NULL) + return NULL; + + /* Parse mode */ + switch (*mode++) { + case 'r': + flags |= FS_O_READ; + break; + case 'w': + flags |= FS_O_WRITE|FS_O_CREATE; + break; + case 'a': + flags |= FS_O_APPEND|FS_O_CREATE; + break; + default: + return NULL; + } + + /* Ignore binary flag */ + if (*mode == 'b') + mode++; + if (*mode == '+') { + flags |= FS_O_READ; + /* Don't add write flag if already appending */ + if (!(flags & FS_O_APPEND)) + flags |= FS_O_RDWR; + } + /* Ignore binary flag */ + if (*mode == 'b') + mode++; + /* Incorrect mode string */ + if (*mode != '\0') + return NULL; + + file = (XFILE)WOLFMQTT_MALLOC(sizeof(*file)); + if (file != NULL) { + if (fs_open(file, filename, flags) != 0) { + WOLFMQTT_FREE(file); + file = NULL; + } + } + + return file; +} + +int z_fs_close(XFILE file) +{ + int ret; + + if (file == NULL) + return -1; + ret = (fs_close(file) == 0) ? 0 : -1; + + WOLFMQTT_FREE(file); + + return ret; +} +#endif /* !WOLFSSL_ZEPHYR */ +#endif /* !NO_FILESYSTEM */ + +#else + +/* Default implementations */ + +#endif diff --git a/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttport.h b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttport.h new file mode 100644 index 0000000..b31b361 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/mqttClient/mqttport.h @@ -0,0 +1,325 @@ +/* + * mqttport.h + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_PORT_H +#define WOLFMQTT_PORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* FreeRTOS TCP */ +#ifdef FREERTOS_TCP + #include "FreeRTOS.h" + #include "task.h" + #include "FreeRTOS_IP.h" + #include "FreeRTOS_DNS.h" + #include "FreeRTOS_Sockets.h" + + #define SOCKET_T Socket_t + #define SOCK_ADDR_IN struct freertos_sockaddr + +/* ToppersOS and LWIP */ +#elif defined(TOPPERS) && defined(WOLFSSL_LWIP) + /* lwIP includes. */ + #include "lwip/api.h" + #include "lwip/tcpip.h" + #include "lwip/memp.h" + #include "lwip/stats.h" + #include "lwip/sockets.h" + #include "lwip/netdb.h" + +/* FreeRTOS and LWIP */ +#elif defined(FREERTOS) && defined(WOLFSSL_LWIP) + /* Scheduler includes. */ + #include "FreeRTOS.h" + #include "task.h" + #include "semphr.h" + + /* lwIP includes. */ + #include "lwip/api.h" + #include "lwip/tcpip.h" + #include "lwip/memp.h" + #include "lwip/stats.h" + #include "lwip/sockets.h" + #include "lwip/netdb.h" + +/* LWIP only */ +#elif defined(WOLFSSL_LWIP) + /* lwIP includes. */ + #include "lwip/api.h" + #include "lwip/tcpip.h" + #include "lwip/memp.h" + #include "lwip/stats.h" + #include "lwip/sockets.h" + #include "lwip/netdb.h" + +/* wolfIP TCP/IP stack */ +#elif defined(WOLFMQTT_WOLFIP) + #include "wolfip.h" + + #define SOCKET_T int + #define SOCKET_INVALID (-1) + #define SOCK_ADDR_IN struct wolfIP_sockaddr_in + /* For wolfIP targets without filesystem support, define NO_FILESYSTEM + * via build configuration (e.g., compiler flags or user_settings.h). */ + #ifndef NO_FILESYSTEM + #define NO_FILESYSTEM + #endif + +/* User defined IO */ +#elif defined(WOLFMQTT_USER_IO) + #include "userio_template.h" + +/* NetX */ +#elif defined(HAVE_NETX) + #include "nx_api.h" + + #define SOCKET_T NX_TCP_SOCKET + #define SOCK_ADDR_IN NXD_ADDRESS + +/* Windows */ +#elif defined(USE_WINDOWS_API) + #include + #include + #include + #define SOCKET_T SOCKET + #ifdef _WIN32 + #define SOERROR_T int + #else + #define SOERROR_T char + #endif + #define SELECT_FD(fd) (fd) + #ifndef SOCKET_INVALID /* Do not redefine from wolfssl */ + #define SOCKET_INVALID ((SOCKET_T)INVALID_SOCKET) + #endif + #define SOCK_CLOSE closesocket + #define SOCK_SEND(s,b,l,f) send((s), (const char*)(b), (size_t)(l), (f)) + #define SOCK_RECV(s,b,l,f) recv((s), (char*)(b), (size_t)(l), (f)) + #define GET_SOCK_ERROR(f,s,o,e) (e) = WSAGetLastError() + #define SOCK_EQ_ERROR(e) (((e) == WSAEWOULDBLOCK) || ((e) == WSAEINPROGRESS)) + +/* Freescale MQX / RTCS */ +#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) + #if defined(FREESCALE_MQX) + #include + #endif + #include + /* Note: Use "RTCS_geterror(sock->fd);" to get error number */ + #define SOCKET_INVALID RTCS_SOCKET_ERROR + #define SOCKET_T uint32_t + #define SOCK_CLOSE closesocket + #define SOCK_OPEN RTCS_socket + +/* Microchip MPLABX Harmony, TCP/IP */ +#elif defined(MICROCHIP_MPLAB_HARMONY) + #include "app.h" + #include "system_config.h" + #include "tcpip/tcpip.h" + #include + #include + + #define SOCKET_INVALID (-1) + #define SOCK_CLOSE closesocket + + #ifndef WOLFMQTT_NONBLOCK + #error wolfMQTT must be built with WOLFMQTT_NONBLOCK defined for Harmony + #endif + +/* Zephyr RTOS */ +#elif defined(WOLFMQTT_ZEPHYR) + #include + #include + #ifdef CONFIG_POSIX_API + #include + #else + #include + #include + #endif + #ifdef CONFIG_ARCH_POSIX + #include + #else + #include + #endif + + + #define SOCKET_INVALID (-1) + + #ifndef CONFIG_POSIX_API + typedef zsock_fd_set fd_set; + #define FD_ZERO ZSOCK_FD_ZERO + #define FD_SET ZSOCK_FD_SET + #define FD_ISSET ZSOCK_FD_ISSET + #define select zsock_select + #endif + + #ifdef WOLFSSL_ZEPHYR + /* wolfSSL takes care of most defines */ + #include + #else + #define addrinfo zsock_addrinfo + #define getaddrinfo zsock_getaddrinfo + #define freeaddrinfo zsock_freeaddrinfo + #define socket zsock_socket + #define close zsock_close + #define SOCK_CONNECT zsock_connect + #define getsockopt zsock_getsockopt + #define setsockopt zsock_setsockopt + #define send zsock_send + #define recv zsock_recv + #define MSG_PEEK ZSOCK_MSG_PEEK + #ifndef NO_FILESYSTEM + #define XFOPEN z_fs_open + #define XFCLOSE z_fs_close + + #define XFILE struct fs_file_t* + /* These are our wrappers for opening and closing files to + * make the API more POSIX like. Copied from wolfSSL */ + XFILE z_fs_open(const char* filename, const char* mode); + int z_fs_close(XFILE file); + #endif + #endif + + #ifndef NO_FILESYSTEM + #ifndef XFILE + #define XFILE struct fs_file_t* + #endif + #ifndef XFFLUSH + #define XFFLUSH fs_sync + #endif + #ifndef XFSEEK + #define XFSEEK fs_seek + #endif + #ifndef XFTELL + #define XFTELL fs_tell + #endif + #ifndef XFREWIND + #define XFREWIND fs_rewind + #endif + #ifndef XFREAD + #define XFREAD(P,S,N,F) fs_read(F, P, S*N) + #endif + #ifndef XFWRITE + #define XFWRITE(P,S,N,F) fs_write(F, P, S*N) + #endif + #ifndef XSEEK_SET + #define XSEEK_SET FS_SEEK_SET + #endif + #ifndef XSEEK_END + #define XSEEK_END FS_SEEK_END + #endif + #endif + +/* Linux */ +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +/* Setup defaults */ +#ifndef NO_FILESYSTEM +#ifndef XFILE + #define XFILE FILE* +#endif +#ifndef XFOPEN + #define XFOPEN fopen +#endif +#ifndef XFCLOSE + #define XFCLOSE fclose +#endif +#ifndef XFSEEK + #define XFSEEK fseek +#endif +#ifndef XFTELL + #define XFTELL ftell +#endif +#ifndef XFREAD + #define XFREAD fread +#endif +#ifndef XFWRITE + #define XFWRITE fwrite +#endif +#ifndef XSEEK_SET + #define XSEEK_SET SEEK_SET +#endif +#ifndef XSEEK_END + #define XSEEK_END SEEK_END +#endif +#endif /* NO_FILESYSTEM */ +#ifndef SOCK_OPEN + #define SOCK_OPEN socket +#endif +#ifndef SOCKET_T + #define SOCKET_T int +#endif +#ifndef SOERROR_T + #define SOERROR_T int +#endif +#ifndef SELECT_FD + #define SELECT_FD(fd) ((fd) + 1) +#endif +#ifndef SOCKET_INVALID + #define SOCKET_INVALID ((SOCKET_T)0) +#endif +#ifndef SOCK_CONNECT + #define SOCK_CONNECT connect +#endif +#ifndef SOCK_SEND + #define SOCK_SEND(s,b,l,f) send((s), (b), (size_t)(l), (f)) +#endif +#ifndef SOCK_RECV + #define SOCK_RECV(s,b,l,f) recv((s), (b), (size_t)(l), (f)) +#endif +#ifndef SOCK_CLOSE + #define SOCK_CLOSE close +#endif +#ifndef SOCK_ADDR_IN + #define SOCK_ADDR_IN struct sockaddr_in +#endif +#ifdef SOCK_ADDRINFO + #define SOCK_ADDRINFO struct addrinfo +#endif +#ifndef GET_SOCK_ERROR + #define GET_SOCK_ERROR(f,s,o,e) \ + socklen_t len = sizeof(so_error); \ + (void)getsockopt((f), (s), (o), &(e), &len) +#endif +#ifndef SOCK_EQ_ERROR + #define SOCK_EQ_ERROR(e) (((e) == EWOULDBLOCK) || ((e) == EAGAIN)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFMQTT_PORT_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/src/system_init_ns.c b/dm-wolfssl-ota-client-with-zephyr/src/system_init_ns.c new file mode 100644 index 0000000..2c80f1a --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/system_init_ns.c @@ -0,0 +1,32 @@ +/* + * Early SoC initialization for Non-Secure state on MCXN947. + * + * soc_reset_hook() in Zephyr's soc.c skips SystemInit() when + * CONFIG_TRUSTED_EXECUTION_NONSECURE is set, so these initializations + * are placed here via soc_early_init_hook() instead. + */ + +#include +#include "fsl_device_registers.h" + +void soc_early_init_hook(void) +{ + /* Enable coprocessor access in Non-Secure state */ +#if ((__FPU_PRESENT == 1) && (__FPU_USED == 1)) + SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2)); /* CP10, CP11 for FPU */ +#endif + SCB->CPACR |= ((3UL << 0*2) | (3UL << 1*2)); /* CP0, CP1 for PowerQuad */ + + /* Disable RAM ECC to maximize available memory for Ethernet and app buffers */ + SYSCON->ECC_ENABLE_CTRL = 0; + SYSCON->NVM_CTRL |= SYSCON_NVM_CTRL_DIS_MBECC_ERR_DATA_MASK; + + /* Disable flash cache */ + SYSCON->NVM_CTRL |= SYSCON_NVM_CTRL_DIS_FLASH_CACHE_MASK; + + /* Disable aGDET interrupt and reset */ + SPC0->ACTIVE_CFG |= SPC_ACTIVE_CFG_GLITCH_DETECT_DISABLE_MASK; + SPC0->GLITCH_DETECT_SC &= ~SPC_GLITCH_DETECT_SC_LOCK_MASK; + SPC0->GLITCH_DETECT_SC = 0x3C; + SPC0->GLITCH_DETECT_SC |= SPC_GLITCH_DETECT_SC_LOCK_MASK; +} diff --git a/dm-wolfssl-ota-client-with-zephyr/src/user_settings.h b/dm-wolfssl-ota-client-with-zephyr/src/user_settings.h new file mode 100644 index 0000000..3f84068 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/user_settings.h @@ -0,0 +1,441 @@ +/* user_settings.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef USER_SETTINGS_H +#define USER_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* wolfMQTT feature toggles from Zephyr Kconfig */ +#ifdef CONFIG_WOLFMQTT_TLS + #define ENABLE_MQTT_TLS +#endif + + +/* ------------------------------------------------------------------------- */ +/* Platform */ +/* ------------------------------------------------------------------------- */ +#define WOLFSSL_GENERAL_ALIGNMENT 4 /* platform requires 32-bit alignment on uint32_t */ +#define SIZEOF_LONG_LONG 8 /* long long is 8 bytes / 64-bit */ +//#define WOLFSSL_NO_ASM /* optionally disable inline assembly support */ +#define WOLFSSL_IGNORE_FILE_WARN /* ignore file includes not required */ +//#define WOLFSSL_SMALL_STACK /* option to reduce stack size, offload to heap */ +#define BENCH_EMBEDDED /* use smaller buffers in benchmark / tests */ + +/* Network stack */ +/* Default is POSIX sockets */ +//#define WOLFSSL_USER_IO /* Use the SetIO callbacks, not the internal wolfio.c socket code */ +//#define WOLFSSL_LWIP +//#define WOLFSSL_LWIP_NATIVE +//#define FREERTOS_TCP + +/* RTOS */ +/* Default is POSIX mutex and pthreads*/ +//#define SINGLE_THREADED +//#define FREERTOS +#define NO_FILESYSTEM +#define NO_WRITEV +#define NO_MAIN_FUNCTION +#define NO_MAIN_DRIVER +#define NO_ASN_TIME +#define USE_CERT_BUFFERS_2048 +#define USE_ANY_ADDR + +//#define WOLFCRYPT_TEST +//#define WOLFCRYPT_BENCHMARK + +/* ------------------------------------------------------------------------- */ +/* Hardware */ +/* ------------------------------------------------------------------------- */ +/* CryptoCell support */ +#if 0 + //#define WOLFSSL_CRYPTOCELL + //#define WOLFSSL_CRYPTOCELL_AES +#endif +/* PSA support */ +#ifdef CONFIG_MBEDTLS_PSA_CRYPTO_C + #define WOLFSSL_HAVE_PSA + #ifndef SINGLE_THREADED + #define WOLFSSL_PSA_GLOBAL_LOCK + #endif + #define WC_NO_HASHDRBG /* use PSA RNG directly via wc_psa_get_random */ +#endif + +/* ------------------------------------------------------------------------- */ +/* FIPS */ +/* ------------------------------------------------------------------------- */ +#ifdef CONFIG_WOLFCRYPT_FIPS + /* FIPS Ready */ + #define HAVE_FIPS_VERSION 5 + #define HAVE_FIPS_VERSION_MINOR 3 +#endif + + +/* ------------------------------------------------------------------------- */ +/* TLS */ +/* ------------------------------------------------------------------------- */ +/* TLS v1.2 (on by default) */ +#ifdef CONFIG_WOLFSSL_TLS_VERSION_1_2 + #undef WOLFSSL_NO_TLS12 +#else + #define WOLFSSL_NO_TLS12 +#endif +//#define NO_WOLFSSL_SERVER /* Optionally disable TLS server code */ +//#define NO_WOLFSSL_CLIENT /* Optionally disable TLS client code */ + +/* TLS v1.3 */ +#if defined(CONFIG_WOLFSSL_TLS_VERSION_1_3) || defined(CONFIG_WOLFSSL_TLS13_ENABLED) + #define WOLFSSL_TLS13 +#endif + +/* Disable older TLS version prior to 1.2 */ +#define NO_OLD_TLS + +/* Enable default TLS extensions */ +#define HAVE_TLS_EXTENSIONS +#define HAVE_SUPPORTED_CURVES +#define HAVE_EXTENDED_MASTER +#define HAVE_ENCRYPT_THEN_MAC +#define HAVE_SERVER_RENEGOTIATION_INFO +#define HAVE_SNI /* optional Server Name Indicator (SNI) */ + +/* ASN */ +#define WOLFSSL_ASN_TEMPLATE /* use newer ASN template asn.c code (default) */ +#if 0 /* optional space reductions */ + #define WOLFSSL_NO_ASN_STRICT + #define IGNORE_NAME_CONSTRAINTS +#endif + +/* Session Cache */ +#if 1 + #define SMALL_SESSION_CACHE + #ifdef WOLFSSL_TLS13 + #define HAVE_SESSION_TICKET /* session tickets required for resumption in TLS v1.3 */ + #endif +#else + #define NO_SESSION_CACHE /* disable session resumption */ +#endif + +/* PSK */ +#define NO_PSK /* disable pre-shared-key support */ + + +/* ------------------------------------------------------------------------- */ +/* Algorithms */ +/* ------------------------------------------------------------------------- */ +/* RNG */ +#ifndef WC_NO_HASHDRBG + #define HAVE_HASHDRBG /* Use DRBG SHA2-256 and seed */ +#endif + +/* ECC */ +#if 1 + #define HAVE_ECC + #define ECC_USER_CURVES /* Enable only ECC curves specific */ + #undef NO_ECC256 /* Enable SECP256R1 only (on by default) */ + #define NO_ECC192 /* Disable unused curves so preferredGroup[] picks SECP256R1 */ + #define NO_ECC224 + #define NO_ECC384 + #define NO_ECC521 + #define ECC_TIMING_RESISTANT /* Enable Timing Resistance */ + + //#define ECC_SHAMIR /* Optional ECC calculation speed improvement if not using SP implementation */ + //#define WOLFSSL_CUSTOM_CURVES /* enable other curves (not just prime) */ + //#define HAVE_ECC_SECPR2 + //#define HAVE_ECC_SECPR3 + //#define HAVE_ECC_BRAINPOOL + //#define HAVE_ECC_KOBLITZ + //#define HAVE_ECC_CDH /* Co-factor */ + //#define HAVE_COMP_KEY /* Compressed key support */ + //#define FP_ECC /* Fixed point caching - speed repeated operations against same key */ + //#define HAVE_ECC_ENCRYPT + //#define WOLFCRYPT_HAVE_ECCSI + //#define WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT +#endif + +#define WOLFSSL_OLD_PRIME_CHECK /* Use faster DH prime checking */ + +/* RSA */ +#if 1 + #undef NO_RSA + #define WC_RSA_BLINDING + //#define WC_RSA_NO_PADDING + //#define RSA_LOW_MEM + + #if 0 + #define WOLFSSL_KEY_GEN /* For RSA Key gen only */ + #endif + #if defined(WOLFSSL_TLS13) || defined(CONFIG_WOLFSSL_RSA_PSS) + /* TLS v1.3 requires RSA PSS padding */ + #define WC_RSA_PSS + //#define WOLFSSL_PSS_LONG_SALT + #endif +#else + #define NO_RSA +#endif + +/* DH */ +#if 0 + #undef NO_DH /* on by default */ + #define WOLFSSL_DH_CONST /* don't rely on pow/log */ + #define HAVE_FFDHE_2048 + #define HAVE_FFDHE_3072 + #define HAVE_DH_DEFAULT_PARAMS + //#define WOLFSSL_DH_EXTRA /* Enable additional DH key import/export */ +#else + #define NO_DH +#endif + +/* ChaCha20 / Poly1305 */ +#if 1 + #define HAVE_CHACHA + #define HAVE_POLY1305 + + /* Needed for Poly1305 */ + #define HAVE_ONE_TIME_AUTH +#endif + +/* Ed25519 / Curve25519 */ +#if 0 + #define HAVE_CURVE25519 + #define HAVE_ED25519 /* ED25519 Requires SHA512 */ + + /* Optionally use small math (less flash usage, but much slower) */ + //#define CURVED25519_SMALL +#endif + +/* SHA-1 */ +#if 0 + #undef NO_SHA /* on by default */ + //#define USE_SLOW_SHA /* 1k smaller, but 25% slower */ +#else + #define NO_SHA +#endif + +/* SHA2-256 */ +#if 1 + #undef NO_SHA256 /* on by default */ + //#define USE_SLOW_SHA256 /* ~2k smaller and about 25% slower */ + #define WOLFSSL_SHA224 +#else + #define NO_SHA256 +#endif + +/* SHA2-384/512 */ +#if 1 + #define WOLFSSL_SHA384 + #define WOLFSSL_SHA512 + //#define USE_SLOW_SHA512 /* Over twice as small, but 50% slower */ +#endif + +/* SHA-3 */ +#if 1 + #define WOLFSSL_SHA3 +#endif + +/* AES */ +#define HAVE_AES_ECB +/* AES-CBC */ +#if 1 + #define HAVE_AES_CBC +#else + #define NO_AES_CBC +#endif +/* AES-GCM */ +#if 1 + #define HAVE_AESGCM + #define GCM_SMALL /* GCM Method: GCM_TABLE_4BIT, GCM_SMALL, GCM_WORD32 or GCM_TABLE */ + //#define WOLFSSL_AESGCM_STREAM +#endif +//#define HAVE_AES_DECRYPT +//#define WOLFSSL_AES_COUNTER +//#define WOLFSSL_AES_CFB +//#define WOLFSSL_AES_OFB +//#define HAVE_AESCCM +//#define WOLFSSL_AES_XTS + +//#define NO_AES_128 +//#define NO_AES_192 +//#define NO_AES_256 +//#define WOLFSSL_AES_SMALL_TABLES +//#define WOLFSSL_AES_NO_UNROLL + + +/* HKDF */ +#if defined(WOLFSSL_TLS13) || defined(CONFIG_WOLFSSL_HKDF) + #define HAVE_HKDF +#endif + +/* CMAC - Zephyr nRF BTLE needs CMAC */ +#if 1 + #define WOLFSSL_AES_DIRECT + #define WOLFSSL_CMAC +#endif + + +/* Optional Features */ +#define WOLFSSL_BASE64_ENCODE /* Enable Base64 encoding */ +//#define WC_NO_CACHE_RESISTANT /* systems with cache should enable this for AES, ECC, RSA and DH */ +//#define WOLFSSL_CERT_GEN +//#define WOLFSSL_CERT_REQ +//#define WOLFSSL_CERT_EXT +//#define NO_PWDBASED + +/* Disable Algorithms */ +#define NO_DSA +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_DES3 +#define WOLFSSL_NO_SHAKE128 +#define WOLFSSL_NO_SHAKE256 + + + +/* ------------------------------------------------------------------------- */ +/* Math */ +/* ------------------------------------------------------------------------- */ +/* Math Options */ +/* Multi-precision - generic math for all keys sizes and curves */ +#if 1 + #define WOLFSSL_SP_MATH /* no multi-precision math, only single */ +#elif 1 + /* wolf mp math (sp_int.c) */ + #define WOLFSSL_SP_MATH_ALL /* use SP math for all key sizes and curves */ + //#define WOLFSSL_SP_NO_MALLOC + + /* use smaller version of code */ + #define WOLFSSL_SP_SMALL + + /* Define the maximum math bits used */ + #if !defined(NO_RSA) || !defined(NO_DH) + #define SP_INT_BITS 2048 + #elif defined(HAVE_ECC) + #define SP_INT_BITS 256 + #endif + +#elif 1 + /* Fast Math (tfm.c) (stack based and timing resistant) */ + #define USE_FAST_MATH + #define TFM_TIMING_RESISTANT + + /* Define the maximum math bits used (2 * max) */ + #if !defined(NO_RSA) || !defined(NO_DH) + #define FP_MAX_BITS (2*2048) + #ifdef HAVE_ECC + #define ALT_ECC_SIZE /* use heap allocation for ECC point */ + #endif + #elif defined(HAVE_ECC) + #define FP_MAX_BITS (2*256) + #endif + #ifdef HAVE_ECC + //#define TFM_ECC256 /* optional speedup for ECC-256 bit */ + #endif +#else + /* Normal (integer.c) (heap based, not timing resistant) - not recommended */ + #define USE_INTEGER_HEAP_MATH +#endif + +/* Single Precision (optional) */ +/* Math written for specific curves and key sizes */ +#if 1 + #ifdef HAVE_ECC + #define WOLFSSL_HAVE_SP_ECC + //#define WOLFSSL_SP_NO_256 + //#define WOLFSSL_SP_384 + //#define WOLFSSL_SP_521 + #endif + #ifndef NO_RSA + #define WOLFSSL_HAVE_SP_RSA + //#define WOLFSSL_SP_NO_2048 + //#define WOLFSSL_SP_NO_3072 + //#define WOLFSSL_SP_4096 + #endif + #ifndef NO_DH + #define WOLFSSL_HAVE_SP_DH + #endif + + #define WOLFSSL_SP_SMALL /* use smaller version of code */ + //#define WOLFSSL_SP_NO_MALLOC /* disable heap in wolf/SP math */ + //#define SP_DIV_WORD_USE_DIV /* no div64 */ + + #if 0 + /* optional speedup with inline assembly */ + //#define WOLFSSL_SP_ARM_CORTEX_M_ASM /* Cortex-M3+ */ + //#define WOLFSSL_SP_ARM_THUMB_ASM /* Cortex-M0+ thumb */ + //#define WOLFSSL_SP_ARM32_ASM /* Cortex-R */ + //#define WOLFSSL_SP_ARM64_ASM /* Cortex-A */ + //#define WOLFSSL_SP_USE_UDIV + #endif +#endif + +/* ------------------------------------------------------------------------- */ +/* Assembly Speedups for Symmetric Algorithms */ +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_WOLFCRYPT_ARMASM + #define WOLFSSL_ARMASM + #define WOLFSSL_NO_HASH_RAW + #define WOLFSSL_ARMASM_INLINE /* use inline .c versions */ + #define WOLFSSL_ARMASM_NO_NEON + + /* Default is ARMv8 */ + + #if 0 /* ARMv7 */ + #define WOLFSSL_ARM_ARCH 7 + #define WOLFSSL_ARMASM_NO_HW_CRYPTO /* enable if processor does not support aes/sha instructions */ + #endif +#endif + +#ifdef CONFIG_WOLFCRYPT_INTELASM + #define USE_INTEL_SPEEDUP + #define WOLFSSL_X86_64_BUILD /* 64-bit */ + //#define WOLFSSL_X86_BUILD /* 32-bit */ + + /* Issues with building AESNI "_mm_aesimc_si128" always_inline */ + //#define WOLFSSL_AESNI +#endif + + +/* ------------------------------------------------------------------------- */ +/* Debugging */ +/* ------------------------------------------------------------------------- */ +#undef DEBUG_WOLFSSL +#undef NO_ERROR_STRINGS +#ifdef CONFIG_WOLFSSL_DEBUG + #define DEBUG_WOLFSSL + #define DEBUG_WOLFMQTT + #define WOLFMQTT_DEBUG_SOCKET +#else + #if 1 + #define NO_ERROR_STRINGS + #endif +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* USER_SETTINGS_H */ + diff --git a/dm-wolfssl-ota-client-with-zephyr/src/wolfboot_status.h b/dm-wolfssl-ota-client-with-zephyr/src/wolfboot_status.h new file mode 100644 index 0000000..a128a8f --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/src/wolfboot_status.h @@ -0,0 +1,66 @@ +#ifndef WOLFBOOT_STATUS_H +#define WOLFBOOT_STATUS_H + +#include + +typedef enum { + BOOT_STATE_NORMAL, /* BOOT state SUCCESS, running confirmed image */ + BOOT_STATE_AWAITING_CONFIRM, /* BOOT state TESTING, app must call success() */ + BOOT_STATE_VERIFY_FAILED, /* UPDATE state UPDATING + UPDATE>BOOT (sig/integrity) */ + BOOT_STATE_DOWNGRADE_REJECTED, /* UPDATE state UPDATING + UPDATEBOOT (post-rollback) */ + BOOT_STATE_NO_UPDATE, /* fresh / no pending update */ + BOOT_STATE_UNKNOWN +} boot_state_t; + +static inline const char *boot_state_name(boot_state_t s) { + switch (s) { + case BOOT_STATE_NORMAL: return "NORMAL"; + case BOOT_STATE_AWAITING_CONFIRM: return "AWAITING_CONFIRM"; + case BOOT_STATE_VERIFY_FAILED: return "VERIFY_FAILED"; + case BOOT_STATE_DOWNGRADE_REJECTED: return "DOWNGRADE_REJECTED"; + case BOOT_STATE_ROLLED_BACK: return "ROLLED_BACK"; + case BOOT_STATE_NO_UPDATE: return "NO_UPDATE"; + default: return "UNKNOWN"; + } +} + +/* Classify combined state from BOTH partitions: + * boot_state = wolfBoot_get_partition_state(PART_BOOT, ...) + * update_state= wolfBoot_get_partition_state(PART_UPDATE, ...) + * BOOT trailer holds the running image's confirm state (TESTING/SUCCESS). + * UPDATE trailer holds pending-swap diagnostic (UPDATING when interrupted). + */ +static inline boot_state_t boot_state_classify( + uint32_t boot_v, uint32_t update_v, + uint8_t boot_state, int boot_rc, + uint8_t update_state, int update_rc) +{ + /* 1. BOOT partition state - reveals confirm lifecycle */ + if (boot_rc == 0) { + if (boot_state == 0x10) return BOOT_STATE_AWAITING_CONFIRM; /* TESTING */ + if (boot_state == 0x00) return BOOT_STATE_NORMAL; /* SUCCESS */ + } + + /* 2. UPDATE partition UPDATING means swap was started but did not complete */ + if (update_rc == 0 && update_state == 0x70) { + if (update_v > boot_v) return BOOT_STATE_VERIFY_FAILED; + if (update_v < boot_v) return BOOT_STATE_DOWNGRADE_REJECTED; + return BOOT_STATE_UNKNOWN; + } + + /* 3. No conclusive state info: post-rollback or no update at all */ + if (update_v > boot_v) return BOOT_STATE_ROLLED_BACK; + return BOOT_STATE_NO_UPDATE; +} + +/* Shared globals: filled by main(), read by fwclient publisher */ +extern volatile boot_state_t g_boot_state; +extern volatile uint32_t g_boot_version; +extern volatile uint32_t g_update_version; +extern volatile uint8_t g_boot_state_byte; +extern volatile int g_boot_state_rc; +extern volatile uint8_t g_update_state_byte; +extern volatile int g_update_state_rc; + +#endif /* WOLFBOOT_STATUS_H */ diff --git a/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/.config b/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/.config new file mode 100644 index 0000000..5dc52bd --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/.config @@ -0,0 +1,46 @@ +ARCH?=ARM +TZEN?=1 +TARGET?=mcxn +SIGN?=ECC384 +HASH?=SHA384 +MCUXSDK?=1 +MCUXPRESSO?=$(PWD)/../nxp/frdm_mcxn947/mcuxsdk +MCUXPRESSO_CMSIS?=$(PWD)/../nxp/frdm_mcxn947/mcuxsdk/arch/arm/CMSIS +MCUXPRESSO_CPU?=MCXN947VDF_cm33_core0 +MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/MCX/MCXN/MCXN947 +MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/frdmmcxn947/project_template +DEBUG?=0 +DEBUG_APP_JUMP?=1 +DEBUG_UART?=1 +VTOR?=1 +CORTEX_M0?=0 +CORTEX_M33?=1 +NO_ASM?=0 +NO_MPU=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=1 +NO_ARM_ASM=1 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 +PKA?=1 + +CFLAGS_EXTRA+=-DDEBUG_HARDFAULT + +# 8KB sectors +WOLFBOOT_SECTOR_SIZE?=0x2000 + +# Default configuration +WOLFBOOT_KEYVAULT_ADDRESS?=0x1000D000 +WOLFBOOT_KEYVAULT_SIZE?=0 +WOLFBOOT_NSC_ADDRESS?=0x1000E000 +WOLFBOOT_NSC_SIZE?=0x1000 +WOLFBOOT_PARTITION_SIZE?=0xC0000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x10000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0xD0000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x190000 +IMAGE_HEADER_SIZE?=1024 \ No newline at end of file diff --git a/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/0001-Update-configs-and-memory-map.patch b/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/0001-Update-configs-and-memory-map.patch new file mode 100644 index 0000000..55c4fdc --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/0001-Update-configs-and-memory-map.patch @@ -0,0 +1,73 @@ +From bbe155f1af5230471682afc0c1be7220541737a0 Mon Sep 17 00:00:00 2001 +From: Yosuke Shimizu +Date: Fri, 17 Apr 2026 13:08:05 +0900 +Subject: [PATCH] Update configs and memry map + +--- + hal/mcxn.c | 10 +++++++++- + hal/mcxn.ld | 7 ++++--- + 2 files changed, 13 insertions(+), 4 deletions(-) + +diff --git a/hal/mcxn.c b/hal/mcxn.c +index 6344775c..8fae5fd1 100644 +--- a/hal/mcxn.c ++++ b/hal/mcxn.c +@@ -62,7 +62,7 @@ static void hal_sau_init(void) + 0); + + /* Non-secure RAM */ +- sau_init_region(2, 0x20020000, 0x20025FFF, 0); ++ sau_init_region(2, 0x20016000, 0x20065FFF, 0); + + /* Peripherals */ + sau_init_region(3, 0x40000000, 0x4005FFFF, 0); +@@ -85,6 +85,14 @@ static void periph_unsecure(void) + + GPIO_EnablePinControlNonSecure(GPIO0, (1UL << 10) | (1UL << 27)); + GPIO_EnablePinControlNonSecure(GPIO1, (1UL << 2) | (1UL << 8) | (1UL << 9)); ++ ++ /* Disable GDET for voltage configuration on app */ ++ GDET0->GDET_ENABLE1 = 0; ++ GDET1->GDET_ENABLE1 = 0; ++ ++ /* Configure ENET IRQs as non-secure */ ++ NVIC->ITNS[4] |= (1UL << 11) | (1UL << 12) | (1UL << 13); ++ + } + #endif + +diff --git a/hal/mcxn.ld b/hal/mcxn.ld +index 9d9c9feb..0e1f28ce 100644 +--- a/hal/mcxn.ld ++++ b/hal/mcxn.ld +@@ -1,8 +1,8 @@ + MEMORY + { +- FLASH (rx) : ORIGIN = 0x00000000, LENGTH = @WOLFBOOT_KEYVAULT_ADDRESS@ - @ARCH_FLASH_OFFSET@ ++ FLASH (rx) : ORIGIN = 0x10000000, LENGTH = @WOLFBOOT_KEYVAULT_ADDRESS@ - @ARCH_FLASH_OFFSET@ + RAM (rwx) : ORIGIN = 0x30000000, LENGTH = 0x10000 /* 64K */ +- RAM_HEAP (rwx) : ORIGIN = 0x30010000, LENGTH = 0xE000 /* 56K */ ++ RAM_HEAP (rwx) : ORIGIN = 0x30010000, LENGTH = 0x6000 /* 24K */ + FLASH_KEYVAULT (rw) : ORIGIN = @WOLFBOOT_KEYVAULT_ADDRESS@, LENGTH = @WOLFBOOT_KEYVAULT_SIZE@ + FLASH_NSC (rx) : ORIGIN = @WOLFBOOT_NSC_ADDRESS@, LENGTH = @WOLFBOOT_NSC_SIZE@ + } +@@ -30,7 +30,7 @@ SECTIONS + *(.ARM.exidx*) + } > FLASH + +- .gnu.sgstubs ORIGIN(FLASH_NSC) : ++ .gnu.sgstubs : + { + . += 0x400; + . = ALIGN(4); +@@ -43,6 +43,7 @@ SECTIONS + .data : AT (_stored_data) + { + _start_data = .; ++ KEEP(*(.ramcode*)) + KEEP(*(.data*)) + . = ALIGN(4); + _end_data = .; +-- +2.53.0 + diff --git a/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/clock_config.c b/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/clock_config.c new file mode 100644 index 0000000..3991dad --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/clock_config.c @@ -0,0 +1,354 @@ +/* + * Copyright 2022-2023 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*********************************************************************************************************************** + * This file was generated by the MCUXpresso Config Tools. Any manual edits made to this file + * will be overwritten if the respective MCUXpresso Config Tools is used to update this file. + **********************************************************************************************************************/ +/* + * How to setup clock using clock driver functions: + * + * 1. Setup clock sources. + * + * 2. Set up wait states of the flash. + * + * 3. Set up all dividers. + * + * 4. Set up all selectors to provide selected clocks. + * + */ + +/* clang-format off */ +/* TEXT BELOW IS USED AS SETTING FOR TOOLS ************************************* +!!GlobalInfo +product: Clocks v11.0 +processor: MCXN947 +package_id: MCXN947VDF +mcu_data: ksdk2_0 +processor_version: 0.13.10 +board: FRDM-MCXN947 + * BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/ +/* clang-format on */ + +#include "fsl_clock.h" +#include "clock_config.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ +/* System clock frequency. */ +extern uint32_t SystemCoreClock; + +/******************************************************************************* + ************************ BOARD_InitBootClocks function ************************ + ******************************************************************************/ +void BOARD_InitBootClocks(void) +{ + BOARD_BootClockPLL150M(); +} + +/******************************************************************************* + ******************** Configuration BOARD_BootClockFRO12M ********************** + ******************************************************************************/ +/* clang-format off */ +/* TEXT BELOW IS USED AS SETTING FOR TOOLS ************************************* +!!Configuration +name: BOARD_BootClockFRO12M +outputs: +- {id: CLK_144M_clock.outFreq, value: 144 MHz} +- {id: CLK_48M_clock.outFreq, value: 48 MHz} +- {id: FRO_12M_clock.outFreq, value: 12 MHz} +- {id: MAIN_clock.outFreq, value: 12 MHz} +- {id: Slow_clock.outFreq, value: 3 MHz} +- {id: System_clock.outFreq, value: 12 MHz} +- {id: gdet_clock.outFreq, value: 48 MHz} +- {id: trng_clock.outFreq, value: 48 MHz} +settings: +- {id: RunPowerMode, value: OD} +- {id: SCGMode, value: SIRC} +- {id: SCG.SCSSEL.sel, value: SCG.SIRC} +- {id: SCG_FIRCCSR_FIRCEN_CFG, value: Disabled} +- {id: SYSCON.FREQMEREFCLKSEL.sel, value: SYSCON.evtg_out0a} +- {id: SYSCON.FREQMETARGETCLKSEL.sel, value: SYSCON.evtg_out0a} + * BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/ +/* clang-format on */ + +/******************************************************************************* + * Variables for BOARD_BootClockFRO12M configuration + ******************************************************************************/ +/******************************************************************************* + * Code for BOARD_BootClockFRO12M configuration + ******************************************************************************/ +void BOARD_BootClockFRO12M(void) +{ + CLOCK_EnableClock(kCLOCK_Scg); /*!< Enable SCG clock */ + + CLOCK_SetFLASHAccessCyclesForFreq(12000000U, kOD_Mode); /*!< Set the additional number of flash wait-states */ + + /*!< Set up clock selectors - Attach clocks to the peripheries */ + CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch MAIN_CLK to FRO12M */ + + /*!< Set up dividers */ + CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U); /*!< Set AHBCLKDIV divider to value 1 */ + + /* Set SystemCoreClock variable */ + SystemCoreClock = BOARD_BOOTCLOCKFRO12M_CORE_CLOCK; +} + +/******************************************************************************* + ******************* Configuration BOARD_BootClockFROHF48M ********************* + ******************************************************************************/ +/* clang-format off */ +/* TEXT BELOW IS USED AS SETTING FOR TOOLS ************************************* +!!Configuration +name: BOARD_BootClockFROHF48M +outputs: +- {id: CLK_144M_clock.outFreq, value: 144 MHz} +- {id: CLK_48M_clock.outFreq, value: 48 MHz} +- {id: FRO_12M_clock.outFreq, value: 12 MHz} +- {id: FRO_HF_clock.outFreq, value: 48 MHz} +- {id: MAIN_clock.outFreq, value: 48 MHz} +- {id: Slow_clock.outFreq, value: 12 MHz} +- {id: System_clock.outFreq, value: 48 MHz} +- {id: gdet_clock.outFreq, value: 48 MHz} +- {id: trng_clock.outFreq, value: 48 MHz} +settings: +- {id: RunPowerMode, value: OD} +- {id: SYSCON.FLEXSPICLKSEL.sel, value: NO_CLOCK} +- {id: SYSCON.FREQMEREFCLKSEL.sel, value: SYSCON.evtg_out0a} +- {id: SYSCON.FREQMETARGETCLKSEL.sel, value: SYSCON.evtg_out0a} + * BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/ +/* clang-format on */ + +/******************************************************************************* + * Variables for BOARD_BootClockFROHF48M configuration + ******************************************************************************/ +/******************************************************************************* + * Code for BOARD_BootClockFROHF48M configuration + ******************************************************************************/ +void BOARD_BootClockFROHF48M(void) +{ + CLOCK_EnableClock(kCLOCK_Scg); /*!< Enable SCG clock */ + + /* FRO OSC setup - begin, enable the FRO for safety switching */ + CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch to FRO 12M first to ensure we can change the clock setting */ + + CLOCK_SetFLASHAccessCyclesForFreq(48000000U, kOD_Mode); /*!< Set the additional number of flash wait-states */ + + CLOCK_SetupFROHFClocking(48000000U); /*!< Enable FRO HF(48MHz) output */ + /*!< Set up clock selectors - Attach clocks to the peripheries */ + CLOCK_AttachClk(kFRO_HF_to_MAIN_CLK); /*!< Switch MAIN_CLK to FRO_HF */ + + /*!< Set up dividers */ + CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U); /*!< Set AHBCLKDIV divider to value 1 */ + + /* Set SystemCoreClock variable */ + SystemCoreClock = BOARD_BOOTCLOCKFROHF48M_CORE_CLOCK; +} + +/******************************************************************************* + ******************* Configuration BOARD_BootClockFROHF144M ******************** + ******************************************************************************/ +/* clang-format off */ +/* TEXT BELOW IS USED AS SETTING FOR TOOLS ************************************* +!!Configuration +name: BOARD_BootClockFROHF144M +outputs: +- {id: CLK_144M_clock.outFreq, value: 144 MHz} +- {id: CLK_48M_clock.outFreq, value: 48 MHz} +- {id: FRO_12M_clock.outFreq, value: 12 MHz} +- {id: FRO_HF_clock.outFreq, value: 144 MHz} +- {id: MAIN_clock.outFreq, value: 144 MHz} +- {id: Slow_clock.outFreq, value: 36 MHz} +- {id: System_clock.outFreq, value: 144 MHz} +- {id: gdet_clock.outFreq, value: 48 MHz} +- {id: trng_clock.outFreq, value: 48 MHz} +settings: +- {id: RunPowerMode, value: OD} +- {id: SYSCON.AHBCLKDIV.scale, value: '1', locked: true} +- {id: SYSCON.FLEXSPICLKSEL.sel, value: NO_CLOCK} +- {id: SYSCON.FREQMEREFCLKSEL.sel, value: SYSCON.evtg_out0a} +- {id: SYSCON.FREQMETARGETCLKSEL.sel, value: SYSCON.evtg_out0a} +sources: +- {id: SCG.FIRC.outFreq, value: 144 MHz} + * BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/ +/* clang-format on */ + +/******************************************************************************* + * Variables for BOARD_BootClockFROHF144M configuration + ******************************************************************************/ +/******************************************************************************* + * Code for BOARD_BootClockFROHF144M configuration + ******************************************************************************/ +void BOARD_BootClockFROHF144M(void) +{ + CLOCK_EnableClock(kCLOCK_Scg); /*!< Enable SCG clock */ + + /* FRO OSC setup - begin, enable the FRO for safety switching */ + CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch to FRO 12M first to ensure we can change the clock setting */ + + CLOCK_SetFLASHAccessCyclesForFreq(144000000U, kOD_Mode); /*!< Set the additional number of flash wait-states */ + + CLOCK_SetupFROHFClocking(144000000U); /*!< Enable FRO HF(144MHz) output */ + /*!< Set up clock selectors - Attach clocks to the peripheries */ + CLOCK_AttachClk(kFRO_HF_to_MAIN_CLK); /*!< Switch MAIN_CLK to FRO_HF */ + + /*!< Set up dividers */ + CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U); /*!< Set AHBCLKDIV divider to value 1 */ + + /* Set SystemCoreClock variable */ + SystemCoreClock = BOARD_BOOTCLOCKFROHF144M_CORE_CLOCK; +} + +/******************************************************************************* + ******************** Configuration BOARD_BootClockPLL150M ********************* + ******************************************************************************/ +/* clang-format off */ +/* TEXT BELOW IS USED AS SETTING FOR TOOLS ************************************* +!!Configuration +name: BOARD_BootClockPLL150M +called_from_default_init: true +outputs: +- {id: CLK_144M_clock.outFreq, value: 144 MHz} +- {id: CLK_48M_clock.outFreq, value: 48 MHz} +- {id: FRO_12M_clock.outFreq, value: 12 MHz} +- {id: FRO_HF_clock.outFreq, value: 48 MHz} +- {id: MAIN_clock.outFreq, value: 150 MHz} +- {id: PLL0_CLK_clock.outFreq, value: 150 MHz} +- {id: Slow_clock.outFreq, value: 37.5 MHz} +- {id: System_clock.outFreq, value: 150 MHz} +- {id: gdet_clock.outFreq, value: 48 MHz} +- {id: trng_clock.outFreq, value: 48 MHz} +settings: +- {id: PLL0_Mode, value: Normal} +- {id: RunPowerMode, value: OD} +- {id: SCGMode, value: PLL0} +- {id: SCG.PLL0M_MULT.scale, value: '50', locked: true} +- {id: SCG.PLL0SRCSEL.sel, value: SCG.FIRC_48M} +- {id: SCG.PLL0_NDIV.scale, value: '8', locked: true} +- {id: SCG.SCSSEL.sel, value: SCG.PLL0_CLK} +- {id: SYSCON.FLEXSPICLKSEL.sel, value: NO_CLOCK} +- {id: SYSCON.FREQMEREFCLKSEL.sel, value: SYSCON.evtg_out0a} +- {id: SYSCON.FREQMETARGETCLKSEL.sel, value: SYSCON.evtg_out0a} + * BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/ +/* clang-format on */ + +/******************************************************************************* + * Variables for BOARD_BootClockPLL150M configuration + ******************************************************************************/ +/******************************************************************************* + * Code for BOARD_BootClockPLL150M configuration + ******************************************************************************/ +void BOARD_BootClockPLL150M(void) +{ + CLOCK_EnableClock(kCLOCK_Scg); /*!< Enable SCG clock */ + + /* FRO OSC setup - begin, enable the FRO for safety switching */ + CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch to FRO 12M first to ensure we can change the clock setting */ + + CLOCK_SetFLASHAccessCyclesForFreq(150000000U, kOD_Mode); /*!< Set the additional number of flash wait-states */ + + CLOCK_SetupFROHFClocking(48000000U); /*!< Enable FRO HF(48MHz) output */ + /*!< Set up PLL0 */ + const pll_setup_t pll0Setup = { + .pllctrl = SCG_APLLCTRL_SOURCE(1U) | SCG_APLLCTRL_SELI(27U) | SCG_APLLCTRL_SELP(13U), + .pllndiv = SCG_APLLNDIV_NDIV(8U), + .pllpdiv = SCG_APLLPDIV_PDIV(1U), + .pllmdiv = SCG_APLLMDIV_MDIV(50U), + .pllRate = 150000000U + }; + CLOCK_SetPLL0Freq(&pll0Setup); /*!< Configure PLL0 to the desired values */ + CLOCK_SetPll0MonitorMode(kSCG_Pll0MonitorDisable); /* Pll0 Monitor is disabled */ + + /*!< Set up clock selectors - Attach clocks to the peripheries */ + CLOCK_AttachClk(kPLL0_to_MAIN_CLK); /*!< Switch MAIN_CLK to PLL0 */ + + /*!< Set up dividers */ + CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U); /*!< Set AHBCLKDIV divider to value 1 */ + + /* Set SystemCoreClock variable */ + SystemCoreClock = BOARD_BOOTCLOCKPLL150M_CORE_CLOCK; +} + +/******************************************************************************* + ******************** Configuration BOARD_BootClockPLL100M ********************* + ******************************************************************************/ +/* clang-format off */ +/* TEXT BELOW IS USED AS SETTING FOR TOOLS ************************************* +!!Configuration +name: BOARD_BootClockPLL100M +outputs: +- {id: CLK_144M_clock.outFreq, value: 144 MHz} +- {id: CLK_48M_clock.outFreq, value: 48 MHz} +- {id: CLK_IN_clock.outFreq, value: 24 MHz} +- {id: FRO_12M_clock.outFreq, value: 12 MHz} +- {id: MAIN_clock.outFreq, value: 100 MHz} +- {id: PLL1_CLK_clock.outFreq, value: 100 MHz} +- {id: Slow_clock.outFreq, value: 25 MHz} +- {id: System_clock.outFreq, value: 100 MHz} +- {id: gdet_clock.outFreq, value: 48 MHz} +- {id: trng_clock.outFreq, value: 48 MHz} +settings: +- {id: PLL1_Mode, value: Normal} +- {id: RunPowerMode, value: OD} +- {id: SCGMode, value: PLL1} +- {id: SCG.PLL1M_MULT.scale, value: '100', locked: true} +- {id: SCG.PLL1_NDIV.scale, value: '6', locked: true} +- {id: SCG.PLL1_PDIV.scale, value: '4', locked: true} +- {id: SCG.SCSSEL.sel, value: SCG.PLL1_CLK} +- {id: SCG_FIRCCSR_FIRCEN_CFG, value: Disabled} +- {id: SCG_SOSCCSR_SOSCEN_CFG, value: Enabled} +- {id: SYSCON.FREQMEREFCLKSEL.sel, value: SYSCON.evtg_out0a} +- {id: SYSCON.FREQMETARGETCLKSEL.sel, value: SYSCON.evtg_out0a} +sources: +- {id: SCG.SOSC.outFreq, value: 24 MHz, enabled: true} + * BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/ +/* clang-format on */ + +/******************************************************************************* + * Variables for BOARD_BootClockPLL100M configuration + ******************************************************************************/ +/******************************************************************************* + * Code for BOARD_BootClockPLL100M configuration + ******************************************************************************/ +void BOARD_BootClockPLL100M(void) +{ + CLOCK_EnableClock(kCLOCK_Scg); /*!< Enable SCG clock */ + + /* FRO OSC setup - begin, enable the FRO for safety switching */ + CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch to FRO 12M first to ensure we can change the clock setting */ + + CLOCK_SetFLASHAccessCyclesForFreq(100000000U, kOD_Mode); /*!< Set the additional number of flash wait-states */ + + CLOCK_SetupExtClocking(24000000U); + CLOCK_SetSysOscMonitorMode(kSCG_SysOscMonitorDisable); /* System OSC Clock Monitor is disabled */ + + /*!< Set up PLL1 */ + const pll_setup_t pll1Setup = { + .pllctrl = SCG_SPLLCTRL_SOURCE(0U) | SCG_SPLLCTRL_SELI(53U) | SCG_SPLLCTRL_SELP(26U), + .pllndiv = SCG_SPLLNDIV_NDIV(6U), + .pllpdiv = SCG_SPLLPDIV_PDIV(2U), + .pllmdiv = SCG_SPLLMDIV_MDIV(100U), + .pllRate = 100000000U + }; + CLOCK_SetPLL1Freq(&pll1Setup); /*!< Configure PLL1 to the desired values */ + CLOCK_SetPll1MonitorMode(kSCG_Pll1MonitorDisable); /* Pll1 Monitor is disabled */ + + /*!< Set up clock selectors - Attach clocks to the peripheries */ + CLOCK_AttachClk(kPLL1_to_MAIN_CLK); /*!< Switch MAIN_CLK to PLL1 */ + + /*!< Set up dividers */ + CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U); /*!< Set AHBCLKDIV divider to value 1 */ + + /* Set SystemCoreClock variable */ + SystemCoreClock = BOARD_BOOTCLOCKPLL100M_CORE_CLOCK; +} + diff --git a/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/clock_config.h b/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/clock_config.h new file mode 100644 index 0000000..67a2ce9 --- /dev/null +++ b/dm-wolfssl-ota-client-with-zephyr/wolfbootConfig/clock_config.h @@ -0,0 +1,177 @@ +/* + * Copyright 2022-2023 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*********************************************************************************************************************** + * This file was generated by the MCUXpresso Config Tools. Any manual edits made to this file + * will be overwritten if the respective MCUXpresso Config Tools is used to update this file. + **********************************************************************************************************************/ + +#ifndef _CLOCK_CONFIG_H_ +#define _CLOCK_CONFIG_H_ + +#include "fsl_common.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ +#define BOARD_XTAL0_CLK_HZ 24000000U /*!< Board xtal0 frequency in Hz */ + +/******************************************************************************* + ************************ BOARD_InitBootClocks function ************************ + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes default configuration of clocks. + * + */ +void BOARD_InitBootClocks(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +/******************************************************************************* + ******************** Configuration BOARD_BootClockFRO12M ********************** + ******************************************************************************/ +/******************************************************************************* + * Definitions for BOARD_BootClockFRO12M configuration + ******************************************************************************/ +#define BOARD_BOOTCLOCKFRO12M_CORE_CLOCK 12000000U /*!< Core clock frequency: 12000000Hz */ +#define BOARD_BOOTCLOCKFRO12M_ROSC_CLOCK 0U /*!< ROSC clock frequency: 0Hz */ + + +/******************************************************************************* + * API for BOARD_BootClockFRO12M configuration + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes configuration of clocks. + * + */ +void BOARD_BootClockFRO12M(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +/******************************************************************************* + ******************* Configuration BOARD_BootClockFROHF48M ********************* + ******************************************************************************/ +/******************************************************************************* + * Definitions for BOARD_BootClockFROHF48M configuration + ******************************************************************************/ +#define BOARD_BOOTCLOCKFROHF48M_CORE_CLOCK 48000000U /*!< Core clock frequency: 48000000Hz */ +#define BOARD_BOOTCLOCKFROHF48M_ROSC_CLOCK 0U /*!< ROSC clock frequency: 0Hz */ + + +/******************************************************************************* + * API for BOARD_BootClockFROHF48M configuration + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes configuration of clocks. + * + */ +void BOARD_BootClockFROHF48M(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +/******************************************************************************* + ******************* Configuration BOARD_BootClockFROHF144M ******************** + ******************************************************************************/ +/******************************************************************************* + * Definitions for BOARD_BootClockFROHF144M configuration + ******************************************************************************/ +#define BOARD_BOOTCLOCKFROHF144M_CORE_CLOCK 144000000U /*!< Core clock frequency: 144000000Hz */ +#define BOARD_BOOTCLOCKFROHF144M_ROSC_CLOCK 0U /*!< ROSC clock frequency: 0Hz */ + + +/******************************************************************************* + * API for BOARD_BootClockFROHF144M configuration + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes configuration of clocks. + * + */ +void BOARD_BootClockFROHF144M(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +/******************************************************************************* + ******************** Configuration BOARD_BootClockPLL150M ********************* + ******************************************************************************/ +/******************************************************************************* + * Definitions for BOARD_BootClockPLL150M configuration + ******************************************************************************/ +#define BOARD_BOOTCLOCKPLL150M_CORE_CLOCK 150000000U /*!< Core clock frequency: 150000000Hz */ +#define BOARD_BOOTCLOCKPLL150M_ROSC_CLOCK 0U /*!< ROSC clock frequency: 0Hz */ + + +/******************************************************************************* + * API for BOARD_BootClockPLL150M configuration + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes configuration of clocks. + * + */ +void BOARD_BootClockPLL150M(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +/******************************************************************************* + ******************** Configuration BOARD_BootClockPLL100M ********************* + ******************************************************************************/ +/******************************************************************************* + * Definitions for BOARD_BootClockPLL100M configuration + ******************************************************************************/ +#define BOARD_BOOTCLOCKPLL100M_CORE_CLOCK 100000000U /*!< Core clock frequency: 100000000Hz */ +#define BOARD_BOOTCLOCKPLL100M_ROSC_CLOCK 0U /*!< ROSC clock frequency: 0Hz */ + + +/******************************************************************************* + * API for BOARD_BootClockPLL100M configuration + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes configuration of clocks. + * + */ +void BOARD_BootClockPLL100M(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +#endif /* _CLOCK_CONFIG_H_ */ + diff --git a/west.yml b/west.yml index 074dcec..b9fd3ab 100644 --- a/west.yml +++ b/west.yml @@ -13,6 +13,8 @@ manifest: url-base: https://github.com/wolfssl - name: wolftpm url-base: https://github.com/wolfssl + - name: wolfboot + url-base: https://github.com/wolfssl projects: - name: zephyr @@ -40,4 +42,8 @@ manifest: - name: wolftpm path: modules/lib/wolftpm revision: v3.10.0 - remote: wolftpm \ No newline at end of file + remote: wolftpm + - name: wolfboot + path: modules/bootloader/wolfboot + revision: v2.8.0 + remote: wolfboot \ No newline at end of file