From cf77abac9b3f85853f3e9bf430fd1fff76a97677 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Mon, 4 Nov 2024 07:05:26 -0600 Subject: [PATCH] Feature/bacnet secure connect hub (#818) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added BACnet Secure Connect datalink. * Added BACnet/SC hub application --------- Co-authored-by: Kirill Neznamov Co-authored-by: Mikhail Antropov Co-authored-by: Ondřej Hruška Co-authored-by: Patrick Grimm --- .github/workflows/bsc-tests-linux.yml | 81 + .github/workflows/bsc-tests-macos.yml | 75 + .github/workflows/bsc-tests-windows.yml | 85 + .github/workflows/gcc.yml | 26 + .github/workflows/lint.yml | 6 + CMakeLists.txt | 74 +- Makefile | 11 + apps/Makefile | 17 +- apps/lib/Makefile | 27 +- apps/sc-hub/Makefile | 45 + apps/sc-hub/README.md | 27 + apps/sc-hub/main.c | 240 + bin/bsc-client.sh | 39 + bin/bsc-server.sh | 44 + bin/certs/ca_cert.pem | 19 + bin/certs/ca_key.pem | 27 + bin/certs/client_cert.pem | 24 + bin/certs/client_key.pem | 51 + bin/certs/server_cert.pem | 24 + bin/certs/server_key.pem | 51 + configure | 56 + ports/bsd/bsc-event.c | 183 + ports/bsd/websocket-cli.c | 762 + ports/bsd/websocket-global.c | 105 + ports/bsd/websocket-global.h | 27 + ports/bsd/websocket-srv.c | 999 ++ ports/linux/bsc-event.c | 181 + ports/linux/websocket-cli.c | 781 + ports/linux/websocket-global.c | 129 + ports/linux/websocket-global.h | 29 + ports/linux/websocket-srv.c | 1028 ++ ports/win32/bsc-event.c | 145 + ports/win32/websocket-cli.c | 769 + ports/win32/websocket-global.c | 80 + ports/win32/websocket-global.h | 18 + ports/win32/websocket-srv.c | 1004 ++ src/bacnet/bacapp.c | 82 +- src/bacnet/bacapp.h | 7 + src/bacnet/bacdef.h | 4 +- src/bacnet/bacenum.h | 32 + .../basic/object/client/device-client.c | 22 + src/bacnet/basic/object/netport.c | 331 +- src/bacnet/basic/object/netport_internal.h | 107 + src/bacnet/basic/object/sc_netport.c | 2056 +++ src/bacnet/basic/object/sc_netport.h | 415 + src/bacnet/config.h | 32 + src/bacnet/datalink/bsc/README.md | 54 + src/bacnet/datalink/bsc/bsc-conf.h | 169 + src/bacnet/datalink/bsc/bsc-datalink.c | 653 + src/bacnet/datalink/bsc/bsc-datalink.h | 176 + src/bacnet/datalink/bsc/bsc-event.h | 101 + src/bacnet/datalink/bsc/bsc-hub-connector.c | 651 + src/bacnet/datalink/bsc/bsc-hub-connector.h | 81 + src/bacnet/datalink/bsc/bsc-hub-function.c | 535 + src/bacnet/datalink/bsc/bsc-hub-function.h | 62 + src/bacnet/datalink/bsc/bsc-node-switch.c | 1359 ++ src/bacnet/datalink/bsc/bsc-node-switch.h | 126 + src/bacnet/datalink/bsc/bsc-node.c | 1525 ++ src/bacnet/datalink/bsc/bsc-node.h | 162 + src/bacnet/datalink/bsc/bsc-retcodes.h | 20 + src/bacnet/datalink/bsc/bsc-socket.c | 1839 +++ src/bacnet/datalink/bsc/bsc-socket.h | 308 + src/bacnet/datalink/bsc/bsc-util.c | 343 + src/bacnet/datalink/bsc/bsc-util.h | 36 + src/bacnet/datalink/bsc/bvlc-sc.c | 2357 +++ src/bacnet/datalink/bsc/bvlc-sc.h | 425 + src/bacnet/datalink/bsc/websocket.h | 450 + src/bacnet/datalink/datalink.c | 51 +- src/bacnet/datalink/datalink.h | 16 + src/bacnet/datalink/dlenv.c | 230 + src/bacnet/proplist.c | 2 + src/bacnet/secure_connect.c | 1191 ++ src/bacnet/secure_connect.h | 175 + test/CMakeLists.txt | 25 + test/bacnet/bacapp/CMakeLists.txt | 1 + test/bacnet/bacdest/CMakeLists.txt | 1 + test/bacnet/bacdevobjpropref/CMakeLists.txt | 1 + test/bacnet/bactimevalue/CMakeLists.txt | 1 + .../basic/binding/address/CMakeLists.txt | 1 + test/bacnet/basic/object/acc/CMakeLists.txt | 1 + .../object/access_credential/CMakeLists.txt | 1 + .../basic/object/access_door/CMakeLists.txt | 1 + .../basic/object/access_point/CMakeLists.txt | 1 + .../basic/object/access_rights/CMakeLists.txt | 1 + .../basic/object/access_user/CMakeLists.txt | 1 + .../basic/object/access_zone/CMakeLists.txt | 1 + test/bacnet/basic/object/ai/CMakeLists.txt | 1 + test/bacnet/basic/object/ao/CMakeLists.txt | 1 + test/bacnet/basic/object/av/CMakeLists.txt | 1 + .../basic/object/bacfile/CMakeLists.txt | 1 + test/bacnet/basic/object/bacfile/src/main.c | 2 + test/bacnet/basic/object/bi/CMakeLists.txt | 1 + .../object/bitstring_value/CMakeLists.txt | 1 + test/bacnet/basic/object/blo/CMakeLists.txt | 1 + test/bacnet/basic/object/bo/CMakeLists.txt | 1 + test/bacnet/basic/object/bv/CMakeLists.txt | 1 + .../basic/object/calendar/CMakeLists.txt | 1 + .../basic/object/channel/CMakeLists.txt | 1 + .../basic/object/color_object/CMakeLists.txt | 1 + .../object/color_temperature/CMakeLists.txt | 1 + .../basic/object/command/CMakeLists.txt | 1 + .../credential_data_input/CMakeLists.txt | 1 + test/bacnet/basic/object/csv/CMakeLists.txt | 1 + .../bacnet/basic/object/device/CMakeLists.txt | 1 + test/bacnet/basic/object/iv/CMakeLists.txt | 1 + test/bacnet/basic/object/lc/CMakeLists.txt | 1 + test/bacnet/basic/object/lo/CMakeLists.txt | 1 + test/bacnet/basic/object/lsp/CMakeLists.txt | 1 + test/bacnet/basic/object/lsz/CMakeLists.txt | 1 + .../basic/object/ms-input/CMakeLists.txt | 1 + test/bacnet/basic/object/mso/CMakeLists.txt | 1 + test/bacnet/basic/object/msv/CMakeLists.txt | 1 + test/bacnet/basic/object/nc/CMakeLists.txt | 1 + .../basic/object/netport/CMakeLists.txt | 89 +- test/bacnet/basic/object/netport/src/main.c | 685 +- test/bacnet/basic/object/osv/CMakeLists.txt | 1 + test/bacnet/basic/object/piv/CMakeLists.txt | 1 + .../basic/object/schedule/CMakeLists.txt | 1 + .../object/structured_view/CMakeLists.txt | 1 + .../basic/object/time_value/CMakeLists.txt | 1 + .../basic/object/trendlog/CMakeLists.txt | 1 + test/bacnet/channel_value/CMakeLists.txt | 3 + test/bacnet/cov/CMakeLists.txt | 1 + test/bacnet/create_object/CMakeLists.txt | 1 + .../datalink/bsc-datalink/CMakeLists.txt | 303 + test/bacnet/datalink/bsc-datalink/src/main.c | 10838 +++++++++++++ test/bacnet/datalink/bsc-node/CMakeLists.txt | 290 + test/bacnet/datalink/bsc-node/src/main.c | 13100 ++++++++++++++++ .../bacnet/datalink/bsc-socket/CMakeLists.txt | 274 + test/bacnet/datalink/bsc-socket/src/main.c | 10668 +++++++++++++ test/bacnet/datalink/bvlc-sc/CMakeLists.txt | 48 + test/bacnet/datalink/bvlc-sc/src/main.c | 4431 ++++++ test/bacnet/datalink/hub-sc/CMakeLists.txt | 288 + test/bacnet/datalink/hub-sc/src/main.c | 9085 +++++++++++ test/bacnet/datalink/mock/src/bsc-mock.c | 55 + .../bacnet/datalink/websockets/CMakeLists.txt | 140 + test/bacnet/datalink/websockets/src/main.c | 8250 ++++++++++ test/bacnet/delete_object/CMakeLists.txt | 1 + test/bacnet/event/CMakeLists.txt | 1 + test/bacnet/getalarm/CMakeLists.txt | 1 + test/bacnet/getevent/CMakeLists.txt | 1 + test/bacnet/hostnport/CMakeLists.txt | 1 + test/bacnet/list_element/CMakeLists.txt | 1 + test/bacnet/lso/CMakeLists.txt | 1 + test/bacnet/ptransfer/CMakeLists.txt | 1 + test/bacnet/rpm/CMakeLists.txt | 1 + test/bacnet/secure_connect/CMakeLists.txt | 64 + test/bacnet/secure_connect/src/main.c | 75 + test/bacnet/specialevent/CMakeLists.txt | 1 + test/bacnet/timesync/CMakeLists.txt | 1 + test/bacnet/weeklyschedule/CMakeLists.txt | 1 + test/bacnet/wp/CMakeLists.txt | 1 + test/bacnet/wpm/CMakeLists.txt | 1 + test/bacnet/write_group/CMakeLists.txt | 1 + test/ports/bsd/bsc_event/CMakeLists.txt | 51 + test/ports/bsd/bsc_event/src/main.c | 195 + test/ports/linux/bsc_event/CMakeLists.txt | 52 + test/ports/linux/bsc_event/src/main.c | 196 + test/ports/win32/bsc_event/CMakeLists.txt | 48 + test/ports/win32/bsc_event/src/main.c | 213 + test/ztest/src/ztest.c | 2 +- 161 files changed, 82982 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/bsc-tests-linux.yml create mode 100644 .github/workflows/bsc-tests-macos.yml create mode 100644 .github/workflows/bsc-tests-windows.yml create mode 100644 apps/sc-hub/Makefile create mode 100644 apps/sc-hub/README.md create mode 100644 apps/sc-hub/main.c create mode 100755 bin/bsc-client.sh create mode 100755 bin/bsc-server.sh create mode 100644 bin/certs/ca_cert.pem create mode 100644 bin/certs/ca_key.pem create mode 100644 bin/certs/client_cert.pem create mode 100644 bin/certs/client_key.pem create mode 100644 bin/certs/server_cert.pem create mode 100644 bin/certs/server_key.pem create mode 100644 ports/bsd/bsc-event.c create mode 100644 ports/bsd/websocket-cli.c create mode 100644 ports/bsd/websocket-global.c create mode 100644 ports/bsd/websocket-global.h create mode 100644 ports/bsd/websocket-srv.c create mode 100644 ports/linux/bsc-event.c create mode 100644 ports/linux/websocket-cli.c create mode 100644 ports/linux/websocket-global.c create mode 100644 ports/linux/websocket-global.h create mode 100644 ports/linux/websocket-srv.c create mode 100644 ports/win32/bsc-event.c create mode 100644 ports/win32/websocket-cli.c create mode 100644 ports/win32/websocket-global.c create mode 100644 ports/win32/websocket-global.h create mode 100644 ports/win32/websocket-srv.c create mode 100644 src/bacnet/basic/object/netport_internal.h create mode 100644 src/bacnet/basic/object/sc_netport.c create mode 100644 src/bacnet/basic/object/sc_netport.h create mode 100644 src/bacnet/datalink/bsc/README.md create mode 100644 src/bacnet/datalink/bsc/bsc-conf.h create mode 100644 src/bacnet/datalink/bsc/bsc-datalink.c create mode 100644 src/bacnet/datalink/bsc/bsc-datalink.h create mode 100644 src/bacnet/datalink/bsc/bsc-event.h create mode 100644 src/bacnet/datalink/bsc/bsc-hub-connector.c create mode 100644 src/bacnet/datalink/bsc/bsc-hub-connector.h create mode 100644 src/bacnet/datalink/bsc/bsc-hub-function.c create mode 100644 src/bacnet/datalink/bsc/bsc-hub-function.h create mode 100644 src/bacnet/datalink/bsc/bsc-node-switch.c create mode 100644 src/bacnet/datalink/bsc/bsc-node-switch.h create mode 100644 src/bacnet/datalink/bsc/bsc-node.c create mode 100644 src/bacnet/datalink/bsc/bsc-node.h create mode 100644 src/bacnet/datalink/bsc/bsc-retcodes.h create mode 100644 src/bacnet/datalink/bsc/bsc-socket.c create mode 100644 src/bacnet/datalink/bsc/bsc-socket.h create mode 100644 src/bacnet/datalink/bsc/bsc-util.c create mode 100644 src/bacnet/datalink/bsc/bsc-util.h create mode 100644 src/bacnet/datalink/bsc/bvlc-sc.c create mode 100644 src/bacnet/datalink/bsc/bvlc-sc.h create mode 100644 src/bacnet/datalink/bsc/websocket.h create mode 100644 src/bacnet/secure_connect.c create mode 100644 src/bacnet/secure_connect.h create mode 100644 test/bacnet/datalink/bsc-datalink/CMakeLists.txt create mode 100644 test/bacnet/datalink/bsc-datalink/src/main.c create mode 100644 test/bacnet/datalink/bsc-node/CMakeLists.txt create mode 100644 test/bacnet/datalink/bsc-node/src/main.c create mode 100644 test/bacnet/datalink/bsc-socket/CMakeLists.txt create mode 100644 test/bacnet/datalink/bsc-socket/src/main.c create mode 100644 test/bacnet/datalink/bvlc-sc/CMakeLists.txt create mode 100644 test/bacnet/datalink/bvlc-sc/src/main.c create mode 100644 test/bacnet/datalink/hub-sc/CMakeLists.txt create mode 100644 test/bacnet/datalink/hub-sc/src/main.c create mode 100644 test/bacnet/datalink/mock/src/bsc-mock.c create mode 100644 test/bacnet/datalink/websockets/CMakeLists.txt create mode 100644 test/bacnet/datalink/websockets/src/main.c create mode 100644 test/bacnet/secure_connect/CMakeLists.txt create mode 100644 test/bacnet/secure_connect/src/main.c create mode 100644 test/ports/bsd/bsc_event/CMakeLists.txt create mode 100644 test/ports/bsd/bsc_event/src/main.c create mode 100644 test/ports/linux/bsc_event/CMakeLists.txt create mode 100644 test/ports/linux/bsc_event/src/main.c create mode 100644 test/ports/win32/bsc_event/CMakeLists.txt create mode 100644 test/ports/win32/bsc_event/src/main.c diff --git a/.github/workflows/bsc-tests-linux.yml b/.github/workflows/bsc-tests-linux.yml new file mode 100644 index 00000000..c2b6b312 --- /dev/null +++ b/.github/workflows/bsc-tests-linux.yml @@ -0,0 +1,81 @@ +name: BACNet/SC linux tests + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +jobs: + job_bsc_tests_linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Create Build Environment + run: | + sudo apt-get update -qq + sudo apt-get install -qq libconfig-dev + sudo apt-get install -qq libcap-dev + sudo apt-get install -qq libssl-dev + sudo apt-get install -qq libuv1-dev + git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git + bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make' + sudo bash -c 'cd libwebsockets;cd build;make install' + - name: Build and run bsc_event test + run: | + cd ./test/ports/linux/bsc_event + mkdir build + cd build + cmake .. + make + ./test_bsc_event + - name: Build and run bvlc-sc test + run: | + cd ./test/bacnet/datalink/bvlc-sc + mkdir build + cd build + cmake .. + make + ./test_bvlc-sc + - name: Build and run websockets test + run: | + cd ./test/bacnet/datalink/websockets + mkdir build + cd build + cmake .. + make + ./test_websockets + - name: Build and run bsc-socket test + run: | + cd ./test/bacnet/datalink/bsc-socket + mkdir build + cd build + cmake .. + make + ./test_bsc-socket + - name: Build and run hub-sc test + run: | + cd ./test/bacnet/datalink/hub-sc + mkdir build + cd build + cmake .. + make + ./test_hub-sc + - name: Build and run bsc-node test + run: | + cd ./test/bacnet/datalink/bsc-node + mkdir build + cd build + cmake .. + make + ./test_bsc-node + - name: Build and run bsc-datalink test + run: | + cd ./test/bacnet/datalink/bsc-datalink + mkdir build + cd build + cmake .. + make + ./test_bsc-datalink \ No newline at end of file diff --git a/.github/workflows/bsc-tests-macos.yml b/.github/workflows/bsc-tests-macos.yml new file mode 100644 index 00000000..efd20f91 --- /dev/null +++ b/.github/workflows/bsc-tests-macos.yml @@ -0,0 +1,75 @@ +name: BACNet/SC macos/bsd tests + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +jobs: + job_bsc_tests_macos: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v3 + - name: Create Build Environment + run: | + brew install openssl + brew install libwebsockets + - name: Build and run bsc_event test + run: | + cd ./test/ports/bsd/bsc_event + mkdir build + cd build + cmake .. + make + ./test_bsc_event + - name: Build and run bvlc-sc test + run: | + cd ./test/bacnet/datalink/bvlc-sc + mkdir build + cd build + cmake .. + make + ./test_bvlc-sc + - name: Build and run websockets test + run: | + cd ./test/bacnet/datalink/websockets + mkdir build + cd build + cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.0 + make + ./test_websockets + - name: Build and run bsc-socket test + run: | + cd ./test/bacnet/datalink/bsc-socket + mkdir build + cd build + cmake .. + make + ./test_bsc-socket + - name: Build and run hub-sc test + run: | + cd ./test/bacnet/datalink/hub-sc + mkdir build + cd build + cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.0 + make + ./test_hub-sc + - name: Build and run bsc-node test + run: | + cd ./test/bacnet/datalink/bsc-node + mkdir build + cd build + cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.0 + make + ./test_bsc-node + - name: Build and run bsc-datalink test + run: | + cd ./test/bacnet/datalink/bsc-datalink + mkdir build + cd build + cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@3.0 + make + ./test_bsc-datalink diff --git a/.github/workflows/bsc-tests-windows.yml b/.github/workflows/bsc-tests-windows.yml new file mode 100644 index 00000000..81634e86 --- /dev/null +++ b/.github/workflows/bsc-tests-windows.yml @@ -0,0 +1,85 @@ +name: BACNet/SC windows tests + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +jobs: + job_bsc_tests_windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - name: Create Build Environment + run: | + rmdir 'C:\Program Files\OpenSSL' -Recurse -Force + rmdir 'C:\Strawberry' -Recurse -Force + copy c:\vcpkg\triplets\x64-windows.cmake c:\vcpkg\triplets\x64-windows-custom.cmake + "set(VCPKG_BUILD_TYPE release)`n" >> c:\vcpkg\triplets\x64-windows-custom.cmake + c:\vcpkg\vcpkg.exe install pthreads --triplet x64-windows-custom + c:\vcpkg\vcpkg.exe install libuv --triplet x64-windows-custom + c:\vcpkg\vcpkg.exe install openssl --triplet x64-windows-custom + c:\vcpkg\vcpkg.exe install libwebsockets --triplet x64-windows-custom + git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git + cd libwebsockets + mkdir build + cd build + cmake .. -DOPENSSL_ROOT_DIR=c:\vcpkg\installed\x64-windows-custom -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom -DCMAKE_INSTALL_PREFIX="./pkg" -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32 -DLWS_WITH_LIBUV=1 -DLWS_HAVE_PTHREAD_H=1 -DLWS_EXT_PTHREAD_INCLUDE_DIR="C:\vcpkg\installed\x64-windows-custom\include" -DLWS_EXT_PTHREAD_LIBRARIES="C:\vcpkg\installed\x64-windows-custom\lib\pthreadVC3.lib" + cmake --build . --config RELEASE + cmake --install . --config RELEASE + Copy-Item -Path .\pkg\bin -Destination c:\vcpkg\installed\x64-windows-custom -Recurse -Force + Copy-Item -Path .\pkg\include -Destination c:\vcpkg\installed\x64-windows-custom -Recurse -Force + Copy-Item -Path .\pkg\lib -Destination c:\vcpkg\installed\x64-windows-custom -Recurse -Force + Copy-Item -Path .\pkg\share -Destination c:\vcpkg\installed\x64-windows-custom -Recurse -Force + cd ../../ + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1.1 + with: + msbuild-architecture: x64 + - name: Build and run bsc_event test + run: | + cd ./test/ports/win32/bsc_event + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom + msbuild build/test_bsc_event.sln /Property:Configuration=Release + ./build/Release/test_bsc_event.exe + - name: Build and run bvlc-sc test + run: | + cd ./test/bacnet/datalink/bvlc-sc + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom + msbuild build/test_bvlc-sc.sln /Property:Configuration=Release + ./build/Release/test_bvlc-sc.exe + - name: Build and run websockets test + run: | + cd ./test/bacnet/datalink/websockets + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom + msbuild build/test_websockets.sln /Property:Configuration=Release + ./build/Release/test_websockets.exe + - name: Build and run bsc-socket test + run: | + cd ./test/bacnet/datalink/bsc-socket + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom + msbuild build/test_bsc-socket.sln /Property:Configuration=Release + ./build/Release/test_bsc-socket.exe + # (Link target) -> + # bacreal.obj : error LNK2019: unresolved external symbol big_endian referenced in function decode_double [D:\a\bacnet-stack-upstream\bacnet-stack-upstream\test\bacnet\datalink\hub-sc\build\test_hub-sc.vcxproj] + # - name: Build and run hub-sc test + # run: | + # cd ./test/bacnet/datalink/hub-sc + # cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom + # msbuild build/test_hub-sc.sln /Property:Configuration=Release + # ./build/Release/test_hub-sc.exe + - name: Build and run bsc-node test + run: | + cd ./test/bacnet/datalink/bsc-node + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom + msbuild build/test_bsc-node.sln /Property:Configuration=Release + ./build/Release/test_bsc-node.exe + - name: Build and run bsc-datalink test + run: | + cd ./test/bacnet/datalink/bsc-datalink + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-custom + msbuild build/test_bsc-datalink.sln /Property:Configuration=Release + ./build/Release/test_bsc-datalink.exe \ No newline at end of file diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index 5aa3d200..732121e0 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -77,6 +77,26 @@ jobs: make clean make LEGACY=true gateway + bacnet-sc-hub: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Create BACnet/SC Build Environment + run: | + sudo apt-get update -qq + sudo apt-get install -qq libconfig-dev + sudo apt-get install -qq libcap-dev + sudo apt-get install -qq libssl-dev + sudo apt-get install -qq libuv1-dev + git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git + bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make' + sudo bash -c 'cd libwebsockets;cd build;make install' + - name: Build BACnet/SC Hub Demo + run: | + gcc --version + make clean + make sc-hub + router: runs-on: ubuntu-latest steps: @@ -85,6 +105,12 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -qq libconfig-dev + sudo apt-get install -qq libcap-dev + sudo apt-get install -qq libssl-dev + sudo apt-get install -qq libuv1-dev + git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git + bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make' + sudo bash -c 'cd libwebsockets;cd build;make install' - name: Build Router Demo run: | gcc --version diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6dccd994..7020623c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -88,5 +88,11 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -qq lcov + sudo apt-get install -qq libssl-dev + sudo apt-get install -qq libcap-dev + sudo apt-get install -qq libuv1-dev + git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git + bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make' + sudo bash -c 'cd libwebsockets;cd build;make install' - name: Run Unit Test with Code Coverage run: make test diff --git a/CMakeLists.txt b/CMakeLists.txt index a3c2f63f..751a4f88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,13 +64,19 @@ option( "compile with ipv6 datalink support" ON) +option( + BACDL_BSC + "compile with secure-connect support" + OFF) + if(NOT (BACDL_ETHERNET OR BACDL_MSTP OR BACDL_ARCNET OR BACDL_BIP OR BACDL_BIP6 OR + BACDL_BSC OR BACDL_CUSTOM)) - add_definitions(-DBACDL_NONE) + add_definitions(-DBACDL_NONE) endif() option( @@ -174,6 +180,9 @@ elseif(MSVC) # Might be slower if builded with /Qspectre add_compile_options(/wd5045) + + # format string expected in argument 3 is not a string literal. secure_connect.c:1414 + add_compile_options(/wd4774) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") @@ -186,6 +195,15 @@ endif() find_package(Threads) +if(BACDL_BSC) + find_package(libwebsockets CONFIG REQUIRED) + find_package(PkgConfig) + pkg_check_modules(LIB_WEBSOCKETS REQUIRED libwebsockets) + #libwebsocket need C99 with variadic-macros + add_compile_options(-Wno-variadic-macros) + find_package(OpenSSL) +endif() + if(UCI) FIND_PATH(uci_include_dir uci.h) FIND_LIBRARY(uci_lib NAMES uci) @@ -257,12 +275,30 @@ add_library(${PROJECT_NAME} src/bacnet/weeklyschedule.h src/bacnet/special_event.c src/bacnet/special_event.h + src/bacnet/secure_connect.c + src/bacnet/secure_connect.h $<$:src/bacnet/basic/bbmd/h_bbmd.c> $<$:src/bacnet/basic/bbmd/h_bbmd.h> $<$:src/bacnet/basic/bbmd6/h_bbmd6.c> $<$:src/bacnet/basic/bbmd6/h_bbmd6.h> $<$:src/bacnet/basic/bbmd6/vmac.c> $<$:src/bacnet/basic/bbmd6/vmac.h> + $<$:src/bacnet/datalink/bsc/bvlc-sc.c> + $<$:src/bacnet/datalink/bsc/bvlc-sc.h> + $<$:src/bacnet/datalink/bsc/bsc-socket.c> + $<$:src/bacnet/datalink/bsc/bsc-socket.h> + $<$:src/bacnet/datalink/bsc/bsc-util.h> + $<$:src/bacnet/datalink/bsc/bsc-util.c> + $<$:src/bacnet/datalink/bsc/bsc-hub-connector.h> + $<$:src/bacnet/datalink/bsc/bsc-hub-connector.c> + $<$:src/bacnet/datalink/bsc/bsc-hub-function.h> + $<$:src/bacnet/datalink/bsc/bsc-hub-function.c> + $<$:src/bacnet/datalink/bsc/bsc-node-switch.h> + $<$:src/bacnet/datalink/bsc/bsc-node-switch.c> + $<$:src/bacnet/datalink/bsc/bsc-node.h> + $<$:src/bacnet/datalink/bsc/bsc-node.c> + $<$:src/bacnet/datalink/bsc/bsc-datalink.h> + $<$:src/bacnet/datalink/bsc/bsc-datalink.c> src/bacnet/basic/binding/address.c src/bacnet/basic/binding/address.h src/bacnet/basic/npdu/h_npdu.c @@ -340,6 +376,8 @@ add_library(${PROJECT_NAME} src/bacnet/basic/object/nc.h src/bacnet/basic/object/netport.c src/bacnet/basic/object/netport.h + $<$:src/bacnet/basic/object/sc_netport.c> + $<$:src/bacnet/basic/object/sc_netport.h> src/bacnet/basic/object/objects.c src/bacnet/basic/object/objects.h src/bacnet/basic/object/osv.c @@ -623,6 +661,7 @@ target_compile_definitions( PUBLIC BACNET_PROTOCOL_REVISION=${BACNET_PROTOCOL_REVISION} $<$:BACDL_BIP> + $<$:BACDL_BSC> $<$:BACDL_BIP6> $<$:BACDL_ARCNET> $<$:BACDL_MSTP> @@ -633,7 +672,12 @@ target_compile_definitions( $<$>:BACNET_STACK_STATIC_DEFINE> PRIVATE PRINT_ENABLED=1) -target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads) + +if(BACDL_BSC) + target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads ${LIB_WEBSOCKETS_LIBRARIES} ) +else() + target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads) +endif() add_library( ${PROJECT_NAME}::${PROJECT_NAME} @@ -652,6 +696,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") message(STATUS "BACNET: building for linux") set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux) target_link_libraries(${PROJECT_NAME} PUBLIC m) + add_compile_definitions(BACNET_PORT=linux) target_sources(${PROJECT_NAME} PRIVATE ports/linux/bacport.h @@ -663,11 +708,16 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") $<$:ports/linux/rs485.h> $<$:ports/linux/dlmstp.c> $<$:ports/linux/ethernet.c> + $<$:ports/linux/bsc-event.c> + $<$:ports/linux/websocket-cli.c> + $<$:ports/linux/websocket-srv.c> + $<$:ports/linux/websocket-global.c> ports/linux/mstimer-init.c) elseif(WIN32) message(STATUS "BACNET: building for win32") set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/win32) + add_compile_definitions(BACNET_PORT=win32) target_link_libraries(${PROJECT_NAME} PUBLIC wsock32) @@ -685,7 +735,11 @@ elseif(WIN32) # ports/win32/dlmstp-mm.c ports/win32/mstimer-init.c $<$:ports/win32/rs485.c> - $<$:ports/win32/rs485.h>) + $<$:ports/win32/rs485.h> + $<$:ports/win32/bsc-event.c> + $<$:ports/win32/websocket-cli.c> + $<$:ports/win32/websocket-srv.c> + $<$:ports/win32/websocket-global.c>) if(BACDL_ETHERNET) include(ExternalProject) @@ -729,6 +783,10 @@ elseif(APPLE) $<$:ports/bsd/dlmstp.c> ports/bsd/datetime-init.c ports/bsd/mstimer-init.c + $<$:ports/bsd/bsc-event.c> + $<$:ports/bsd/websocket-cli.c> + $<$:ports/bsd/websocket-srv.c> + $<$:ports/bsd/websocket-global.c> ports/bsd/stdbool.h) if(BACDL_ETHERNET) @@ -766,7 +824,8 @@ endif() target_include_directories(${PROJECT_NAME} PUBLIC $ -) + $<$:${OPEN_SSL_DIR}/include> + ) # # examples @@ -874,7 +933,7 @@ if(BACNET_STACK_BUILD_APPS) ) endif(BACNET_BUILD_BACDISCOVER_APP) - if(BACDL_BIP) + if(BACDL_BIP AND (NOT BACDL_BSC)) add_executable(readbdt apps/readbdt/main.c) target_link_libraries(readbdt PRIVATE ${PROJECT_NAME}) @@ -1017,6 +1076,10 @@ if(BACNET_STACK_BUILD_APPS) add_executable(writepropm apps/writepropm/main.c) target_link_libraries(writepropm PRIVATE ${PROJECT_NAME}) +if(BACDL_BSC) + add_executable(sc-hub apps/sc-hub/main.c) + target_link_libraries(sc-hub PRIVATE ${PROJECT_NAME}) +endif() endif() # @@ -1077,6 +1140,7 @@ message(STATUS "BACNET: BACNET_PROTOCOL_REVISION:.......\"${BACNET_PROTOCOL_REVI message(STATUS "BACNET: Selected datalinks:") message(STATUS "BACNET: BACDL_BIP6:.....................\"${BACDL_BIP6}\"") message(STATUS "BACNET: BACDL_BIP:......................\"${BACDL_BIP}\"") +message(STATUS "BACNET: BACDL_BSC:......................\"${BACDL_BSC}\"") message(STATUS "BACNET: BACDL_ARCNET:...................\"${BACDL_ARCNET}\"") message(STATUS "BACNET: BACDL_MSTP:.....................\"${BACDL_MSTP}\"") message(STATUS "BACNET: BACDL_ETHERNET:.................\"${BACDL_ETHERNET}\"") diff --git a/Makefile b/Makefile index 57c6053d..01e6053a 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,12 @@ bip-client: ethernet: $(MAKE) BACDL=ethernet -s -C apps all +# note: requires additional libraries to be installed +# see .github/workflows/gcc.yml +.PHONY: bsc +bsc: + $(MAKE) BACDL=bsc -s -C apps all + .PHONY: apps apps: $(MAKE) -s -C apps all @@ -200,6 +206,10 @@ server-client: server-discover: $(MAKE) LEGACY=true -s -C apps $@ +.PHONY: sc-hub +sc-hub: + $(MAKE) BACDL=bsc -s -C apps $@ + .PHONY: mstpcap mstpcap: $(MAKE) -s -C apps $@ @@ -456,6 +466,7 @@ clean: ports-clean $(MAKE) -s -C apps/router-ipv6 clean $(MAKE) -s -C apps/router-mstp clean $(MAKE) -s -C apps/gateway clean + $(MAKE) -s -C apps/sc-hub clean $(MAKE) -s -C apps/fuzz-afl clean $(MAKE) -s -C apps/fuzz-libfuzzer clean $(MAKE) -s -C ports/lwip clean diff --git a/apps/Makefile b/apps/Makefile index 4683973d..8d99fdad 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -35,6 +35,10 @@ endif ifeq (${BACDL},bip6) BACDL_DEFINE=-DBACDL_BIP6=1 endif +ifeq (${BACDL},bsc) +BACDL_DEFINE=-DBACDL_BSC=1 +BACNET_DEFINE=-DBACFILE=1 +endif ifeq (${BACDL},none) BACDL_DEFINE=-DBACDL_NONE=1 endif @@ -108,11 +112,19 @@ ifeq (${BACNET_PORT},linux) PFLAGS = -pthread TARGET_EXT = SYSTEM_LIB=-lc,-lgcc,-lrt,-lm +ifeq (${BACDL},bsc) +# note: install libwebsockets libssl libcrypto and lcap to build for BACnet/SC +SYSTEM_LIB += -lwebsockets -lssl -lcrypto -lcap +endif endif ifeq (${BACNET_PORT},bsd) PFLAGS = -pthread TARGET_EXT = SYSTEM_LIB=-lc,-lm +ifeq (${BACDL},bsc) +# note: install libwebsockets libssl and libcrypto to build for BACnet/SC +SYSTEM_LIB += -lwebsockets -lssl -lcrypto +endif CSTANDARD = -std=c99 endif ifeq (${BACNET_PORT},win32) @@ -174,7 +186,6 @@ endif BACNET_DEFINES += -DPRINT_ENABLED=1 BACNET_DEFINES += -DBACAPP_ALL -BACNET_DEFINES += -DBACFILE BACNET_DEFINES += -DINTRINSIC_REPORTING BACNET_DEFINES += -DBACNET_TIME_MASTER BACNET_DEFINES += -DBACNET_PROPERTY_LISTS=1 @@ -462,3 +473,7 @@ writepropm: $(BACNET_LIB_TARGET) .PHONY: writegroup writegroup: $(BACNET_LIB_TARGET) $(MAKE) -B -C $@ + +.PHONY: sc-hub +sc-hub: $(BACNET_LIB_TARGET) + $(MAKE) -B -C $@ diff --git a/apps/lib/Makefile b/apps/lib/Makefile index 894ca06e..bd92683d 100644 --- a/apps/lib/Makefile +++ b/apps/lib/Makefile @@ -6,6 +6,7 @@ BACNET_LIB_DIR ?= $(realpath .) BACNET_LIB_TARGET ?= $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a BACNET_SRC_DIR ?= $(realpath ../../src) BACNET_PORT_DIR ?= $(realpath ../../ports/linux) +BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object BACNET_DEFINES ?= #build for release (default) or debug @@ -61,13 +62,34 @@ PORT_BIP6_SRC = \ $(BACNET_SRC_DIR)/bacnet/basic/bbmd6/vmac.c \ $(BACNET_SRC_DIR)/bacnet/datalink/bvlc6.c +PORT_BSC_SRC = \ + $(BACNET_PORT_DIR)/bip-init.c \ + $(BACNET_PORT_DIR)/websocket-global.c \ + $(BACNET_PORT_DIR)/websocket-srv.c \ + $(BACNET_PORT_DIR)/websocket-cli.c \ + $(BACNET_PORT_DIR)/bsc-event.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bvlc.c \ + $(BACNET_SRC_DIR)/bacnet/basic/bbmd/h_bbmd.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-datalink.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-hub-connector.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-hub-function.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-node-switch.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-node.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-socket.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bsc/bsc-util.c \ + $(BACNET_SRC_DIR)/bacnet/datalink/bsc/bvlc-sc.c \ + $(BACNET_OBJECT_DIR)/sc_netport.c \ + $(BACNET_OBJECT_DIR)/bacfile.c \ + $(BACNET_SRC_DIR)/bacnet/secure_connect.c + PORT_ALL_SRC = \ $(BACNET_SRC_DIR)/bacnet/datalink/datalink.c \ $(PORT_ARCNET_SRC) \ $(PORT_MSTP_SRC) \ $(PORT_ETHERNET_SRC) \ $(PORT_BIP_SRC) \ - $(PORT_BIP6_SRC) + $(PORT_BIP6_SRC) \ + $(PORT_BSC_SRC) PORT_NONE_SRC = \ $(BACNET_SRC_DIR)/bacnet/datalink/datalink.c @@ -90,6 +112,9 @@ endif ifeq (${BACDL_DEFINE},-DBACDL_NONE=1) BACNET_PORT_SRC = ${PORT_NONE_SRC} endif +ifeq (${BACDL_DEFINE},-DBACDL_BSC=1) +BACNET_PORT_SRC = ${PORT_BSC_SRC} ${APPS_ENVIRONMENT_SRC} +endif ifeq (${BACDL_DEFINE},-DBACDL_ALL=1) BACNET_PORT_SRC = ${PORT_ALL_SRC} endif diff --git a/apps/sc-hub/Makefile b/apps/sc-hub/Makefile new file mode 100644 index 00000000..6530cede --- /dev/null +++ b/apps/sc-hub/Makefile @@ -0,0 +1,45 @@ +#Makefile to build BACnet Application using GCC compiler + +# Executable file name +TARGET = bacschub +# BACnet objects that are used with this app +BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object +SRC = main.c \ + $(BACNET_OBJECT_DIR)/client/device-client.c \ + $(BACNET_OBJECT_DIR)/netport.c \ + $(BACNET_SRC_DIR)/bacnet/secure_connect.c \ + $(BACNET_OBJECT_DIR)/sc_netport.c + +CFLAGS += -DBACNET_SECURE_CONNECT_ROUTING_TABLE=1 +CFLAGS += -DBSC_CONF_HUB_FUNCTIONS_NUM=1 +CFLAGS += -DBSC_CONF_HUB_CONNECTORS_NUM=1 + +# TARGET_EXT is defined in apps/Makefile as .exe or nothing +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +OBJS += ${SRC:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) -s ) + +-.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +.PHONY: depend +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +.PHONY: clean +clean: + rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map ${BACNET_LIB_TARGET} + +.PHONY: include +include: .depend diff --git a/apps/sc-hub/README.md b/apps/sc-hub/README.md new file mode 100644 index 00000000..e365d9c2 --- /dev/null +++ b/apps/sc-hub/README.md @@ -0,0 +1,27 @@ +# BACnet/SC Hub + +Test BACnet/SC using the following steps: + + * Build apps for bsc datalink: + + make bsc + + * Run hub app: + + cd bin + ./bsc-server.sh + ./bacschub 1 Hubster + + * Run server app: + + cd bin + ./bsc-client.sh + ./bacserv 123 Francine + + * Run any client: + + cd bin + ./bsc-client.sh + ./bacwi + ./bacepics 1 + ./bacepics 123 diff --git a/apps/sc-hub/main.c b/apps/sc-hub/main.c new file mode 100644 index 00000000..d929269f --- /dev/null +++ b/apps/sc-hub/main.c @@ -0,0 +1,240 @@ +/** + * @file + * @brief Sample BACnet/SC hub. + * @author Mikhail Antropov + * @date December 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include "bacnet/config.h" +#include "bacnet/apdu.h" +#include "bacnet/iam.h" +#include "bacnet/version.h" +#include "bacnet/basic/services.h" +#include "bacnet/datalink/dlenv.h" +#include "bacnet/basic/sys/filename.h" +#include "bacnet/basic/sys/mstimer.h" +#include "bacnet/datalink/datalink.h" +/* include the device object */ +#include "bacnet/basic/object/device.h" +#if defined(BAC_UCI) +#include "bacnet/basic/ucix/ucix.h" +#endif /* defined(BAC_UCI) */ +#include "bacnet/datalink/bsc/bsc-datalink.h" +#include "bacnet/datalink/bsc/bsc-event.h" + +/* (Doxygen note: The next two lines pull all the following Javadoc + * into the SCServerDemo module.) */ +/** @addtogroup SCServerDemo */ +/*@{*/ + +#ifndef BACDL_BSC +#error "BACDL_BSC must be defined" +#endif + +/* current version of the BACnet stack */ +static const char *BACnet_Version = BACNET_VERSION_TEXT; + +/** Buffer used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/** Initialize the handlers we will utilize. + * @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler + */ +static void Init_Service_Handlers(void) +{ + Device_Init(NULL); + + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, handler_write_property_multiple); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device); + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, handler_timesync_utc); + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync); +#if defined(BACNET_TIME_MASTER) + handler_timesync_init(); +#endif +} + +static void print_usage(const char *filename) +{ + printf("Usage: %s [device-instance [device-name]]\n", filename); + printf(" [--version][--help]\n"); +} + +static void print_help(const char *filename) +{ + printf( + "Simulate a BACnet/SC HUB device\n" + "device-instance: BACnet Device Object Instance number that you are\n" + "trying simulate.\n" + "device-name: The Device name in ASCII for this device.\n" + "\n"); + printf("Other parameters are passing over environment variables:\n" + "- BACNET_SC_ISSUER_1_CERTIFICATE_FILE: Filename of CA certificate\n" + "- BACNET_SC_OPERATIONAL_CERTIFICATE_FILE: Filename of device " + "certificate\n" + "- BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE: Filename of " + "device certificate key\n" + "- BACNET_SC_HUB_FUNCTION_BINDING: Local port or pair \"interface " + "name:port number\"\n" + "For additional information see file bin/bsc-server.sh\n"); + printf( + "\nExample:\n" + "To simulate Device 111, use following command:\n" + "%s 111\n", + filename); + printf( + "To simulate Device 111 named NoFred, use following command:\n" + "%s 111 NoFred\n", + filename); +} + +/** Main function of server demo. + * + * @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am, + * datalink_receive, npdu_handler, + * dcc_timer_seconds, datalink_maintenance_timer, + * Load_Control_State_Machine_Handler, handler_cov_task, + * tsm_timer_milliseconds + * + * @param argc [in] Arg count. + * @param argv [in] Takes one argument: the Device Instance #. + * @return 0 on success. + */ +int main(int argc, char *argv[]) +{ +#if defined(BAC_UCI) + int uciId = 0; + struct uci_context *ctx; +#endif +#if defined(BACNET_TIME_MASTER) + BACNET_DATE_TIME bdatetime = { 0 }; +#endif + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + int argi = 0; + const char *filename = NULL; + + BACNET_CHARACTER_STRING DeviceName; + uint16_t pdu_len = 0; + unsigned delay_milliseconds = 1; + unsigned elapsed_seconds = 0; + struct mstimer datalink_timer = { 0 }; + + filename = filename_remove_path(argv[0]); + argi = 0; + if ((argc >= 2) && (strcmp(argv[1], "--help") == 0)) { + print_usage(filename); + print_help(filename); + return 0; + } + if ((argc >= 2) && (strcmp(argv[1], "--version") == 0)) { + printf("%s %s\n", filename, BACNET_VERSION_TEXT); + printf("Copyright (C) 2022 by Steve Karg and others.\n" + "This is free software; see the source for copying " + "conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + +#if defined(BAC_UCI) + ctx = ucix_init("bacnet_dev"); + if (!ctx) { + fprintf(stderr, "Failed to load config file bacnet_dev\n"); + } + uciId = ucix_get_option_int(ctx, "bacnet_dev", "0", "Id", 0); + if (uciId != 0) { + Device_Set_Object_Instance_Number(uciId); + } else { +#endif /* defined(BAC_UCI) */ + /* allow the device ID to be set */ + if (++argi < argc) { + Device_Set_Object_Instance_Number(strtol(argv[argi], NULL, 0)); + } + +#if defined(BAC_UCI) + } + ucix_cleanup(ctx); +#endif /* defined(BAC_UCI) */ + + printf( + "BACnet SC Hub Demo\n" + "BACnet Stack Version %s\n" + "BACnet Device ID: %u\n" + "Max APDU: %d\n", + BACnet_Version, Device_Object_Instance_Number(), MAX_APDU); + /* load any static address bindings to show up + in our device bindings list */ + address_init(); + Init_Service_Handlers(); +#if defined(BAC_UCI) + const char *uciname; + ctx = ucix_init("bacnet_dev"); + if (!ctx) { + fprintf(stderr, "Failed to load config file bacnet_dev\n"); + } + uciname = ucix_get_option(ctx, "bacnet_dev", "0", "Name"); + if (uciname != 0) { + Device_Object_Name_ANSI_Init(uciname); + } else { +#endif /* defined(BAC_UCI) */ + if (++argi < argc) { + Device_Object_Name_ANSI_Init(argv[argi]); + } +#if defined(BAC_UCI) + } + ucix_cleanup(ctx); +#endif /* defined(BAC_UCI) */ + if (Device_Object_Name(Device_Object_Instance_Number(), &DeviceName)) { + printf("BACnet Device Name: %s\n", DeviceName.value); + } + dlenv_init(); + atexit(datalink_cleanup); + mstimer_set(&datalink_timer, 1000); + /* loop forever */ + for (;;) { + /* input */ + pdu_len = + datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, delay_milliseconds); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (mstimer_expired(&datalink_timer)) { + elapsed_seconds = mstimer_interval(&datalink_timer) / 1000; + mstimer_reset(&datalink_timer); + datalink_maintenance_timer(elapsed_seconds); +#if defined(BACNET_TIME_MASTER) + Device_getCurrentDateTime(&bdatetime); + handler_timesync_task(&bdatetime); +#endif + } + } + + return 0; +} + +/* @} */ + +/* End group SCServerDemo */ diff --git a/bin/bsc-client.sh b/bin/bsc-client.sh new file mode 100755 index 00000000..4d618f14 --- /dev/null +++ b/bin/bsc-client.sh @@ -0,0 +1,39 @@ +#!/bin/bash +echo "Example of parameters for BACnet/SC clients" + +BACNET_SC_PRIMARY_HUB_URI="wss://127.0.0.1:50050" +export BACNET_SC_PRIMARY_HUB_URI + +BACNET_SC_FAILOVER_HUB_URI="wss://127.0.0.1:5555" +export BACNET_SC_FAILOVER_HUB_URI + +BACNET_SC_ISSUER_1_CERTIFICATE_FILE="certs/ca_cert.pem" +export BACNET_SC_ISSUER_1_CERTIFICATE_FILE + +# Second CA certificate is not used yet +#BACNET_SC_ISSUER_2_CERTIFICATE_FILE="certs/ca_cert2.pem" +#export BACNET_SC_ISSUER_2_CERTIFICATE_FILE + +BACNET_SC_OPERATIONAL_CERTIFICATE_FILE="certs/client_cert.pem" +export BACNET_SC_OPERATIONAL_CERTIFICATE_FILE + +BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE="certs/client_key.pem" +export BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE + +# Need if the device allows the direct connect. +# Possible values: or port number, like "5000" or pair interface name and +# port number, like "eth0:50050" +#BACNET_SC_DIRECT_CONNECT_BINDING="eth0:1234" +#export BACNET_SC_DIRECT_CONNECT_BINDING + +# Set if the device supports the direct connect initiate +# Value: if allow then "1", "y", "Y", otherwise forbidden +#BACNET_SC_DIRECT_CONNECT_INITIATE="y" +#export BACNET_SC_DIRECT_CONNECT_INITIATE + +# List of direct connect accept URLs separated by a space character +#BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS="wss://192.0.0.1:40000 wss://192.0.0.2:6666" +#export BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS + +echo "Launching new shell using the BACnet/SC client environment..." +/bin/bash diff --git a/bin/bsc-server.sh b/bin/bsc-server.sh new file mode 100755 index 00000000..4e50cf26 --- /dev/null +++ b/bin/bsc-server.sh @@ -0,0 +1,44 @@ +#!/bin/bash +echo "Example of parameters for BACnet/SC server" + +BACNET_SC_PRIMARY_HUB_URI="wss://127.0.0.1:50050" +export BACNET_SC_PRIMARY_HUB_URI +BACNET_SC_FAILOVER_HUB_URI="wss://127.0.0.1:50050" +export BACNET_SC_FAILOVER_HUB_URI + +BACNET_SC_ISSUER_1_CERTIFICATE_FILE="certs/ca_cert.pem" +export BACNET_SC_ISSUER_1_CERTIFICATE_FILE + +# Second CA certificate is not used yet +#BACNET_SC_ISSUER_2_CERTIFICATE_FILE="certs/ca_cert2.pem" +#export BACNET_SC_ISSUER_2_CERTIFICATE_FILE + +BACNET_SC_OPERATIONAL_CERTIFICATE_FILE="certs/server_cert.pem" +export BACNET_SC_OPERATIONAL_CERTIFICATE_FILE + +BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE="certs/server_key.pem" +export BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE + +# Need if the device is HUB. +# Possible values: or port number, like "9999" or pair interface name and +# port number, like "eth0:50050" +BACNET_SC_HUB_FUNCTION_BINDING="50050" +export BACNET_SC_HUB_FUNCTION_BINDING + +# Need if the device allows the direct connect. +# Possible values: or port number, like "5000" or pair interface name and +# port number, like "eth0:50050" +#BACNET_SC_DIRECT_CONNECT_BINDING="eth0:1234" +#export BACNET_SC_DIRECT_CONNECT_BINDING + +# Set if the device supports the direct connect initiate +# Value: if allow then "1", "y", "Y", otherwise forbidden +#BACNET_SC_DIRECT_CONNECT_INITIATE="y" +#export BACNET_SC_DIRECT_CONNECT_INITIATE + +# List of direct connect accept URLs separated by a space character +#BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS="wss://192.0.0.1:40000 wss://192.0.0.2:6666" +#export BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS + +echo "Launching new shell using the BACnet/SC server environment..." +/bin/bash diff --git a/bin/certs/ca_cert.pem b/bin/certs/ca_cert.pem new file mode 100644 index 00000000..8572264b --- /dev/null +++ b/bin/certs/ca_cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHDCCAgQCCQDCD53YZJJ7ljANBgkqhkiG9w0BAQsFADBPMRswGQYDVQQKExJs +aWJ3ZWJzb2NrZXRzLXRlc3QxEjAQBgNVBAcTCVhpYW9iaXRhbjEPMA0GA1UECBMG +VGFpcGVpMQswCQYDVQQGEwJUVzAgFw0yMjA3MDYxMTI0MjBaGA8yMDUwMDcxOTEx +MjQyMFowTzEbMBkGA1UEChMSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQHEwlY +aWFvYml0YW4xDzANBgNVBAgTBlRhaXBlaTELMAkGA1UEBhMCVFcwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDV/Oz5VsX3V4R8vaANyPIMaFsygMeWpKan +HMT4kfigUYUHUYeLkbPewZAycCfYsCdBJmsXCXCsfiELpuYnepRetrZ29wTj/b+m +2TjUb2G4Q67CKSHNlh6CQqFvEWnBX+8fYehQT3QwJXN9hVBuPxD+xA+bI6Z28W0k +A0ZL9FfWCT6fODxM7egkSSmKeDgTUsn5wd8R8kQhk4yI2KPUys+nYxzowjNOi8uh +DXzr+Fxg/ps68ViZjGFgXSFHw5ZIvRQTGI2h0T9qmHP0BVFybJu9s6XDYuoM8EJs +WSs+4vOacaTEVYAQF5HxALfCtaOePjSp+ffE+U0CxYF3NH93YKZxAgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBAImJvqnpPdSuFOZT6vtH1pJCEvJ9bSx1CAv6F4FDowKw +CqKSlYEjrILdlB0921OT0vah3lUv/kGNLvXUqTiBakwfRG09aIEnShmyo0hceOh3 +O1KKYvJ2jJGk6PlRxeSg7d5Mi7XgnAdeaxwhuvZZmaIzbhAWP8qgI06P2RBS5BJv +rDD3Dhw8N8GwBD1RY2yKryFQ+4U21Ersw/38cY8UAFTkg3rWr4DWx6tnIfdrN1II +pRqSxHQW4kZag1uZdFcSiaYT+ebLWVXAzbfJLOV8EFF3RDC/A3U3VcORDBDWDhmh +8r4T+6FggYL2ejMmfwbewzI+Hm4SidgOxLf1Fx1d2v4= +-----END CERTIFICATE----- diff --git a/bin/certs/ca_key.pem b/bin/certs/ca_key.pem new file mode 100644 index 00000000..35d888ce --- /dev/null +++ b/bin/certs/ca_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA1fzs+VbF91eEfL2gDcjyDGhbMoDHlqSmpxzE+JH4oFGFB1GH +i5Gz3sGQMnAn2LAnQSZrFwlwrH4hC6bmJ3qUXra2dvcE4/2/ptk41G9huEOuwikh +zZYegkKhbxFpwV/vH2HoUE90MCVzfYVQbj8Q/sQPmyOmdvFtJANGS/RX1gk+nzg8 +TO3oJEkping4E1LJ+cHfEfJEIZOMiNij1MrPp2Mc6MIzTovLoQ186/hcYP6bOvFY +mYxhYF0hR8OWSL0UExiNodE/aphz9AVRcmybvbOlw2LqDPBCbFkrPuLzmnGkxFWA +EBeR8QC3wrWjnj40qfn3xPlNAsWBdzR/d2CmcQIDAQABAoIBACXIxz8vpz0JYwdz +pDNK7BJsys2cF6Ht6M9RMSaC/9evDUJBjyGB1LTckM2XKDIGyKekVPx4WkDa9JLO +IY2PA2vF72oKK/7l61VWvcYk9KhIqy71fFaEz1ZI1aB6/qV6fwqXiyHDJczqj/31 +8EHHOQUDMY4/OUF/V7ooKd13g5rzLAPGNtC7n+VkjA11x8Ol2WbH3dj1xOG/fPRW +rciWFayJr3qVlZ+By1fDCY81MsUVAeUW/T3k9BnKg6hqa3JG67NA6BrczNKSKGy7 +ppVVAeaGDIs7bsCzRpEBCgNhRBZE/AyewMVnBJbKArnryii8/ZbVQUMLVH2ph4Fi +k+0VfVECgYEA/ZE95qKFmqQeO6mvB7kVkpVKSXGu/Y5kFcpLWOc5EVHL6FmfzbqB +Axoq9HhfLy9a7NzsaCw0f4VyZrsOasgB1CBZP602CGi8hbHajJMwJg7B9d8Rqbg5 +vakEudpB1h9jm9NJ5acR7/vcdVpEgpn2nP84Rn/N6reuwRVa8/u+YecCgYEA2Ap6 +O6xM22pNDd3p5yPe868fyf5V5/2Kov4vF8wfisaEcfb/WbJRgwc6/kUUk1lJnxnG +PK98Hmjid5Y39dzilXgtC66XVAu0OJ04lqilsM4BSCdklimphuFszJ3POt6U7hgB +1qsKMKautj3/8v2Swcq6FIx6l6Fo7q+sptVrWecCgYB+6c1TvJCmf/Jp5lomwWqc +vYA7Fl2Bp11M0r23t7OGiaxHlWQ4slqUVOqfBgiOM2ONHl5tHYMBOKez53gl1gmk +RLSm/GkI+HMzb3t11M3KnqRSDd5nVkAAP7KP20AMjhVrDuvzuBVwSl1jl1SnEayv +k8Z08s775gffuHKXn68AjQKBgFXsLNso1tR5PbYjKVD9ikGex+TdW6tNwmK6913e +mDjoZWqyErLI4fRb3tGcBefonghCBv7ByHqLum5Xd2A4foF172xy/sq1cPMHTKdW +4bck54ub5zx1y2/SniPvK6k9NzxOngSTuBTLZjcjB3XL9h9PEpzzlhpSXtp3UhJ0 +VSLHAoGADNykRP2U84xpPm6w1i6rGHqP8ge+OoowiGGKHTEHrV9Syfl/cB9UMeyC +EIhF7Nqk5v+Mh+P1RIRq59fMA4rNzmaxek5rK6EPg1b/gxzEsLVEEBXIQ5WWnXjR +u+ZVI73hb6yVVpuoNhRCgYgEpHknGCEvbURKOhyDMNilIj1mqDM= +-----END RSA PRIVATE KEY----- diff --git a/bin/certs/client_cert.pem b/bin/certs/client_cert.pem new file mode 100644 index 00000000..d0e9eacd --- /dev/null +++ b/bin/certs/client_cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIDEAAEMA0GCSqGSIb3DQEBCwUAME8xGzAZBgNVBAoTEmxp +YndlYnNvY2tldHMtdGVzdDESMBAGA1UEBxMJWGlhb2JpdGFuMQ8wDQYDVQQIEwZU +YWlwZWkxCzAJBgNVBAYTAlRXMCAXDTIyMDcwNjExMjUxOFoYDzIwNTAwNzE5MTEy +NTE4WjBMMQswCQYDVQQGEwJUVzEPMA0GA1UECBMGVGFpcGVpMRswGQYDVQQKExJs +aWJ3ZWJzb2NrZXRzLXRlc3QxDzANBgNVBAMTBmNsaWVudDCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAK8EsjZ4X7w4nBiTgX1ECxhKSgtvzduU6Nin+us5 +A295uCD8C3HMRlh1rnsYqZrwMBfl9uhRs+XyXavBYcJavp6o+D4Zf0PsFAZUPlzI +6iKcMs6WZuB5vjjgin7Ml3Ziy6RclZOK69hkOChUkF0F8mccWzRXYJwq75rZCAUG +75pwcj/sJ3s4VECi5JkD9ko+3kcy6Ml9qgCLghWG1gOg1kvTy1boKkry3kKs9bad +n2aYQXY3yJXgwbs2IAdGnPcgx9ZnnkDgjT2s0QHFlsLvqFVphARJDwyNaYbpw/d9 +xtUXFnZkFK+5B3jzD8Oadwc/qo6mh/uqAk4WQqPmFPYUP+Ph8DDcvELMo2pn+9c+ +YOAoXtuED33J9N1sv2UWrkoLEJ8s4a2c/ViefvMFKo+OEPBOqWES9IeMXiLBDr/i +MDW1WJvFtWx2W3VtUnjMKbRG+xj4FGjv8ic/4SqnK6SCe/yKGgEyvo/afNm90Itv +1E6aiAyIMsmQC1JOoVh/e0qDGqpIVI7A/Osz9tShxDeRuZF9+gMMn0hAJkb5R3Mx +o8X6kDwaszHHvRUsfTfkfW8P1QbHwnMxeSypu0aIxw+c8Dmzt+ls7bj2m26H89Jh +Ylcrhd3Dm/7ahlVfd5yKwAW8vw2wWyECVy7LLZ6cKZWW2mZGKj7zuxkVe9B/MDTu +Tn9zAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAMLpo+SXCF+5sM5hWbJm8wRdN6Ec +MHjT3ta+zytm3oyqYcS/bSCVt0j8eqrZT3scCwwiwSy2s7Zcet5umkFUHb6Jen0Q +lXKBoaSGTWBOZbkiU6O6m6RLMrMmbp2khNag6Yq9p3sfsYz8iE18z6C1vpuKc9QT +PQLQezlhwZZWRNwkCyNRBji9N7U/uOROI8dk6KHnMZR2iR4eCSB2gtkvhPeLUXwX +8Jym2QHX5CY7/YMfs4D0I8fpjSOeeRY3cnJv37LuQL5rJpuvIZxFJ3nVr7V+DTMr +NqNE4iWHMVXqcEbMS/YJj84icGe1pCU01eI/a34rySgUu+3SCUUaXZs/+QA= +-----END CERTIFICATE----- diff --git a/bin/certs/client_key.pem b/bin/certs/client_key.pem new file mode 100644 index 00000000..10ee6159 --- /dev/null +++ b/bin/certs/client_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKQIBAAKCAgEArwSyNnhfvDicGJOBfUQLGEpKC2/N25To2Kf66zkDb3m4IPwL +ccxGWHWuexipmvAwF+X26FGz5fJdq8Fhwlq+nqj4Phl/Q+wUBlQ+XMjqIpwyzpZm +4Hm+OOCKfsyXdmLLpFyVk4rr2GQ4KFSQXQXyZxxbNFdgnCrvmtkIBQbvmnByP+wn +ezhUQKLkmQP2Sj7eRzLoyX2qAIuCFYbWA6DWS9PLVugqSvLeQqz1tp2fZphBdjfI +leDBuzYgB0ac9yDH1meeQOCNPazRAcWWwu+oVWmEBEkPDI1phunD933G1RcWdmQU +r7kHePMPw5p3Bz+qjqaH+6oCThZCo+YU9hQ/4+HwMNy8Qsyjamf71z5g4Che24QP +fcn03Wy/ZRauSgsQnyzhrZz9WJ5+8wUqj44Q8E6pYRL0h4xeIsEOv+IwNbVYm8W1 +bHZbdW1SeMwptEb7GPgUaO/yJz/hKqcrpIJ7/IoaATK+j9p82b3Qi2/UTpqIDIgy +yZALUk6hWH97SoMaqkhUjsD86zP21KHEN5G5kX36AwyfSEAmRvlHczGjxfqQPBqz +Mce9FSx9N+R9bw/VBsfCczF5LKm7RojHD5zwObO36WztuPabbofz0mFiVyuF3cOb +/tqGVV93nIrABby/DbBbIQJXLsstnpwplZbaZkYqPvO7GRV70H8wNO5Of3MCAwEA +AQKCAgBlx3Vb9+S0sLcWE7HaBxfsqEcnH323lIFUfVuOLzmwOji59dkx9Hl0N/uf +2lfHoqZVP4a208yqjKesmmkffWYdHmY+ttUryr5ab+NUUgLW3b8uJIjvQd09dlcU +JiZu0kVa7/yMMN22mZGO4p6RekPdcsAXUDj+mHl+s3fWdJIiXgIS6mKLZdaZQCFw +Wr1/r8/T1+dIRav3ZSnhGuiac44ryp8Viy4NJ+/ZFnxOFpv8Lc7j0Mz1XB9Nn8xA +bvAZRxbvuJJvaCa7FyT0wtNJydU61oHPfC0kP5h+vLMZ2isnAKYvc0QUbKXL0I36 +Unml3YByNKYf1F5Cyu82ITdPY7dC99Uon/G5fRUVnamX4A+NbfGEhlJ2XXUQXTih +AwFuZHrTHAJX9dow4LQQuIhDPOYUNzZo4W04DDJ+buf4gzpNh19YReBDuzg3CTbY +K8REnXvMY4KD6ciMOraxJS1CImgSphVpP7TwOOlbuxj9v1EcTAx4Ih18qrql622V +4Ep4lb/JxWnXNHw/z2dpdMUkJJf3lmn+/4ZV+gNBtByqLRIPTG2l7O4UnR/2iuqx +0wKpY7Th4IfXjSVW6sOc+Jw7d5hsV/z7AUREEJ4WX6NNpBwQZN2VxH07Ixyvk3ZQ +XgVQXNhryNCIWpt/fWivuPCzwt2UtOrp/5WC57w1eeMsXchrAQKCAQEA5KHzL4vW +BXefo6/KhwIJA7y5TSgAbDhYpQi42Llt37NMnJcyKTVmyxpfYG8enp1FqzTVYtce +ZIw3e9T5HGNqUjpoxCp53KcDu8iGF6CQqJMFrk8GkVQU81qXuWoAP2yJfQSmBuhg +PdHKUK1hG1o62DH2gW3en7j3pxCp4Nhp9Ip/59g0LsPDBg6nYi8x5aDNoYmvEVEA +5FRz7u/wwgymGIPgWpYG3c5ks1nQAZGSa7dLFtDMEYOx476wBuHAVSWSApdUM8Vw +k/kcf/vao3Ozqe3os77CoFRwZMNaz/F/z5SwcGwBoe4QgiBo39KXR3ZcW3Hchvzz +F8T6m80xazgTjQKCAQEAw/fUbSxik3x8ZpYPqSJZob0XvKktg7R5DN2W/eLnF4cU +E3p+8Z38LWv7l6rrNGMANHjYDGSZ/gy+H1ou8irT+UdBllXKpqD59ZVUs+ovNszR +hP7wlLM0BgEDX3kn6xRAcZ+tkYSdWyco+ZKOHcfquT/rJ4K5BCeu31HoxtQlxJDQ +R6KjBN+ac7c3l2WRGz/pKj0G952qBCrXLq220TNqDKDDLPT2ZFAkeHwzPX2eeqSu +b4kHEIDzrfMUD5cmTsBPq0LYulguBYxZWsPcm+fWcSnw9s2L+EL27R99IgzCvdGI +qREu/ieZfym1ngeX3VpsPNT6wCUXLacpyJFmYWqe/wKCAQBDuISjzLO0It6ySVuf +6cZpyPjKFdMqOvZmy9KUvzgATseeilpdQmgUONePZNqY+NSuBZQ/FqD1/2Jf15GC +CyBvAsYNdNdruDyu3pJ5ZSH0DLheDSJQ4ari+i5+yRsRrkB7O2lCGoqHRSC8DN76 +jxtm9kWhyL1saglQ/uqSowefW3bPYYbzYyDdJnIF7xzEe4DtHC8y3d95wlokqAWU +NNLt6AIIUu/tuziwtyWZkVjhdwVS2L3ZYjzKzKyvHScvEVXSVqimPRE/g0Yh0qPM +TnUlHEcVFqfnfZtc/VBkz4+gGNa6NgO1xyI0qQznXyVYEYMBwxQjF8GZhMsGYx0E +OTPxAoIBAQC6g1rL2Y2sv7OK09Ht8QKL/HPmHMJz8sWvaAt4coftjaOe8lx1zB6i +gH7BmGwpvPwJ9OXzzZ/++tPM9UFvPTHotFgJJHgu5VFh2pH2drIfHwte4GHLtU+T +U02Jhb9nbyvyuL4ynJxDkF7gQgs+7vJJBDvkq/c5rCc05zl8W5fioM2LEJDFZ6gR +8FRZpNJZtBdQGytRat7Q/pE1HSK9s4iAolWAqVkEmn5LqjH+mVvHcIuRHkL6AzFp +4su6Furf0wtAfevWV2zd3PmCeJ20SRCBPiDYVUOdsuZfQzQOv0Rt80p1VyR5wujF +7cWstZ9ljtUrhK1vSR3pVotfGHvvxdGzAoIBAQC6YpZdqM8zh2EA0uiNUOU/ZcmT +1QlKg+iXKfWZr+adPVPk1dT9yUx/nNwnfCbKcTEyVt7eAziiLsZw6eXOPK9U8zvC +JPUqggDZ3+kbk4jPDRAs0JtIkC0buHzkXF0rgA4bJzuUG0DEsXJXYqHoOm1OVJfs +xoLPthqUD0D2sCY7FbJXgNMa3vYb19kxeJ7v7piBrihLk5LWdAx2lSV0laFE9O8C +h6HaZeR9JIiBycZ9qHIXPPCyZWQOllCIVi1qSQkpQN4vYNhc/IE7MvQ1JBaOblJH +cscILC8aiJVFOSZnNjK/florxtTODD9IG3gHfxTR1sfaLv5zCQf9wOI5RI/x +-----END RSA PRIVATE KEY----- diff --git a/bin/certs/server_cert.pem b/bin/certs/server_cert.pem new file mode 100644 index 00000000..de2c36c6 --- /dev/null +++ b/bin/certs/server_cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEGzCCAwOgAwIBAgIDEAADMA0GCSqGSIb3DQEBCwUAME8xGzAZBgNVBAoTEmxp +YndlYnNvY2tldHMtdGVzdDESMBAGA1UEBxMJWGlhb2JpdGFuMQ8wDQYDVQQIEwZU +YWlwZWkxCzAJBgNVBAYTAlRXMCAXDTIyMDcwNjExMjUwM1oYDzIwNTAwNzE5MTEy +NTAzWjBPMQswCQYDVQQGEwJUVzEPMA0GA1UECBMGVGFpcGVpMRswGQYDVQQKExJs +aWJ3ZWJzb2NrZXRzLXRlc3QxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMtXqhiYhGjVLwVuayvMMIrjK7S4LbRHlNUD +tJzFo2AjuLumteF+KLs4VjYzxudaCQryvMal0d/828un3x4gWjYTOC55cQ9MDNd+ +pmMEE4+d1unTfo9882jM8ejlNFTcDoTPhoN7wqfxO15FniTwTCW90bmz72t6zLOm +3CpZuudHSnXHqtgOz6nSSbTjO1QR3LN+DvN04bMXBfe/0NVE/QpeOOGPZSGCOtKv +DZ4OuyKhm2RorGJnLmXxDFHmBMp00dAcerBERLDkLRpdW48K2i5tBQvAcJZzlL8Q ++H2fguSbB22eUytBbbNngv6hyRy0bAIzaYHwapRIxrLTWndMiB7yOvdislU+1SoF +WAbfV5yuhwOnwTMSodR09OxgUauA2yrAL2Q7BM5GnYLpUo5q5FgAwksJl8wySDpY +cQr7FrzSjqc1o9Us7hEElkvNBZk1+VPTaP5xEYypcbg+u+YCzctJWnJKbCBz2es6 +KkpfC4aWc8UILlUK/W4jRFsNG3UhA1vnIZwK86iqm3k+PEbahcExA4qB/LBhogvs +4z9b2MQ35tiIoQDj7R5xN8c5AywDyb8xGv2Ymk4jGtDkdqrbtIU42+HM1yOk8u8Z +EixLQzowmqUqreBChoAosc3FOROmTCDlY634/acIIP31dedHNYZ51RrukAdA62Db +GDwgwz6TAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKVv/SCPOgwPAY0G47ycvUHg +k3DQgmbxVTFFWQyrP6XvKHOl3eZU2ReJxeTxCMUKcNlW4+eM2oCZ7W1vitaupnO0 +zLW6GBFSVXU4T+YZciIHnxkqlH3XxckgzNVLyWDAAJoaw2c+X1E9BzYbIT+zMYjZ +1bYAavyQOoMPd/1z0dYmI9FrwxZ2OBnv+cXAri0/JMGTczX6fVgSEPGfHHco6SKJ +RZW8VI9gIORuWhy9vxUWACJo0OkJDuU7+WTa9eDfjF+bbh+/Is86frL2gv8y9iMU +t267uX9RRd0FpiKp+pKMHmPwG3pukxWd8vkOrgjJDF+TVHggL1JYXZ7EJ9yjPdw= +-----END CERTIFICATE----- diff --git a/bin/certs/server_key.pem b/bin/certs/server_key.pem new file mode 100644 index 00000000..71edaeb7 --- /dev/null +++ b/bin/certs/server_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAy1eqGJiEaNUvBW5rK8wwiuMrtLgttEeU1QO0nMWjYCO4u6a1 +4X4ouzhWNjPG51oJCvK8xqXR3/zby6ffHiBaNhM4LnlxD0wM136mYwQTj53W6dN+ +j3zzaMzx6OU0VNwOhM+Gg3vCp/E7XkWeJPBMJb3RubPva3rMs6bcKlm650dKdceq +2A7PqdJJtOM7VBHcs34O83ThsxcF97/Q1UT9Cl444Y9lIYI60q8Nng67IqGbZGis +YmcuZfEMUeYEynTR0Bx6sEREsOQtGl1bjwraLm0FC8BwlnOUvxD4fZ+C5JsHbZ5T +K0Fts2eC/qHJHLRsAjNpgfBqlEjGstNad0yIHvI692KyVT7VKgVYBt9XnK6HA6fB +MxKh1HT07GBRq4DbKsAvZDsEzkadgulSjmrkWADCSwmXzDJIOlhxCvsWvNKOpzWj +1SzuEQSWS80FmTX5U9No/nERjKlxuD675gLNy0lackpsIHPZ6zoqSl8LhpZzxQgu +VQr9biNEWw0bdSEDW+chnArzqKqbeT48RtqFwTEDioH8sGGiC+zjP1vYxDfm2Iih +AOPtHnE3xzkDLAPJvzEa/ZiaTiMa0OR2qtu0hTjb4czXI6Ty7xkSLEtDOjCapSqt +4EKGgCixzcU5E6ZMIOVjrfj9pwgg/fV150c1hnnVGu6QB0DrYNsYPCDDPpMCAwEA +AQKCAgEAti3Ie1jJ6JJ3GkqfQhIiu4xkL/mDGS24rlAvlPWNNilK7PSjp288VuBW +fSFOZyBHUcPAVclii2ckFQZF49vgduopP5oWLTpZlwglUjjovZcQYVXW4f90pMRZ +R+tyQ0UUzPCQ92P+NjDum7uLwFR6JKY4LESFkKnolFw+jMLvrR42G+fTrNc4PlHl +L2cWVG/caAPcqUpim+1Yr2rvUuQIQS3BkHTEioz+3ptve9h+2LwpWIBfUZpX+4VH +IGkQicK359hcI2hGkqUbs69IxJNYfpe2GZ1EQccz0SsHNqWew+oRcaiTowFvo3ht +6GxSnqEW07H0KTKYilnZi4XIW3H50QauahbwH0uVsd2U9o4l7T8ZMe4RPmf9wFn/ +Ez4FthbnQOWBo1DxaBmx9Bfn5nC5I6Ryr6XMnnJJ+eAtSjXSAV7/63CrpcB+sBtF +izF3yEQ8rtWACqxjMUPC3Jq9l9398/jzCpZZLvz4NUjjtfNh3i6DP6IhAK13TV7I +4G40brZhmXSVKBnxG7D5iY6PbgVWHFDEjPDwoJktK1BPad3qWf94fmt5ykZd/teq +eRIEvUzJG5PFGwOzxVRHDN8QQmNJcCJuytw4DnsxoC83izfh+nEqEPPQiDFoTLyD ++4VRgFBb6airVcyXWdr20CdOGgXoBMrNBl+E62MYAe2qc67cQYECggEBAO92XNAF +RAwZydzYch5E5ejz9TZbHfef4zNBHU/mV1vh89d9KjSF/OiL3EnoYxPkHgUXyn9A +4C4u1rldZ6dwCVAbZJnT8UF2nLRFMXhaqyJEmT7jCYRy7erd7VL95gVhUOsJYwLm +tYfcik07QmDSPVuGhV2KeknzTa1NSTU8vwQF+wKHi864dmzkslYnQqbFoTVRZtNe +rLb6g6ncw6JWx9yeB07VdpJp6x9TYT+HGj3kb5acuInP+jg7GZzkrHJMnHLweFvb +NWZflPfiANmwM8wMR+kfP2vogGamKCieojVaTi8vgqqjx4UGPd596FNFeiZRvXNk +0dB0AK2Q/126SlECggEBANlitdxHttxh/WqwFK35bcOTXah6PapPdg/72PuOl3Wt +7Wn8At6FIGQ3XZOOPf9v5HfqPpSlNV93AXXaO6ITzwIEb8IG3iEOtTLw292KU8lJ +Ll8XphOkVUrPkzGG6dfVNp6I2hTpYJISwAU5HvKpc9oB0YKhVLfC+idakVC5fZz6 +zFAo27FeoIEdHt92Ze6Q34NJgpVbgmT0+wtuurqoZ/zRtkx45wuqI3E+TDa1Suq3 +4OQse2WcgjzSfdLJtTfyeL1p4VhGycxg+JE5H4ji3z53FxrMnA0mSElyEji88xTG +HCYGInJpZr+QzgPTzWf5R46k08GKsmVgj1P2z9Oe3aMCggEAHBkdZuNXSrwz7ZAQ +Q/D9sUn++fPTHl1KgZcgY2G52nQ28pAjRap7NKZEoP99sLXRt/NEtY3dQE4KsBF/ +uivxS88LDOnLo0zRsm10EpVBA3JdMP3e/gWmWSrVUCmute7nWczuK0b7Aggkyk8r +yMSiony0ZXd8RfUgpjctYevQ1h4FiBRzmTwX2Us0idttfLgv5Fu63w564bWfgW0A +H6pjJUX9wYhWs3NuPWzBOn0V3TFSzq9xD+qxh6uXo8tJWNgYe3wwA0dDitGoXzXL +t79vNIA0Z+xcusK7XlAc4jdK/UutZOgEQxWYsck7tUG84xiB7sEko8euvvD9q2Aa +NttHIQKCAQBDw0ZnVR+GUTFaqWq41JDHr085rmk8hqqJzvrTZ56E+2OPbxRB4wO2 +2Z9c2LEwx+NTEfzR/zHFAWa+IvoFyuDzgwiHVfa2HUyUhYoz4+O8UgiXcdq+MrK3 +sD706FZKhlcRUh2Q+TK2NgwPVL/16qaHZZu2ea3b3nZ2AABeRoQGhBNeRW4+Ov4T +YU13YQPjOtvBpA+FmVHrQIZlCO5CMy6/G2wVKHb1JuMHcOD3CMpConEDD26jvQbI +COFZ7LCFsRq8CJ5LEz3ibLPvKPxQglAOu7bDDtbyLmXIJzvQ3e10I1Ew9SaVvY9e +1beyb7qO16DbsTulECQN/+yNwtkD4Xi7AoIBAAcsoOzuPm75JO2B3v2Ed+7GK1Sc +lvVNU3Cz0NfLPJVysVSHegaSZ83ZoNPhmTBGbrVEr1b3BUY0lkFIOo3mf02l7WHY +xMQ8Hc2anU8qdOXvoXbQ4/YYK1WBvKu/ClBSEcaO90bqyDs73miFW/C1TfxPXnkS +T+rOMmwRQgLFPl5FAbHOk1AXZE4FFGPuUbRtkOErdw4tZhSwfXt/FA6+2LnHy788 +XOFbq0FKncnh2ja+d7eNlHwLKcSC4CFNFKQmlEhZZJHcen/B0mzrHl5vSlYy7P7s +c2R43TlEAMwr2J0L7Xe4+3E61OdCO0F7NF0v3f/4ZoJImLtNzMO1hiaQ4ig= +-----END RSA PRIVATE KEY----- diff --git a/configure b/configure index 78d9ba37..d9bf1513 100755 --- a/configure +++ b/configure @@ -1,2 +1,58 @@ #!/bin/sh # Nothing to do +sudo apt-get update -qq +# install core development packages +sudo apt-get install -qq build-essential +sudo apt-get install -qq cmake +sudo apt-get install -qq lcov +sudo apt-get install -qq clang +sudo apt-get install -qq gcc +sudo apt-get install -qq libconfig-dev +sudo apt-get install -qq liblwip-dev +sudo apt-get install -qq mingw-w64 + +# install git and useful tools packages +sudo apt-get install -qq git +sudo apt-get install -qq gitk +sudo apt-get install -qq gitg + +# install source code format packages +sudo apt-get install -qq tofrodos +sudo apt-get install -qq clang-format +sudo apt-get install -qq sloccount +sudo apt-get install -qq complexity +sudo apt-get install -qq pmccabe + +# install ARM compiler and debugging tools +sudo apt-get install -qq gcc-arm-none-eabi +sudo apt-get install -qq binutils-arm-none-eabi +sudo apt-get install -qq picolibc-arm-none-eabi +sudo apt-get install -qq gdb-multiarch + +# install AVR compiler and debugging tools +sudo apt-get install -qq gcc-avr +sudo apt-get install -qq avarice +sudo apt-get install -qq avr-libc +sudo apt-get install -qq avrdude +sudo apt-get install -qq gdb-avr +sudo apt-get install -qq avrdude-doc + +# install static analysis tools +sudo apt-get install -qq flawfinder +sudo apt-get install -qq cppcheck +sudo apt-get install -qq splint +sudo apt-get install -qq clang-tools +sudo apt-get install -qq clang-tidy +sudo apt-get install -qq codespell + +# install BACnet Secure Connect (BACnet/SC) dependencies +sudo apt-get install -qq libconfig-dev +sudo apt-get install -qq libcap-dev +sudo apt-get install -qq libssl-dev +sudo apt-get install -qq libuv1-dev +git clone --branch v4.3-stable https://github.com/warmcat/libwebsockets.git +bash -c 'cd libwebsockets;mkdir build;cd build;cmake .. -DLWS_WITH_LIBUV=ON -DLWS_WITH_MINIMAL_EXAMPLES=0 -DLWS_MAX_SMP=32;make' +sudo bash -c 'cd libwebsockets;cd build;make install' + +# all complete! +echo "🚀 ALL GOOD TO GO" diff --git a/ports/bsd/bsc-event.c b/ports/bsd/bsc-event.c new file mode 100644 index 00000000..df5bea19 --- /dev/null +++ b/ports/bsd/bsc-event.c @@ -0,0 +1,183 @@ +/** + * @file + * @brief Implementation of mutex abstraction used in BACNet secure connect. + * @author Kirill Neznamov + * @date August 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include "bacnet/basic/sys/debug.h" +#include "bacnet/datalink/bsc/bsc-event.h" + +#define DEBUG_BSC_EVENT 0 + +#if DEBUG_BSC_EVENT == 1 +#define DEBUG_PRINTF printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +struct BSC_Event { + pthread_mutex_t mutex; + pthread_cond_t cond; + bool v; + size_t counter; +}; + +BSC_EVENT *bsc_event_init(void) +{ + BSC_EVENT *ev; + pthread_mutexattr_t attr; + + if (pthread_mutexattr_init(&attr) != 0) { + return NULL; + } + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { + pthread_mutexattr_destroy(&attr); + return NULL; + } + + ev = malloc(sizeof(BSC_EVENT)); + + if (!ev) { + pthread_mutexattr_destroy(&attr); + return NULL; + } + + if (pthread_mutex_init(&ev->mutex, &attr) != 0) { + pthread_mutexattr_destroy(&attr); + free(ev); + return NULL; + } + + pthread_mutexattr_destroy(&attr); + + if (pthread_cond_init(&ev->cond, NULL) != 0) { + pthread_mutex_destroy(&ev->mutex); + free(ev); + return NULL; + } + + ev->v = false; + ev->counter = 0; + return ev; +} + +void bsc_event_deinit(BSC_EVENT *ev) +{ + pthread_mutex_destroy(&ev->mutex); + pthread_cond_destroy(&ev->cond); + free(ev); +} + +void bsc_event_wait(BSC_EVENT *ev) +{ + pthread_mutex_lock(&ev->mutex); + DEBUG_PRINTF("bsc_event_wait() >>> ev = %p\n", ev); + DEBUG_PRINTF("bsc_event_wait() counter before %zu\n", ev->counter); + ev->counter++; + DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter); + while (!ev->v) { + pthread_cond_wait(&ev->cond, &ev->mutex); + } + DEBUG_PRINTF("bsc_event_wait() ev = %p\n", ev); + DEBUG_PRINTF("bsc_event_wait() before counter %zu\n", ev->counter); + ev->counter--; + DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter); + if (!ev->counter) { + ev->v = false; + DEBUG_PRINTF("bsc_event_wait() reset ev\n"); + } + else { + DEBUG_PRINTF("bsc_event_wait() wake up other waiting threads\n"); + pthread_cond_broadcast(&ev->cond); + } + DEBUG_PRINTF("bsc_event_wait() <<< ev = %p\n", ev); + pthread_mutex_unlock(&ev->mutex); +} + +bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout) +{ + struct timespec to; + int r = 0; + + clock_gettime(CLOCK_REALTIME, &to); + to.tv_sec = to.tv_sec + ms_timeout / 1000; + to.tv_nsec = to.tv_nsec + (ms_timeout % 1000) * 1000000; + to.tv_sec += to.tv_nsec / 1000000000; + to.tv_nsec %= 1000000000; + + DEBUG_PRINTF("bsc_event_timedwait() >>> before lock ev = %p ev->v = %d\n", + ev, ev->v); + + pthread_mutex_lock(&ev->mutex); + + DEBUG_PRINTF( + "bsc_event_timedwait() >>> after lock ev = %p ev->v = %d\n", ev, ev->v); + + ev->counter++; + DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter); + + while (!ev->v) { + r = pthread_cond_timedwait(&ev->cond, &ev->mutex, &to); + if (r != 0) { + break; + } + } + + if(ev->v) { + if(r!=0) { + DEBUG_PRINTF("Fired!!! r = %d\n", r); + } + r = 0; + } + + ev->counter--; + DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter); + + if (!ev->counter) { + DEBUG_PRINTF( + "bsc_event_timedwait() event is reset, err = %d\n", r); + ev->v = false; + } + else { + DEBUG_PRINTF("bsc_event_timedwait() wake up other waiting threads\n"); + pthread_cond_broadcast(&ev->cond); + } + + DEBUG_PRINTF( + "bsc_event_timedwait() <<< ret = %d, ev = %p ev->v = %d r = %d\n", + r == 0, ev, ev->v, r); + pthread_mutex_unlock(&ev->mutex); + return r == 0; +} + +void bsc_event_signal(BSC_EVENT *ev) +{ + DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev); + pthread_mutex_lock(&ev->mutex); + DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev); + ev->v = true; + pthread_cond_broadcast(&ev->cond); + DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev); + pthread_mutex_unlock(&ev->mutex); + DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev); +} + +void bsc_wait(int seconds) +{ + sleep(seconds); +} + +void bsc_wait_ms(int mseconds) +{ + usleep(mseconds * 1000); +} diff --git a/ports/bsd/websocket-cli.c b/ports/bsd/websocket-cli.c new file mode 100644 index 00000000..68236453 --- /dev/null +++ b/ports/bsd/websocket-cli.c @@ -0,0 +1,762 @@ +/** + * @file + * @brief Implementation of websocket client interface. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include "bacnet/datalink/bsc/websocket.h" +#include "bacnet/basic/sys/debug.h" +#include "websocket-global.h" + +#define DEBUG_WEBSOCKET_CLIENT 0 + +#if DEBUG_WEBSOCKET_CLIENT == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF(...) +#endif + +#ifndef LWS_PROTOCOL_LIST_TERM +#define LWS_PROTOCOL_LIST_TERM \ + { \ + NULL, NULL, 0, 0, 0, NULL, 0 \ + } +#endif + +#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN + +typedef enum { + BSC_WEBSOCKET_STATE_IDLE = 0, + BSC_WEBSOCKET_STATE_CONNECTING = 1, + BSC_WEBSOCKET_STATE_CONNECTED = 2, + BSC_WEBSOCKET_STATE_DISCONNECTING = 3 +} BSC_WEBSOCKET_STATE; + +typedef struct { + struct lws_context *ctx; + struct lws *ws; + BSC_WEBSOCKET_STATE state; + bool want_send_data; + bool can_send_data; + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + uint8_t *fragment_buffer; + size_t fragment_buffer_size; + size_t fragment_buffer_len; + char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN]; + BACNET_ERROR_CODE err_code; +} BSC_WEBSOCKET_CONNECTION; + +/* Some forward function declarations */ + +static int bws_cli_websocket_event(struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len); + +static const char *bws_hub_protocol = BSC_WEBSOCKET_HUB_PROTOCOL_STR; +static const char *bws_direct_protocol = BSC_WEBSOCKET_DIRECT_PROTOCOL_STR; + +static pthread_mutex_t bws_cli_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + +/* Websockets protocol defined in BACnet/SC \S AB.7.1. */ + +static struct lws_protocols bws_cli_direct_protocol[] = { + { BSC_WEBSOCKET_DIRECT_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL, + 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static struct lws_protocols bws_cli_hub_protocol[] = { + { BSC_WEBSOCKET_HUB_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL, + 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static lws_retry_bo_t bws_retry; + +static BSC_WEBSOCKET_CONNECTION bws_cli_conn[BSC_CLIENT_WEBSOCKETS_MAX_NUM] = { + 0 +}; + +static BSC_WEBSOCKET_HANDLE bws_cli_alloc_connection(void) +{ + int i; + + for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) { + if (bws_cli_conn[i].state == BSC_WEBSOCKET_STATE_IDLE) { + memset(&bws_cli_conn[i], 0, sizeof(bws_cli_conn[i])); + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void bws_cli_free_connection(BSC_WEBSOCKET_HANDLE h) +{ + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_IDLE) { + if (bws_cli_conn[h].fragment_buffer) { + free(bws_cli_conn[h].fragment_buffer); + bws_cli_conn[h].fragment_buffer = NULL; + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = 0; + } + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_IDLE; + bws_cli_conn[h].ws = NULL; + bws_cli_conn[h].ctx = NULL; + } + } +} + +static BSC_WEBSOCKET_HANDLE bws_cli_find_connnection(struct lws *ws) +{ + int i; + for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) { + if (bws_cli_conn[i].ws == ws && + bws_cli_conn[i].state != BSC_WEBSOCKET_STATE_IDLE) { + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void bws_set_err_desc(BSC_WEBSOCKET_HANDLE h, char *err_desc) +{ + size_t len; + if (bws_cli_conn[h].err_code == ERROR_CODE_SUCCESS) { + len = strlen(err_desc) >= sizeof(bws_cli_conn[h].err_desc) + ? sizeof(bws_cli_conn[h].err_desc) - 1 + : strlen(err_desc); + + memcpy(bws_cli_conn[h].err_desc, err_desc, len); + bws_cli_conn[h].err_desc[len] = 0; + + if (strstr(err_desc, "tls:")) { + bws_cli_conn[h].err_code = ERROR_CODE_TLS_ERROR; + } else { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + } + } +} + +static void bws_set_disconnect_reason(BSC_WEBSOCKET_HANDLE h, uint16_t err_code) +{ + bws_cli_conn[h].err_desc[0] = 0; + switch (err_code) { + case LWS_CLOSE_STATUS_NORMAL: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER; + break; + } + case LWS_CLOSE_STATUS_GOINGAWAY: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES; + break; + } + case LWS_CLOSE_STATUS_PROTOCOL_ERR: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR; + break; + } + case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_NO_STATUS: + case LWS_CLOSE_STATUS_RESERVED: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_INVALID_PAYLOAD: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT; + break; + } + case LWS_CLOSE_STATUS_POLICY_VIOLATION: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY; + break; + } + case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG; + break; + } + case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING; + break; + } + case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE; + break; + } + default: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + } +} + +static int bws_cli_websocket_event(struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + BSC_WEBSOCKET_HANDLE h; + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + uint8_t err_code[2]; + + (void)user; + + DEBUG_PRINTF( + "bws_cli_websocket_event() >>> reason = %d, user = %p, in = %p\n", + reason, user, in); + + switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + + DEBUG_PRINTF("bws_cli_websocket_event() connection established\n"); + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED; + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + pthread_mutex_unlock(&bws_cli_mutex); + dispatch_func( + h, BSC_WEBSOCKET_CONNECTED, 0, NULL, NULL, 0, user_param); + break; + } + case LWS_CALLBACK_CLIENT_RECEIVE: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + DEBUG_PRINTF( + "bws_cli_websocket_event() received %d bytes of data\n", len); + if (!lws_frame_is_binary(wsi)) { + /* According AB.7.5.3 BACnet/SC BVLC Message Exchange, + if a received data frame is not binary, + the WebSocket connection shall be closed with a + status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED. + */ + DEBUG_PRINTF("bws_cli_websocket_event() got non-binary frame, " + "close connection for socket %d\n", + h); + lws_close_reason( + wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1\n"); + return -1; + } + if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) { + pthread_mutex_unlock(&bws_cli_mutex); + } else { + if (!bws_cli_conn[h].fragment_buffer) { + DEBUG_PRINTF("bws_cli_websocket_event() alloc %d bytes for " + "socket %d\n", + len, h); + bws_cli_conn[h].fragment_buffer = malloc(BSC_RX_BUFFER_LEN); + if (!bws_cli_conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1, " + "allocation of %d bytes failed\n", + len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN : len); + return -1; + } + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN; + } + if (bws_cli_conn[h].fragment_buffer_len + len > + bws_cli_conn[h].fragment_buffer_size) { + DEBUG_PRINTF( + "bws_cli_websocket_event() realloc buf of %d bytes" + "for socket %d to %d bytes\n", + bws_cli_conn[h].fragment_buffer_len, h, + bws_cli_conn[h].fragment_buffer_len + len); + bws_cli_conn[h].fragment_buffer = + realloc(bws_cli_conn[h].fragment_buffer, + bws_cli_conn[h].fragment_buffer_len + len); + if (!bws_cli_conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1, " + "re-allocation of %d bytes failed\n", + bws_cli_conn[h].fragment_buffer_len + len); + return -1; + } + bws_cli_conn[h].fragment_buffer_size = + bws_cli_conn[h].fragment_buffer_len + len; + } + DEBUG_PRINTF("bws_cli_websocket_event() got next %d bytes for " + "socket %d\n", + len, h); + memcpy(&bws_cli_conn[h].fragment_buffer[ + bws_cli_conn[h].fragment_buffer_len], + in, len); + bws_cli_conn[h].fragment_buffer_len += len; + + if (lws_is_final_fragment(wsi)) { + DEBUG_PRINTF( + "bws_cli_websocket_event() last fragment received\n"); + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + pthread_mutex_unlock(&bws_cli_mutex); + dispatch_func(h, BSC_WEBSOCKET_RECEIVED, 0, NULL, + bws_cli_conn[h].fragment_buffer, + bws_cli_conn[h].fragment_buffer_len, user_param); + pthread_mutex_lock(&bws_cli_mutex); + bws_cli_conn[h].fragment_buffer_len = 0; + pthread_mutex_unlock(&bws_cli_mutex); + } else { + pthread_mutex_unlock(&bws_cli_mutex); + } + } + break; + } + case LWS_CALLBACK_CLIENT_WRITEABLE: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + + DEBUG_PRINTF("bws_cli_websocket_event() can write, state = %d\n", + bws_cli_conn[h].state); + DEBUG_PRINTF("bws_cli_websocket_event() ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, bws_cli_conn[h].can_send_data); + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED && + bws_cli_conn[h].want_send_data) { + bws_cli_conn[h].can_send_data = true; + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + pthread_mutex_unlock(&bws_cli_mutex); + dispatch_func( + h, BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, user_param); + pthread_mutex_lock(&bws_cli_mutex); + bws_cli_conn[h].want_send_data = false; + bws_cli_conn[h].can_send_data = false; + DEBUG_PRINTF( + "bws_cli_websocket_event() was send , ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + pthread_mutex_unlock(&bws_cli_mutex); + /* wakeup worker to process internal state */ + lws_cancel_service(bws_cli_conn[h].ctx); + } else { + bws_cli_conn[h].want_send_data = false; + DEBUG_PRINTF( + "bws_cli_websocket_event() no send, ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + pthread_mutex_unlock(&bws_cli_mutex); + } + break; + } + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) { + err_code[0] = ((uint8_t *)in)[1]; + err_code[1] = ((uint8_t *)in)[0]; + bws_set_disconnect_reason(h, *((uint16_t *)&err_code)); + } + pthread_mutex_unlock(&bws_cli_mutex); + break; + } + case LWS_CALLBACK_CLIENT_CLOSED: + case LWS_CALLBACK_CLOSED: + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE) { + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR && in) { + bws_set_err_desc(h, (char *)in); + } + pthread_mutex_unlock(&bws_cli_mutex); + /* wakeup worker to process pending event */ + lws_cancel_service(bws_cli_conn[h].ctx); + } else { + pthread_mutex_unlock(&bws_cli_mutex); + } + break; + } + default: { + break; + } + } + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; +} + +static void *bws_cli_worker(void *arg) +{ + BSC_WEBSOCKET_CONNECTION *conn = (BSC_WEBSOCKET_CONNECTION *)arg; + BSC_WEBSOCKET_HANDLE h = conn - &bws_cli_conn[0]; + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN]; + uint16_t err_code; + + while (1) { + DEBUG_PRINTF("bws_cli_worker() try mutex h = %d\n", h); + pthread_mutex_lock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() mutex locked h = %d\n", h); + if (conn->state == BSC_WEBSOCKET_STATE_CONNECTED) { + if (conn->want_send_data) { + DEBUG_PRINTF( + "bws_cli_worker() process request for sending data\n"); + lws_callback_on_writable(conn->ws); + } + } else if (conn->state == BSC_WEBSOCKET_STATE_DISCONNECTING) { + DEBUG_PRINTF("bws_cli_worker() process disconnecting event\n"); + DEBUG_PRINTF("bws_cli_worker() destroy ctx %p\n", conn->ctx); + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result lws_context_destroy() + is not thread safe. More over, on different platforms the + function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + pthread_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + lws_context_destroy(conn->ctx); + bsc_websocket_global_unlock(); + pthread_mutex_lock(&bws_cli_mutex); + dispatch_func = conn->dispatch_func; + user_param = conn->user_param; + err_code = conn->err_code; + if (err_code != ERROR_CODE_SUCCESS) { + memcpy(err_desc, conn->err_desc, sizeof(err_desc)); + } + bws_cli_free_connection(h); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() unlock mutex\n"); + dispatch_func(h, BSC_WEBSOCKET_DISCONNECTED, err_code, + err_code != ERROR_CODE_SUCCESS ? err_desc : NULL, NULL, 0, + user_param); + return NULL; + } + DEBUG_PRINTF("bws_cli_worker() unlock mutex\n"); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() going to block on lws_service() call\n"); + lws_service(conn->ctx, 0); + } + + return NULL; +} + +BSC_WEBSOCKET_RET bws_cli_connect(BSC_WEBSOCKET_PROTOCOL proto, + char *url, + uint8_t *ca_cert, + size_t ca_cert_size, + uint8_t *cert, + size_t cert_size, + uint8_t *key, + size_t key_size, + size_t timeout_s, + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func, + void *dispatch_func_user_param, + BSC_WEBSOCKET_HANDLE *out_handle) +{ + struct lws_context_creation_info info; + char tmp_url[BSC_WSURL_MAX_LEN]; + const char *prot = NULL, *addr = NULL, *path = NULL; + int port = -1; + BSC_WEBSOCKET_HANDLE h; + struct lws_client_connect_info cinfo; + pthread_t thread_id; + size_t len; + pthread_attr_t attr; + int r; + DEBUG_PRINTF("bws_cli_connect() >>> proto = %d, url = %s\n", proto, url); + + if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size || + !url || !out_handle || !timeout_s || !dispatch_func) { + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + *out_handle = BSC_WEBSOCKET_INVALID_HANDLE; + + if (proto != BSC_WEBSOCKET_HUB_PROTOCOL && + proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) { + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + memset(&info, 0, sizeof(info)); + memset(&cinfo, 0, sizeof(cinfo)); + + len = strlen(url) >= sizeof(tmp_url) ? sizeof(tmp_url) - 1 : strlen(url); + memcpy(tmp_url, url, len); + tmp_url[len] = 0; + + bsc_websocket_init_log(); + pthread_mutex_lock(&bws_cli_mutex); + + if (lws_parse_uri(tmp_url, &prot, &addr, &port, &path) != 0 || + port == -1 || !prot || !addr || !path) { + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (strcmp(prot, "wss") != 0) { + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + h = bws_cli_alloc_connection(); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTING; + bws_cli_conn[h].fragment_buffer = NULL; + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = 0; + bws_cli_conn[h].dispatch_func = dispatch_func; + bws_cli_conn[h].user_param = dispatch_func_user_param; + info.port = CONTEXT_PORT_NO_LISTEN; + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + info.protocols = bws_cli_hub_protocol; + } else { + info.protocols = bws_cli_direct_protocol; + } + info.gid = -1; + info.uid = -1; + info.client_ssl_cert_mem = cert; + info.client_ssl_cert_mem_len = cert_size; + info.client_ssl_ca_mem = ca_cert; + info.client_ssl_ca_mem_len = ca_cert_size; + info.client_ssl_key_mem = key; + info.client_ssl_key_mem_len = key_size; + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND; + info.timeout_secs = timeout_s; + info.connect_timeout_secs = timeout_s; + + /* TRICKY: check comments related to lws_context_destroy() call */ + + pthread_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + bws_cli_conn[h].ctx = lws_create_context(&info); + bsc_websocket_global_unlock(); + pthread_mutex_lock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() created ctx %p\n", bws_cli_conn[h].ctx); + + if (!bws_cli_conn[h].ctx) { + bws_cli_free_connection(h); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + bws_cli_conn[h].ws = NULL; + cinfo.context = bws_cli_conn[h].ctx; + cinfo.address = addr; + cinfo.origin = cinfo.address; + cinfo.host = cinfo.address; + cinfo.port = port; + cinfo.path = path; + cinfo.pwsi = &bws_cli_conn[h].ws; + cinfo.alpn = "h2;http/1.1"; + bws_retry.secs_since_valid_ping = 3; + bws_retry.secs_since_valid_hangup = 10; + cinfo.retry_and_idle_policy = &bws_retry; + cinfo.ssl_connection = LCCSCF_USE_SSL | + LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED; + + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + cinfo.protocol = bws_hub_protocol; + } else { + cinfo.protocol = bws_direct_protocol; + } + + bws_cli_conn[h].err_code = ERROR_CODE_SUCCESS; + *out_handle = h; + pthread_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + lws_client_connect_via_info(&cinfo); + bsc_websocket_global_unlock(); + r = pthread_attr_init(&attr); + + if(!r) { + r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + } + + if(!r) { + r = pthread_create(&thread_id, &attr, &bws_cli_worker, &bws_cli_conn[h]); + } + + if(r) { + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result lws_context_destroy() + is not thread safe. More over, on different platforms the + function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + bsc_websocket_global_lock(); + lws_context_destroy(bws_cli_conn[h].ctx); + bsc_websocket_global_unlock(); + pthread_mutex_lock(&bws_cli_mutex); + bws_cli_free_connection(h); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + pthread_attr_destroy(&attr); + DEBUG_PRINTF("bws_cli_connect() <<< ret = %d\n", BSC_WEBSOCKET_SUCCESS); + return BSC_WEBSOCKET_SUCCESS; +} + +void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_cli_disconnect() >>> h = %d\n", h); + + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + pthread_mutex_lock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_disconnect() state = %d\n", bws_cli_conn[h].state); + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTING || + bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process change of connection state */ + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(bws_cli_conn[h].ctx); + } + pthread_mutex_unlock(&bws_cli_mutex); + } + + DEBUG_PRINTF("bws_cli_disconnect() <<<\n"); +} + +void bws_cli_send(BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_cli_send() >>> h = %d\n", h); + + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + pthread_mutex_lock(&bws_cli_mutex); + + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process send request */ + bws_cli_conn[h].want_send_data = true; + DEBUG_PRINTF("bws_cli_send() cs = 1\n"); + lws_cancel_service(bws_cli_conn[h].ctx); + } + + pthread_mutex_unlock(&bws_cli_mutex); + } + + DEBUG_PRINTF("bws_cli_send() <<<\n"); +} + +BSC_WEBSOCKET_RET bws_cli_dispatch_send( + BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size) +{ + int written; + BSC_WEBSOCKET_RET ret; + + DEBUG_PRINTF( + "bws_cli_dispatch_send() >>> h = %d, payload = %p, payload_size = %d\n", + h, payload, payload_size); + + if (h < 0 || h >= BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + DEBUG_PRINTF( + "bws_cli_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + pthread_mutex_lock(&bws_cli_mutex); + + if ((bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) || + !bws_cli_conn[h].want_send_data || !bws_cli_conn[h].can_send_data) { + DEBUG_PRINTF("bws_cli_dispatch_send() state = %d, ws = %d, cs = %d\n", + bws_cli_conn[h].state, bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = " + "BSC_WEBSOCKET_INVALID_OPERATION\n"); + pthread_mutex_unlock(&bws_cli_mutex); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + written = + lws_write(bws_cli_conn[h].ws, payload, payload_size, LWS_WRITE_BINARY); + + DEBUG_PRINTF("bws_cli_dispatch_send() %d bytes is sent\n", written); + + if (written < (int)payload_size) { + DEBUG_PRINTF( + "bws_cli_dispatch_send() websocket connection is broken(closed)\n"); + /* tell worker to process change of connection state */ + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(bws_cli_conn[h].ctx); + ret = BSC_WEBSOCKET_INVALID_OPERATION; + } else { + ret = BSC_WEBSOCKET_SUCCESS; + } + + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = %d\n", ret); + return ret; +} diff --git a/ports/bsd/websocket-global.c b/ports/bsd/websocket-global.c new file mode 100644 index 00000000..d6dc75fe --- /dev/null +++ b/ports/bsd/websocket-global.c @@ -0,0 +1,105 @@ +/** + * @file + * @brief Implementation of global websocket functions. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include "websocket-global.h" +#include "bacnet/basic/sys/debug.h" + +static pthread_mutex_t websocket_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; +static pthread_mutex_t websocket_dispatch_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + +#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1) + +void bsc_websocket_global_lock(void) +{ + pthread_mutex_lock(&websocket_mutex); +} + +void bsc_websocket_global_unlock(void) +{ + pthread_mutex_unlock(&websocket_mutex); +} + +void bws_dispatch_lock(void) +{ + pthread_mutex_lock(&websocket_dispatch_mutex); +} + +void bws_dispatch_unlock(void) +{ + pthread_mutex_unlock(&websocket_dispatch_mutex); +} + +#else +static int volatile websocket_mutex_cnt = 0; +static int volatile websocket_dispatch_mutex_cnt = 0; + +void bsc_websocket_global_lock_dbg(char *f, int line) +{ + printf("bsc_websocket_global_lock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, websocket_mutex_cnt, pthread_self()); + websocket_mutex_cnt++; + fflush(stdout); + pthread_mutex_lock(&websocket_mutex); + printf("bsc_websocket_global_lock_dbg() <<< lock_cnt %d tid = %ld\n", websocket_mutex_cnt, pthread_self()); + fflush(stdout); +} + +void bsc_websocket_global_unlock_dbg(char *f, int line) +{ + printf("bsc_websocket_global_unlock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, websocket_mutex_cnt, pthread_self()); + websocket_mutex_cnt--; + fflush(stdout); + pthread_mutex_unlock(&websocket_mutex); + printf("bsc_websocket_global_unlock_dbg() <<< lock_cnt %d tid = %ld\n", websocket_mutex_cnt, pthread_self()); + fflush(stdout); +} + +void bws_dispatch_lock_dbg(char *f, int line) +{ + printf("bws_dispatch_lock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, websocket_dispatch_mutex_cnt, pthread_self()); + websocket_dispatch_mutex_cnt++; + fflush(stdout); + pthread_mutex_lock(&websocket_dispatch_mutex); + printf("bws_dispatch_lock_dbg() <<< lock_cnt %d tid = %ld\n", websocket_dispatch_mutex_cnt, pthread_self()); + fflush(stdout); +} + +void bws_dispatch_unlock_dbg(char *f, int line) +{ + printf("bws_dispatch_unlock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, websocket_dispatch_mutex_cnt, pthread_self()); + websocket_dispatch_mutex_cnt--; + fflush(stdout); + pthread_mutex_unlock(&websocket_dispatch_mutex); + printf("bws_dispatch_unlock_dbg() <<< lock_cnt %d tid = %ld\n", websocket_dispatch_mutex_cnt, pthread_self()); + fflush(stdout); +} + +#endif + +static bool bsc_websocket_log_initialized = false; + +void bsc_websocket_init_log(void) +{ + bsc_websocket_global_lock(); + if(!bsc_websocket_log_initialized) { + bsc_websocket_log_initialized = true; +#if DEBUG_LIBWEBSOCKETS_ENABLED == 1 + printf("LWS_MAX_SMP = %d", LWS_MAX_SMP); + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | + LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY | + LLL_USER | LLL_THREAD, + NULL); +#else + lws_set_log_level(0, NULL); +#endif + } + bsc_websocket_global_unlock(); +} diff --git a/ports/bsd/websocket-global.h b/ports/bsd/websocket-global.h new file mode 100644 index 00000000..7f977077 --- /dev/null +++ b/ports/bsd/websocket-global.h @@ -0,0 +1,27 @@ +/** + * @file + * @brief Global websocket functions + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef __BSC_WEBSOCKET_MUTEX_INCLUDED__ +#define __BSC_WEBSOCKET_MUTEX_INCLUDED__ + +#ifndef BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED +#define BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED 0 +#endif + +#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1) +void bsc_websocket_global_lock(void); +void bsc_websocket_global_unlock(void); +#else +void bsc_websocket_global_lock_dbg(char *f, int line); +void bsc_websocket_global_unlock_dbg(char *f, int line); +#define bsc_websocket_global_lock() bsc_websocket_global_lock_dbg(__FILE__, __LINE__); +#define bsc_websocket_global_unlock() bsc_websocket_global_unlock_dbg(__FILE__, __LINE__); +#endif + +void bsc_websocket_init_log(void); + +#endif diff --git a/ports/bsd/websocket-srv.c b/ports/bsd/websocket-srv.c new file mode 100644 index 00000000..30300cc2 --- /dev/null +++ b/ports/bsd/websocket-srv.c @@ -0,0 +1,999 @@ +/** + * @file + * @brief Implementation of server websocket interface. + * @author Kirill Neznamov + * @date June 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include "bacnet/datalink/bsc/websocket.h" +#include "bacnet/basic/sys/debug.h" +#include "websocket-global.h" +#include + +#define DEBUG_WEBSOCKET_SERVER 0 + +#if DEBUG_WEBSOCKET_SERVER == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF(...) +#endif + +#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN + +#ifndef LWS_PROTOCOL_LIST_TERM +#define LWS_PROTOCOL_LIST_TERM \ + { \ + NULL, NULL, 0, 0, 0, NULL, 0 \ + } +#endif + +typedef enum { + BSC_WEBSOCKET_STATE_IDLE = 0, + BSC_WEBSOCKET_STATE_CONNECTED = 1, + BSC_WEBSOCKET_STATE_DISCONNECTING = 2 +} BSC_WEBSOCKET_STATE; + +/* Some forward function declarations */ + +static int bws_srv_websocket_event(struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len); + +typedef struct { + struct lws_context *ctx; + struct lws *ws; + BSC_WEBSOCKET_STATE state; + bool want_send_data; + bool can_send_data; + uint8_t *fragment_buffer; + size_t fragment_buffer_size; + size_t fragment_buffer_len; + BACNET_ERROR_CODE err_code; +} BSC_WEBSOCKET_CONNECTION; + +#if BSC_CONF_WEBSOCKET_SERVERS_NUM < 1 +#error "BSC_CONF_WEBSOCKET_SERVERS_NUM must be >= 1" +#endif + +static pthread_mutex_t bws_global_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; +static pthread_mutex_t bws_srv_direct_mutex[BSC_CONF_WEBSOCKET_SERVERS_NUM]; +static pthread_mutex_t bws_srv_hub_mutex[BSC_CONF_WEBSOCKET_SERVERS_NUM]; + +#if BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM > 0 +static BSC_WEBSOCKET_CONNECTION + bws_hub_conn[BSC_CONF_WEBSOCKET_SERVERS_NUM] + [BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM] = { 0 }; +#else +static BSC_WEBSOCKET_CONNECTION bws_hub_conn[1][1] = { 0 }; +#endif + +#if BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM > 0 +static BSC_WEBSOCKET_CONNECTION + bws_direct_conn[BSC_CONF_WEBSOCKET_SERVERS_NUM] + [BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM] = { 0 }; +#else +static BSC_WEBSOCKET_CONNECTION bws_direct_conn[1][1] = { 0 }; +#endif + +typedef struct BACNetWebsocketServerContext { + bool used; + struct lws_context *wsctx; + BSC_WEBSOCKET_PROTOCOL proto; + BSC_WEBSOCKET_CONNECTION *conn; + pthread_mutex_t *mutex; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + bool stop_worker; +} BSC_WEBSOCKET_CONTEXT; + +static BSC_WEBSOCKET_CONTEXT bws_hub_ctx[BSC_CONF_WEBSOCKET_SERVERS_NUM] = { + 0 +}; +static BSC_WEBSOCKET_CONTEXT bws_direct_ctx[BSC_CONF_WEBSOCKET_SERVERS_NUM] = { + 0 +}; + +static bool bws_mutex_init(pthread_mutex_t *mutex) +{ + pthread_mutexattr_t attr; + + if (pthread_mutexattr_init(&attr) != 0) { + return false; + } + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { + pthread_mutexattr_destroy(&attr); + return false; + } + + if (pthread_mutex_init(mutex, &attr) != 0) { + pthread_mutexattr_destroy(&attr); + return false; + } + + pthread_mutexattr_destroy(&attr); + return true; +} + +static BSC_WEBSOCKET_CONTEXT *bws_alloc_server_ctx(BSC_WEBSOCKET_PROTOCOL proto) +{ + int i; + BSC_WEBSOCKET_CONTEXT *ctx = (proto == BSC_WEBSOCKET_HUB_PROTOCOL) + ? &bws_hub_ctx[0] + : &bws_direct_ctx[0]; + + pthread_mutex_lock(&bws_global_mutex); + DEBUG_PRINTF("bws_alloc_server_ctx() >>> proto = %d\n", proto); + + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (!ctx[i].used) { + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + ctx[i].mutex = &bws_srv_hub_mutex[i]; + ctx[i].conn = &bws_hub_conn[i][0]; + } else { + ctx[i].mutex = &bws_srv_direct_mutex[i]; + ctx[i].conn = &bws_direct_conn[i][0]; + } + if (!bws_mutex_init(ctx[i].mutex)) { + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + pthread_mutex_unlock(&bws_global_mutex); + return NULL; + } + ctx[i].used = true; + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + pthread_mutex_unlock(&bws_global_mutex); + return &ctx[i]; + } + } + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + pthread_mutex_unlock(&bws_global_mutex); + return NULL; +} + +static void bws_set_disconnect_reason( + BSC_WEBSOCKET_CONTEXT *ctx, BSC_WEBSOCKET_HANDLE h, uint16_t err_code) +{ + switch (err_code) { + case LWS_CLOSE_STATUS_NORMAL: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER; + break; + } + case LWS_CLOSE_STATUS_GOINGAWAY: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES; + break; + } + case LWS_CLOSE_STATUS_PROTOCOL_ERR: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR; + break; + } + case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_NO_STATUS: + case LWS_CLOSE_STATUS_RESERVED: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_INVALID_PAYLOAD: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT; + break; + } + case LWS_CLOSE_STATUS_POLICY_VIOLATION: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY; + break; + } + case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG; + break; + } + case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING; + break; + } + case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE; + break; + } + default: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + } +} + +static void bws_free_server_ctx(BSC_WEBSOCKET_CONTEXT *ctx) +{ + pthread_mutex_lock(&bws_global_mutex); + DEBUG_PRINTF("bws_free_server_ctx() >>> ctx = %p\n", ctx); + ctx->used = false; + ctx->wsctx = NULL; + ctx->conn = NULL; + pthread_mutex_destroy(ctx->mutex); + ctx->mutex = NULL; + ctx->dispatch_func = NULL; + ctx->user_param = NULL; + DEBUG_PRINTF("bws_free_server_ctx() <<< \n"); + pthread_mutex_unlock(&bws_global_mutex); +} + +#if DEBUG_ENABLED == 1 +static bool bws_validate_ctx_pointer(BSC_WEBSOCKET_CONTEXT *ctx) +{ + bool is_validated = false; + int i; + + pthread_mutex_lock(&bws_global_mutex); + + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (ctx == &bws_hub_ctx[i]) { + is_validated = true; + break; + } + } + + if (!is_validated) { + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (ctx == &bws_direct_ctx[i]) { + is_validated = true; + break; + } + } + } + + pthread_mutex_unlock(&bws_global_mutex); + return is_validated; +} +#endif + +static int bws_srv_get_max_sockets(BSC_WEBSOCKET_PROTOCOL proto) +{ + int max = 0; + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + max = BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM; + } else if (proto == BSC_WEBSOCKET_DIRECT_PROTOCOL) { + max = BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM; + } + return max; +} + +static BSC_WEBSOCKET_HANDLE bws_srv_alloc_connection(BSC_WEBSOCKET_CONTEXT *ctx) +{ + int i; + + DEBUG_PRINTF("bws_srv_alloc_connection() >>> ctx = %p\n", ctx); + + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + if (ctx->conn[i].state == BSC_WEBSOCKET_STATE_IDLE) { + memset(&ctx->conn[i], 0, sizeof(ctx->conn[i])); + DEBUG_PRINTF("bws_srv_alloc_connection() <<< ret = %d\n", i); + return i; + } + } + + DEBUG_PRINTF("bws_srv_alloc_connection() <<< ret = " + "BSC_WEBSOCKET_INVALID_HANDLE\n"); + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void bws_srv_free_connection( + BSC_WEBSOCKET_CONTEXT *ctx, BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_srv_free_connection() >>> ctx = %p, h = %d\n", ctx, h); + + if (h >= 0 && h < bws_srv_get_max_sockets(ctx->proto)) { + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE) { + if (ctx->conn[h].fragment_buffer) { + free(ctx->conn[h].fragment_buffer); + ctx->conn[h].fragment_buffer = NULL; + ctx->conn[h].fragment_buffer_len = 0; + ctx->conn[h].fragment_buffer_size = 0; + } + ctx->conn[h].state = BSC_WEBSOCKET_STATE_IDLE; + ctx->conn[h].ws = NULL; + } + } + DEBUG_PRINTF("bws_srv_free_connection() <<<\n"); +} + +static BSC_WEBSOCKET_HANDLE bws_find_connnection( + BSC_WEBSOCKET_CONTEXT *ctx, struct lws *ws) +{ + int i; + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + if (ctx->conn[i].ws == ws && + ctx->conn[i].state != BSC_WEBSOCKET_STATE_IDLE) { + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static int bws_srv_websocket_event(struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + BSC_WEBSOCKET_HANDLE h; + BSC_WEBSOCKET_CONTEXT *ctx = + (BSC_WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi)); + int ret = 0; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + bool stop_worker; + uint8_t err_code[2]; + uint16_t err; + (void)user; + + DEBUG_PRINTF("bws_srv_websocket_event() >>> ctx = %p, user_param = %p, " + "proto = %d, wsi = %p, " + "reason = %d, in = %p, len = %d\n", + ctx, ctx->user_param, ctx->proto, wsi, reason, in, len); + + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: { + pthread_mutex_lock(ctx->mutex); + DEBUG_PRINTF("bws_srv_websocket_event() established connection\n"); + h = bws_srv_alloc_connection(ctx); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF("bws_srv_websocket_event() no free sockets, " + "dropping incoming connection\n"); + pthread_mutex_unlock(ctx->mutex); + return -1; + } + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d set state of" + " socket %d to BACNET_WEBSOCKET_STATE_CONNECTING\n", + ctx, ctx->proto, h); + ctx->conn[h].ws = wsi; + ctx->conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED; + ctx->conn[h].err_code = ERROR_CODE_SUCCESS; + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + pthread_mutex_unlock(ctx->mutex); + dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_CONNECTED, 0, NULL, NULL, 0, user_param); + /* wakeup worker to process pending event */ + lws_cancel_service(ctx->wsctx); + break; + } + case LWS_CALLBACK_CLOSED: { + DEBUG_PRINTF("bws_srv_websocket_event() closed connection\n"); + pthread_mutex_lock(ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + pthread_mutex_unlock(ctx->mutex); + } else { + DEBUG_PRINTF("bws_srv_websocket_event() ctx %p user_param = %p " + "proto %d state of " + "socket %d is %d\n", + ctx, ctx->user_param, ctx->proto, h, ctx->conn[h].state); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + stop_worker = ctx->stop_worker; + err = ctx->conn[h].err_code; + bws_srv_free_connection(ctx, h); + pthread_mutex_unlock(ctx->mutex); + if (!stop_worker) { + dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_DISCONNECTED, err, NULL, NULL, 0, + user_param); + } + } + break; + } + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + pthread_mutex_lock(ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) { + err_code[0] = ((uint8_t *)in)[1]; + err_code[1] = ((uint8_t *)in)[0]; + bws_set_disconnect_reason(ctx, h, *((uint16_t *)&err_code)); + } + pthread_mutex_unlock(ctx->mutex); + break; + } + case LWS_CALLBACK_RECEIVE: { + pthread_mutex_lock(ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + pthread_mutex_unlock(ctx->mutex); + } else { + DEBUG_PRINTF("bws_srv_websocket_event() ctx %p proto %d " + "received %d bytes of " + "data for websocket %d\n", + ctx, ctx->proto, len, h); + if (!lws_frame_is_binary(wsi)) { + /* According AB.7.5.3 BACnet/SC BVLC Message Exchange, + if a received data frame is not binary, + the WebSocket connection shall be closed with a + status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED. + */ + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d got " + "non-binary frame, " + "close websocket %d\n", + ctx, ctx->proto, h); + lws_close_reason( + wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0); + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = -1\n"); + return -1; + } + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) { + pthread_mutex_unlock(ctx->mutex); + } else { + if (!ctx->conn[h].fragment_buffer) { + ctx->conn[h].fragment_buffer = + malloc(BSC_RX_BUFFER_LEN); + if (!ctx->conn[h].fragment_buffer) { + lws_close_reason(wsi, + LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0); + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = " + "-1, allocation of %d bytes failed\n", + len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN + : len); + return -1; + } + ctx->conn[h].fragment_buffer_len = 0; + ctx->conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN; + } + if (ctx->conn[h].fragment_buffer_len + len > + ctx->conn[h].fragment_buffer_size) { + DEBUG_PRINTF( + "bws_srv_websocket_event() realloc buf of %d bytes" + "for socket %d to %d bytes\n", + ctx->conn[h].fragment_buffer_len, h, + ctx->conn[h].fragment_buffer_len + len); + + ctx->conn[h].fragment_buffer = + realloc(ctx->conn[h].fragment_buffer, + ctx->conn[h].fragment_buffer_len + len); + if (!ctx->conn[h].fragment_buffer) { + lws_close_reason(wsi, + LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0); + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF( + "bws_srv_websocket_event() <<< ret = -1, " + "re-allocation of %d bytes failed\n", + ctx->conn[h].fragment_buffer_len + len); + return -1; + } + ctx->conn[h].fragment_buffer_size = + ctx->conn[h].fragment_buffer_len + len; + } + DEBUG_PRINTF( + "bws_srv_websocket_event() got next %d bytes for " + "socket %d total_len %d\n", + len, h, ctx->conn[h].fragment_buffer_len); + memcpy(&ctx->conn[h].fragment_buffer[ + ctx->conn[h].fragment_buffer_len], + in, len); + ctx->conn[h].fragment_buffer_len += len; + if (lws_is_final_fragment(wsi) && !ctx->stop_worker) { + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + pthread_mutex_unlock(ctx->mutex); + dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_RECEIVED, 0, NULL, + ctx->conn[h].fragment_buffer, + ctx->conn[h].fragment_buffer_len, user_param); + pthread_mutex_lock(ctx->mutex); + ctx->conn[h].fragment_buffer_len = 0; + pthread_mutex_unlock(ctx->mutex); + } else { + pthread_mutex_unlock(ctx->mutex); + } + } + } + break; + } + case LWS_CALLBACK_SERVER_WRITEABLE: { + pthread_mutex_lock(ctx->mutex); + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d can write\n", ctx, + ctx->proto); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + pthread_mutex_unlock(ctx->mutex); + } else { + DEBUG_PRINTF("bws_srv_websocket_event() ctx %p proto %d socket " + "%d state = %d\n", + ctx, ctx->proto, h, ctx->conn[h].state); + + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_DISCONNECTING) { + /* if -1 returned from this callback libwebsocket closes ++ websocket related to wsi */ + ret = -1; + } + + if ((ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE) && + !ctx->stop_worker && ctx->conn[h].want_send_data) { + ctx->conn[h].can_send_data = true; + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + stop_worker = ctx->stop_worker; + pthread_mutex_unlock(ctx->mutex); + if (!stop_worker) { + dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, + user_param); + } + pthread_mutex_lock(ctx->mutex); + ctx->conn[h].want_send_data = false; + ctx->conn[h].can_send_data = false; + pthread_mutex_unlock(ctx->mutex); + /* wakeup worker to process internal state */ + lws_cancel_service(ctx->wsctx); + } else { + ctx->conn[h].want_send_data = false; + pthread_mutex_unlock(ctx->mutex); + } + } + break; + } + default: { + break; + } + } + DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = %d\n", ret); + return ret; +} + +static void *bws_srv_worker(void *arg) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)arg; + int i; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + + DEBUG_PRINTF("bws_srv_worker() started for ctx %p proto %d user_param %p\n", + ctx, ctx->proto, ctx->user_param); + + pthread_mutex_lock(ctx->mutex); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + pthread_mutex_unlock(ctx->mutex); + + dispatch_func((BSC_WEBSOCKET_SRV_HANDLE)ctx, 0, + BSC_WEBSOCKET_SERVER_STARTED, 0, NULL, NULL, 0, user_param); + + while (1) { + DEBUG_PRINTF("bws_srv_worker() ctx %p proto %d blocked user_param %p\n", + ctx, ctx->proto, ctx->user_param); + pthread_mutex_lock(ctx->mutex); + + if (ctx->stop_worker) { + DEBUG_PRINTF("bws_srv_worker() ctx %p user_param %p proto %d going " + "to stop\n", + ctx, ctx->user_param, ctx->proto); + DEBUG_PRINTF("bws_srv_worker() destroy wsctx %p, ctx = %p, " + "user_param = %p\n", + ctx->wsctx, ctx, ctx->user_param); + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result lws_context_destroy() + is not thread safe. More over, on different platforms the + function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_srv_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + pthread_mutex_unlock(ctx->mutex); + bsc_websocket_global_lock(); + lws_context_destroy(ctx->wsctx); + bsc_websocket_global_unlock(); + pthread_mutex_lock(ctx->mutex); + ctx->wsctx = NULL; + DEBUG_PRINTF("bws_srv_worker() set wsctx %p\n", ctx->wsctx); + ctx->stop_worker = false; + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d emitting stop event\n", ctx, + ctx->proto); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param = %p\n", ctx, user_param); + pthread_mutex_unlock(ctx->mutex); + dispatch_func(ctx, 0, BSC_WEBSOCKET_SERVER_STOPPED, 0, NULL, NULL, + 0, user_param); + bws_free_server_ctx(ctx); + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d stopped\n", ctx, ctx->proto); + return NULL; + } + + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + DEBUG_PRINTF("bws_srv_worker() ctx %p user_param %p proto %d " + "socket %d(%p) state = %d\n", + ctx, ctx->user_param, ctx->proto, i, &ctx->conn[i], + ctx->conn[i].state); + if (ctx->conn[i].state == BSC_WEBSOCKET_STATE_CONNECTED) { + if (ctx->conn[i].want_send_data) { + DEBUG_PRINTF("bws_srv_worker() process request for sending " + "data on socket %d\n", + i); + lws_callback_on_writable(ctx->conn[i].ws); + } + } else if (ctx->conn[i].state == + BSC_WEBSOCKET_STATE_DISCONNECTING) { + DEBUG_PRINTF("bws_srv_worker() process disconnecting event on " + "socket %d\n", + i); + lws_callback_on_writable(ctx->conn[i].ws); + } + } + + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d unblocked\n", ctx, ctx->proto); + pthread_mutex_unlock(ctx->mutex); + + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param %p proto %d going to block on " + "lws_service() call\n", + ctx, ctx->user_param, ctx->proto); + lws_service(ctx->wsctx, 0); + } + + return NULL; +} + +BSC_WEBSOCKET_RET bws_srv_start(BSC_WEBSOCKET_PROTOCOL proto, + int port, + char *iface, + uint8_t *ca_cert, + size_t ca_cert_size, + uint8_t *cert, + size_t cert_size, + uint8_t *key, + size_t key_size, + size_t timeout_s, + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func, + void *dispatch_func_user_param, + BSC_WEBSOCKET_SRV_HANDLE *sh) +{ + pthread_t thread_id; + struct lws_context_creation_info info = { 0 }; + BSC_WEBSOCKET_CONTEXT *ctx; + pthread_attr_t attr; + int r; + struct lws_protocols protos[] = { + { (proto == BSC_WEBSOCKET_HUB_PROTOCOL) + ? BSC_WEBSOCKET_HUB_PROTOCOL_STR + : BSC_WEBSOCKET_DIRECT_PROTOCOL_STR, + bws_srv_websocket_event, 0, 0, 0, NULL, 0 }, + LWS_PROTOCOL_LIST_TERM + }; + + DEBUG_PRINTF("bws_srv_start() >>> proto = %d port = %d " + "dispatch_func_user_param = %p\n", + proto, port, dispatch_func_user_param); + + if (proto != BSC_WEBSOCKET_HUB_PROTOCOL && + proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) { + DEBUG_PRINTF("bws_srv_start() <<< bad protocol, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size || + !dispatch_func || !timeout_s || !sh) { + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (port < 0 || port > 65535) { + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + ctx = bws_alloc_server_ctx(proto); + + if (!ctx) { + DEBUG_PRINTF( + "bws_srv_start() <<< maximum amount of servers for " + "server proto %d is to small, ret = BSC_WEBSOCKET_NO_RESOURCES\n", + proto); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + bsc_websocket_init_log(); + + pthread_mutex_lock(ctx->mutex); + info.port = port; + info.iface = iface; + info.protocols = protos; + info.gid = -1; + info.uid = -1; + info.server_ssl_cert_mem = cert; + info.server_ssl_cert_mem_len = cert_size; + info.server_ssl_ca_mem = ca_cert; + info.server_ssl_ca_mem_len = ca_cert_size; + info.server_ssl_private_key_mem = key; + info.server_ssl_private_key_mem_len = key_size; + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND; + info.timeout_secs = timeout_s; + info.connect_timeout_secs = timeout_s; + info.user = ctx; + + /* TRICKY: check comments related to lws_context_destroy() call */ + + pthread_mutex_unlock(ctx->mutex); + bsc_websocket_global_lock(); + ctx->wsctx = lws_create_context(&info); + bsc_websocket_global_unlock(); + pthread_mutex_lock(ctx->mutex); + + if (!ctx->wsctx) { + pthread_mutex_unlock(ctx->mutex); + bws_free_server_ctx(ctx); + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + ctx->dispatch_func = dispatch_func; + ctx->user_param = dispatch_func_user_param; + ctx->proto = proto; + r = pthread_attr_init(&attr); + + if(!r) { + r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + } + + if(!r) { + r = pthread_create(&thread_id, &attr, &bws_srv_worker, ctx); + } + + if (r) { + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result lws_context_destroy() + is not thread safe. More over, on different platforms the + function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_srv_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + pthread_mutex_unlock(ctx->mutex); + bsc_websocket_global_lock(); + lws_context_destroy(ctx->wsctx); + bsc_websocket_global_unlock(); + pthread_mutex_lock(ctx->mutex); + ctx->wsctx = NULL; + pthread_mutex_unlock(ctx->mutex); + bws_free_server_ctx(ctx); + DEBUG_PRINTF( + "bws_srv_start() <<< ret = BACNET_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + pthread_attr_destroy(&attr); + pthread_mutex_unlock(ctx->mutex); + *sh = (BSC_WEBSOCKET_SRV_HANDLE)ctx; + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_SUCCESS\n"); + return BSC_WEBSOCKET_SUCCESS; +} + +BSC_WEBSOCKET_RET bws_srv_stop(BSC_WEBSOCKET_SRV_HANDLE sh) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF( + "bws_srv_stop() >>> ctx = %p user_param = %p\n", ctx, ctx->user_param); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_stop() <<< bad websocket handle, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } +#endif + + pthread_mutex_lock(ctx->mutex); + + if (ctx->stop_worker) { + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF( + "bws_srv_stop() <<< ret = BSC_WEBSOCKET_INVALID_OPERATION\n"); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + ctx->stop_worker = true; + /* wake up libwebsockets runloop */ + lws_cancel_service(ctx->wsctx); + pthread_mutex_unlock(ctx->mutex); + + DEBUG_PRINTF("bws_srv_stop() <<< ret = BSC_WEBSOCKET_SUCCESS\n"); + return BSC_WEBSOCKET_SUCCESS; +} + +void bws_srv_disconnect(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + DEBUG_PRINTF("bws_srv_disconnect() >>> sh = %p h = %d\n", sh, h); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_disconnect() <<< bad websocket handle\n"); + return; + } +#endif + + pthread_mutex_lock(ctx->mutex); + if (h >= 0 && h < bws_srv_get_max_sockets(ctx->proto) && + !ctx->stop_worker) { + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process change of connection state */ + ctx->conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(ctx->wsctx); + } + } + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_disconnect() <<<\n"); +} + +void bws_srv_send(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF("bws_srv_send() >>> ctx = %p h = %d\n", ctx, h); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_send() <<< bad websocket handle\n"); + return; + } +#endif + + pthread_mutex_lock(ctx->mutex); + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process send request */ + ctx->conn[h].want_send_data = true; + lws_cancel_service(ctx->wsctx); + } + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_send() <<<\n"); +} + +BSC_WEBSOCKET_RET bws_srv_dispatch_send(BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + uint8_t *payload, + size_t payload_size) +{ + int written; + BSC_WEBSOCKET_RET ret; + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF("bws_srv_dispatch_send() >>> ctx = %p h = %d payload %p " + "payload_size %d\n", + ctx, h, payload, payload_size); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< bad websocket handle, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } +#endif + + if (h < 0 || h >= bws_srv_get_max_sockets(ctx->proto)) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (!payload || !payload_size) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + pthread_mutex_lock(ctx->mutex); + + if (ctx->stop_worker) { + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_dispatch_send() <<< ret = " + "BSC_WEBSOCKET_INVALID_OPERATION\n"); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + if ((ctx->conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) || + !ctx->conn[h].want_send_data || !ctx->conn[h].can_send_data) { + DEBUG_PRINTF( + "bws_srv_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n"); + pthread_mutex_unlock(ctx->mutex); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + written = + lws_write(ctx->conn[h].ws, payload, payload_size, LWS_WRITE_BINARY); + + DEBUG_PRINTF("bws_srv_dispatch_send() %d bytes is sent\n", written); + + if (written < (int)payload_size) { + DEBUG_PRINTF( + "bws_srv_dispatch_send() websocket connection is broken(closed)\n"); + /* tell worker to process change of connection state */ + ctx->conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(ctx->wsctx); + ret = BSC_WEBSOCKET_INVALID_OPERATION; + } else { + ret = BSC_WEBSOCKET_SUCCESS; + } + + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_dispatch_send() <<< ret = %d\n", ret); + return ret; +} + +bool bws_srv_get_peer_ip_addr(BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + uint8_t *ip_str, + size_t ip_str_len, + uint16_t *port) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + int fd; + struct sockaddr_storage addr; + socklen_t len; + const char *ret = NULL; + + if (!ctx || h < 0 || !ip_str || !ip_str_len || !port || + h >= bws_srv_get_max_sockets(ctx->proto)) { + return false; + } + + pthread_mutex_lock(ctx->mutex); + + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE && + ctx->conn[h].ws != NULL && !ctx->stop_worker) { + len = sizeof(addr); + fd = lws_get_socket_fd(ctx->conn[h].ws); + if (fd != -1) { + getpeername(fd, (struct sockaddr *)&addr, &len); + if (addr.ss_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + *port = ntohs(s->sin_port); + ret = inet_ntop( + AF_INET, &s->sin_addr, (char *)ip_str, ip_str_len); + } else { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + *port = ntohs(s->sin6_port); + ret = inet_ntop( + AF_INET6, &s->sin6_addr, (char *)ip_str, ip_str_len); + } + } + } + + pthread_mutex_unlock(ctx->mutex); + + if (ret) { + return true; + } + + return false; +} diff --git a/ports/linux/bsc-event.c b/ports/linux/bsc-event.c new file mode 100644 index 00000000..ddcc3547 --- /dev/null +++ b/ports/linux/bsc-event.c @@ -0,0 +1,181 @@ +/** + * @file + * @brief Implementation of mutex abstraction used in BACNet secure connect. + * @author Kirill Neznamov + * @date August 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include "bacnet/basic/sys/debug.h" +#include "bacnet/datalink/bsc/bsc-event.h" + +#define DEBUG_BSC_EVENT 0 + +#if DEBUG_BSC_EVENT == 1 +#define DEBUG_PRINTF printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +struct BSC_Event { + pthread_mutex_t mutex; + pthread_cond_t cond; + bool v; + size_t counter; +}; + +BSC_EVENT *bsc_event_init(void) +{ + BSC_EVENT *ev; + pthread_mutexattr_t attr; + + if (pthread_mutexattr_init(&attr) != 0) { + return NULL; + } + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { + pthread_mutexattr_destroy(&attr); + return NULL; + } + + ev = malloc(sizeof(BSC_EVENT)); + + if (!ev) { + pthread_mutexattr_destroy(&attr); + return NULL; + } + + if (pthread_mutex_init(&ev->mutex, &attr) != 0) { + pthread_mutexattr_destroy(&attr); + free(ev); + return NULL; + } + + pthread_mutexattr_destroy(&attr); + + if (pthread_cond_init(&ev->cond, NULL) != 0) { + pthread_mutex_destroy(&ev->mutex); + free(ev); + return NULL; + } + + ev->v = false; + ev->counter = 0; + return ev; +} + +void bsc_event_deinit(BSC_EVENT *ev) +{ + pthread_mutex_destroy(&ev->mutex); + pthread_cond_destroy(&ev->cond); + free(ev); +} + +void bsc_event_wait(BSC_EVENT *ev) +{ + pthread_mutex_lock(&ev->mutex); + DEBUG_PRINTF("bsc_event_wait() >>> ev = %p\n", ev); + DEBUG_PRINTF("bsc_event_wait() counter before %zu\n", ev->counter); + ev->counter++; + DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter); + while (!ev->v) { + pthread_cond_wait(&ev->cond, &ev->mutex); + } + DEBUG_PRINTF("bsc_event_wait() ev = %p\n", ev); + DEBUG_PRINTF("bsc_event_wait() before counter %zu\n", ev->counter); + ev->counter--; + DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter); + if (!ev->counter) { + ev->v = false; + DEBUG_PRINTF("bsc_event_wait() reset ev\n"); + } else { + DEBUG_PRINTF("bsc_event_wait() wake up other waiting threads\n"); + pthread_cond_broadcast(&ev->cond); + } + DEBUG_PRINTF("bsc_event_wait() <<< ev = %p\n", ev); + pthread_mutex_unlock(&ev->mutex); +} + +bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout) +{ + struct timespec to; + int r = 0; + + clock_gettime(CLOCK_REALTIME, &to); + to.tv_sec = to.tv_sec + ms_timeout / 1000; + to.tv_nsec = to.tv_nsec + (ms_timeout % 1000) * 1000000; + to.tv_sec += to.tv_nsec / 1000000000; + to.tv_nsec %= 1000000000; + + DEBUG_PRINTF( + "bsc_event_timedwait() >>> before lock ev = %p ev->v = %d\n", ev, + ev->v); + + pthread_mutex_lock(&ev->mutex); + + DEBUG_PRINTF( + "bsc_event_timedwait() >>> after lock ev = %p ev->v = %d\n", ev, ev->v); + + ev->counter++; + DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter); + + while (!ev->v) { + r = pthread_cond_timedwait(&ev->cond, &ev->mutex, &to); + if (r != 0) { + break; + } + } + + if (ev->v) { + if (r != 0) { + DEBUG_PRINTF("Fired!!! r = %d\n", r); + } + r = 0; + } + + ev->counter--; + DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter); + + if (!ev->counter) { + DEBUG_PRINTF("bsc_event_timedwait() event is reset, err = %d\n", r); + ev->v = false; + } else { + DEBUG_PRINTF("bsc_event_timedwait() wake up other waiting threads\n"); + pthread_cond_broadcast(&ev->cond); + } + + DEBUG_PRINTF( + "bsc_event_timedwait() <<< ret = %d, ev = %p ev->v = %d r = %d\n", + r == 0, ev, ev->v, r); + pthread_mutex_unlock(&ev->mutex); + return r == 0; +} + +void bsc_event_signal(BSC_EVENT *ev) +{ + DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev); + pthread_mutex_lock(&ev->mutex); + DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev); + ev->v = true; + pthread_cond_broadcast(&ev->cond); + DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev); + pthread_mutex_unlock(&ev->mutex); + DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev); +} + +void bsc_wait(int seconds) +{ + sleep(seconds); +} + +void bsc_wait_ms(int mseconds) +{ + usleep(mseconds * 1000); +} diff --git a/ports/linux/websocket-cli.c b/ports/linux/websocket-cli.c new file mode 100644 index 00000000..e5d9bc1d --- /dev/null +++ b/ports/linux/websocket-cli.c @@ -0,0 +1,781 @@ +/** + * @file + * @brief Implementation of websocket client interface. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "bacnet/datalink/bsc/websocket.h" +#include "bacnet/basic/sys/debug.h" +#include "websocket-global.h" + +#define DEBUG_WEBSOCKET_CLIENT 0 + +#if DEBUG_WEBSOCKET_CLIENT == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +#ifndef LWS_PROTOCOL_LIST_TERM +#define LWS_PROTOCOL_LIST_TERM \ + { \ + NULL, NULL, 0, 0, 0, NULL, 0 \ + } +#endif + +#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN + +typedef enum { + BSC_WEBSOCKET_STATE_IDLE = 0, + BSC_WEBSOCKET_STATE_CONNECTING = 1, + BSC_WEBSOCKET_STATE_CONNECTED = 2, + BSC_WEBSOCKET_STATE_DISCONNECTING = 3 +} BSC_WEBSOCKET_STATE; + +typedef struct { + struct lws_context *ctx; + struct lws *ws; + BSC_WEBSOCKET_STATE state; + bool want_send_data; + bool can_send_data; + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + uint8_t *fragment_buffer; + size_t fragment_buffer_size; + size_t fragment_buffer_len; + char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN]; + BACNET_ERROR_CODE err_code; +} BSC_WEBSOCKET_CONNECTION; + +/* Some forward function declarations */ + +static int bws_cli_websocket_event( + struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len); + +static const char *bws_hub_protocol = BSC_WEBSOCKET_HUB_PROTOCOL_STR; +static const char *bws_direct_protocol = BSC_WEBSOCKET_DIRECT_PROTOCOL_STR; + +static pthread_mutex_t bws_cli_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + +/* Websockets protocol defined in BACnet/SC \S AB.7.1. */ + +static struct lws_protocols bws_cli_direct_protocol[] = { + { BSC_WEBSOCKET_DIRECT_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL, + 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static struct lws_protocols bws_cli_hub_protocol[] = { + { BSC_WEBSOCKET_HUB_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL, + 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static lws_retry_bo_t bws_retry; + +static BSC_WEBSOCKET_CONNECTION bws_cli_conn[BSC_CLIENT_WEBSOCKETS_MAX_NUM] = { + 0 +}; + +static BSC_WEBSOCKET_HANDLE bws_cli_alloc_connection(void) +{ + int i; + + for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) { + if (bws_cli_conn[i].state == BSC_WEBSOCKET_STATE_IDLE) { + memset(&bws_cli_conn[i], 0, sizeof(bws_cli_conn[i])); + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void bws_cli_free_connection(BSC_WEBSOCKET_HANDLE h) +{ + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_IDLE) { + if (bws_cli_conn[h].fragment_buffer) { + free(bws_cli_conn[h].fragment_buffer); + bws_cli_conn[h].fragment_buffer = NULL; + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = 0; + } + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_IDLE; + bws_cli_conn[h].ws = NULL; + bws_cli_conn[h].ctx = NULL; + } + } +} + +static BSC_WEBSOCKET_HANDLE bws_cli_find_connnection(struct lws *ws) +{ + int i; + for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) { + if (bws_cli_conn[i].ws == ws && + bws_cli_conn[i].state != BSC_WEBSOCKET_STATE_IDLE) { + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void bws_set_err_desc(BSC_WEBSOCKET_HANDLE h, char *err_desc) +{ + size_t len; + if (bws_cli_conn[h].err_code == ERROR_CODE_SUCCESS) { + len = strlen(err_desc) >= sizeof(bws_cli_conn[h].err_desc) + ? sizeof(bws_cli_conn[h].err_desc) - 1 + : strlen(err_desc); + + memcpy(bws_cli_conn[h].err_desc, err_desc, len); + bws_cli_conn[h].err_desc[len] = 0; + + if (strstr(err_desc, "tls:")) { + bws_cli_conn[h].err_code = ERROR_CODE_TLS_ERROR; + } else { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + } + } +} + +static void bws_set_disconnect_reason(BSC_WEBSOCKET_HANDLE h, uint16_t err_code) +{ + bws_cli_conn[h].err_desc[0] = 0; + switch (err_code) { + case LWS_CLOSE_STATUS_NORMAL: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER; + break; + } + case LWS_CLOSE_STATUS_GOINGAWAY: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES; + break; + } + case LWS_CLOSE_STATUS_PROTOCOL_ERR: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR; + break; + } + case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_NO_STATUS: + case LWS_CLOSE_STATUS_RESERVED: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_INVALID_PAYLOAD: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT; + break; + } + case LWS_CLOSE_STATUS_POLICY_VIOLATION: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY; + break; + } + case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG; + break; + } + case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING; + break; + } + case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE; + break; + } + default: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + } +} + +static int bws_cli_websocket_event( + struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + BSC_WEBSOCKET_HANDLE h; + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + uint8_t err_code[2]; + + (void)user; + + DEBUG_PRINTF( + "bws_cli_websocket_event() >>> reason = %d, user = %p, in = %p\n", + reason, user, in); + + switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + + DEBUG_PRINTF("bws_cli_websocket_event() connection established\n"); + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED; + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + pthread_mutex_unlock(&bws_cli_mutex); + dispatch_func( + h, BSC_WEBSOCKET_CONNECTED, 0, NULL, NULL, 0, user_param); + break; + } + case LWS_CALLBACK_CLIENT_RECEIVE: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + DEBUG_PRINTF( + "bws_cli_websocket_event() received %d bytes of data\n", len); + if (!lws_frame_is_binary(wsi)) { + /* According AB.7.5.3 BACnet/SC BVLC Message Exchange, + if a received data frame is not binary, + the WebSocket connection shall be closed with a + status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED. + */ + DEBUG_PRINTF( + "bws_cli_websocket_event() got non-binary frame, " + "close connection for socket %d\n", + h); + lws_close_reason( + wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1\n"); + return -1; + } + if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) { + pthread_mutex_unlock(&bws_cli_mutex); + } else { + if (!bws_cli_conn[h].fragment_buffer) { + DEBUG_PRINTF( + "bws_cli_websocket_event() alloc %d bytes for " + "socket %d\n", + len, h); + bws_cli_conn[h].fragment_buffer = malloc(BSC_RX_BUFFER_LEN); + if (!bws_cli_conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_websocket_event() <<< ret = -1, " + "allocation of %d bytes failed\n", + len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN : len); + return -1; + } + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN; + } + if (bws_cli_conn[h].fragment_buffer_len + len > + bws_cli_conn[h].fragment_buffer_size) { + DEBUG_PRINTF( + "bws_cli_websocket_event() realloc buf of %d bytes" + "for socket %d to %d bytes\n", + bws_cli_conn[h].fragment_buffer_len, h, + bws_cli_conn[h].fragment_buffer_len + len); + bws_cli_conn[h].fragment_buffer = realloc( + bws_cli_conn[h].fragment_buffer, + bws_cli_conn[h].fragment_buffer_len + len); + if (!bws_cli_conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_websocket_event() <<< ret = -1, " + "re-allocation of %d bytes failed\n", + bws_cli_conn[h].fragment_buffer_len + len); + return -1; + } + bws_cli_conn[h].fragment_buffer_size = + bws_cli_conn[h].fragment_buffer_len + len; + } + DEBUG_PRINTF( + "bws_cli_websocket_event() got next %d bytes for " + "socket %d\n", + len, h); + memcpy( + &bws_cli_conn[h] + .fragment_buffer[bws_cli_conn[h].fragment_buffer_len], + in, len); + bws_cli_conn[h].fragment_buffer_len += len; + + if (lws_is_final_fragment(wsi)) { + DEBUG_PRINTF( + "bws_cli_websocket_event() last fragment received\n"); + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + pthread_mutex_unlock(&bws_cli_mutex); + dispatch_func( + h, BSC_WEBSOCKET_RECEIVED, 0, NULL, + bws_cli_conn[h].fragment_buffer, + bws_cli_conn[h].fragment_buffer_len, user_param); + pthread_mutex_lock(&bws_cli_mutex); + bws_cli_conn[h].fragment_buffer_len = 0; + pthread_mutex_unlock(&bws_cli_mutex); + } else { + pthread_mutex_unlock(&bws_cli_mutex); + } + } + break; + } + case LWS_CALLBACK_CLIENT_WRITEABLE: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + + DEBUG_PRINTF( + "bws_cli_websocket_event() can write, state = %d\n", + bws_cli_conn[h].state); + DEBUG_PRINTF( + "bws_cli_websocket_event() ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, bws_cli_conn[h].can_send_data); + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED && + bws_cli_conn[h].want_send_data) { + bws_cli_conn[h].can_send_data = true; + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + pthread_mutex_unlock(&bws_cli_mutex); + dispatch_func( + h, BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, user_param); + pthread_mutex_lock(&bws_cli_mutex); + bws_cli_conn[h].want_send_data = false; + bws_cli_conn[h].can_send_data = false; + DEBUG_PRINTF( + "bws_cli_websocket_event() was send , ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + pthread_mutex_unlock(&bws_cli_mutex); + /* wakeup worker to process internal state */ + lws_cancel_service(bws_cli_conn[h].ctx); + } else { + bws_cli_conn[h].want_send_data = false; + DEBUG_PRINTF( + "bws_cli_websocket_event() no send, ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + pthread_mutex_unlock(&bws_cli_mutex); + } + break; + } + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) { + err_code[0] = ((uint8_t *)in)[1]; + err_code[1] = ((uint8_t *)in)[0]; + bws_set_disconnect_reason(h, *((uint16_t *)&err_code)); + } + pthread_mutex_unlock(&bws_cli_mutex); + break; + } + case LWS_CALLBACK_CLIENT_CLOSED: + case LWS_CALLBACK_CLOSED: + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: { + pthread_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE) { + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR && in) { + bws_set_err_desc(h, (char *)in); + } + pthread_mutex_unlock(&bws_cli_mutex); + /* wakeup worker to process pending event */ + lws_cancel_service(bws_cli_conn[h].ctx); + } else { + pthread_mutex_unlock(&bws_cli_mutex); + } + break; + } + default: { + break; + } + } + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; +} + +static void *bws_cli_worker(void *arg) +{ + BSC_WEBSOCKET_CONNECTION *conn = (BSC_WEBSOCKET_CONNECTION *)arg; + BSC_WEBSOCKET_HANDLE h = conn - &bws_cli_conn[0]; + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN]; + uint16_t err_code; + + while (1) { + DEBUG_PRINTF("bws_cli_worker() try mutex h = %d\n", h); + pthread_mutex_lock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() mutex locked h = %d\n", h); + if (conn->state == BSC_WEBSOCKET_STATE_CONNECTED) { + if (conn->want_send_data) { + DEBUG_PRINTF( + "bws_cli_worker() process request for sending data\n"); + lws_callback_on_writable(conn->ws); + } + } else if (conn->state == BSC_WEBSOCKET_STATE_DISCONNECTING) { + DEBUG_PRINTF("bws_cli_worker() process disconnecting event\n"); + DEBUG_PRINTF("bws_cli_worker() destroy ctx %p\n", conn->ctx); + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result + lws_context_destroy() is not thread safe.More over, on different + platforms the function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + pthread_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + lws_context_destroy(conn->ctx); + bsc_websocket_global_unlock(); + pthread_mutex_lock(&bws_cli_mutex); + dispatch_func = conn->dispatch_func; + user_param = conn->user_param; + err_code = conn->err_code; + if (err_code != ERROR_CODE_SUCCESS) { + memcpy(err_desc, conn->err_desc, sizeof(err_desc)); + } + bws_cli_free_connection(h); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() unlock mutex\n"); + dispatch_func( + h, BSC_WEBSOCKET_DISCONNECTED, err_code, + err_code != ERROR_CODE_SUCCESS ? err_desc : NULL, NULL, 0, + user_param); + return NULL; + } + DEBUG_PRINTF("bws_cli_worker() unlock mutex\n"); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() going to block on lws_service() call\n"); + lws_service(conn->ctx, 0); + } + + return NULL; +} + +BSC_WEBSOCKET_RET bws_cli_connect( + BSC_WEBSOCKET_PROTOCOL proto, + char *url, + uint8_t *ca_cert, + size_t ca_cert_size, + uint8_t *cert, + size_t cert_size, + uint8_t *key, + size_t key_size, + size_t timeout_s, + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func, + void *dispatch_func_user_param, + BSC_WEBSOCKET_HANDLE *out_handle) +{ + struct lws_context_creation_info info; + char tmp_url[BSC_WSURL_MAX_LEN]; + const char *prot = NULL, *addr = NULL, *path = NULL; + int port = -1; + BSC_WEBSOCKET_HANDLE h; + struct lws_client_connect_info cinfo; + pthread_t thread_id; + size_t len; + pthread_attr_t attr; + int r; + + DEBUG_PRINTF("bws_cli_connect() >>> proto = %d, url = %s\n", proto, url); + + if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size || + !url || !out_handle || !timeout_s || !dispatch_func) { + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + *out_handle = BSC_WEBSOCKET_INVALID_HANDLE; + + if (proto != BSC_WEBSOCKET_HUB_PROTOCOL && + proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) { + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + memset(&info, 0, sizeof(info)); + memset(&cinfo, 0, sizeof(cinfo)); + + len = strlen(url) >= sizeof(tmp_url) ? sizeof(tmp_url) - 1 : strlen(url); + memcpy(tmp_url, url, len); + tmp_url[len] = 0; + + bsc_websocket_init_log(); + pthread_mutex_lock(&bws_cli_mutex); + + if (lws_parse_uri(tmp_url, &prot, &addr, &port, &path) != 0 || port == -1 || + !prot || !addr || !path) { + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (strcmp(prot, "wss") != 0) { + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + h = bws_cli_alloc_connection(); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTING; + bws_cli_conn[h].fragment_buffer = NULL; + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = 0; + bws_cli_conn[h].dispatch_func = dispatch_func; + bws_cli_conn[h].user_param = dispatch_func_user_param; + info.port = CONTEXT_PORT_NO_LISTEN; + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + info.protocols = bws_cli_hub_protocol; + } else { + info.protocols = bws_cli_direct_protocol; + } + info.gid = -1; + info.uid = -1; + info.client_ssl_cert_mem = cert; + info.client_ssl_cert_mem_len = cert_size; + info.client_ssl_ca_mem = ca_cert; + info.client_ssl_ca_mem_len = ca_cert_size; + info.client_ssl_key_mem = key; + info.client_ssl_key_mem_len = key_size; + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND; + info.timeout_secs = timeout_s; + info.connect_timeout_secs = timeout_s; + + /* TRICKY: check comments related to lws_context_destroy() call */ + + pthread_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + bws_cli_conn[h].ctx = lws_create_context(&info); + bsc_websocket_global_unlock(); + pthread_mutex_lock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() created ctx %p\n", bws_cli_conn[h].ctx); + + if (!bws_cli_conn[h].ctx) { + bws_cli_free_connection(h); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + bws_cli_conn[h].ws = NULL; + cinfo.context = bws_cli_conn[h].ctx; + cinfo.address = addr; + cinfo.origin = cinfo.address; + cinfo.host = cinfo.address; + cinfo.port = port; + cinfo.path = path; + cinfo.pwsi = &bws_cli_conn[h].ws; + cinfo.alpn = "h2;http/1.1"; + bws_retry.secs_since_valid_ping = 3; + bws_retry.secs_since_valid_hangup = 10; + cinfo.retry_and_idle_policy = &bws_retry; + cinfo.ssl_connection = LCCSCF_USE_SSL | + LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED; + + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + cinfo.protocol = bws_hub_protocol; + } else { + cinfo.protocol = bws_direct_protocol; + } + + bws_cli_conn[h].err_code = ERROR_CODE_SUCCESS; + *out_handle = h; + pthread_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + lws_client_connect_via_info(&cinfo); + bsc_websocket_global_unlock(); + r = pthread_attr_init(&attr); + + if (!r) { + r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + } + + if (!r) { + r = pthread_create( + &thread_id, &attr, &bws_cli_worker, &bws_cli_conn[h]); + } + + if (r) { + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result lws_context_destroy() + is not thread safe. More over, on different platforms the + function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + /* pthread_mutex_unlock(&bws_cli_mutex); */ + bsc_websocket_global_lock(); + lws_context_destroy(bws_cli_conn[h].ctx); + bsc_websocket_global_unlock(); + pthread_mutex_lock(&bws_cli_mutex); + bws_cli_free_connection(h); + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + pthread_attr_destroy(&attr); + DEBUG_PRINTF("bws_cli_connect() <<< ret = %d\n", BSC_WEBSOCKET_SUCCESS); + return BSC_WEBSOCKET_SUCCESS; +} + +void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_cli_disconnect() >>> h = %d\n", h); + + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + pthread_mutex_lock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_disconnect() state = %d\n", bws_cli_conn[h].state); + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTING || + bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process change of connection state */ + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(bws_cli_conn[h].ctx); + } + pthread_mutex_unlock(&bws_cli_mutex); + } + + DEBUG_PRINTF("bws_cli_disconnect() <<<\n"); +} + +void bws_cli_send(BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_cli_send() >>> h = %d\n", h); + + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + pthread_mutex_lock(&bws_cli_mutex); + + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process send request */ + bws_cli_conn[h].want_send_data = true; + DEBUG_PRINTF("bws_cli_send() cs = 1\n"); + lws_cancel_service(bws_cli_conn[h].ctx); + } + + pthread_mutex_unlock(&bws_cli_mutex); + } + + DEBUG_PRINTF("bws_cli_send() <<<\n"); +} + +BSC_WEBSOCKET_RET bws_cli_dispatch_send( + BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size) +{ + int written; + BSC_WEBSOCKET_RET ret; + + DEBUG_PRINTF( + "bws_cli_dispatch_send() >>> h = %d, payload = %p, payload_size = %d\n", + h, payload, payload_size); + + if (h < 0 || h >= BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + DEBUG_PRINTF( + "bws_cli_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + pthread_mutex_lock(&bws_cli_mutex); + + if ((bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) || + !bws_cli_conn[h].want_send_data || !bws_cli_conn[h].can_send_data) { + DEBUG_PRINTF( + "bws_cli_dispatch_send() state = %d, ws = %d, cs = %d\n", + bws_cli_conn[h].state, bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = " + "BSC_WEBSOCKET_INVALID_OPERATION\n"); + pthread_mutex_unlock(&bws_cli_mutex); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + written = + lws_write(bws_cli_conn[h].ws, payload, payload_size, LWS_WRITE_BINARY); + + DEBUG_PRINTF("bws_cli_dispatch_send() %d bytes is sent\n", written); + + if (written < (int)payload_size) { + DEBUG_PRINTF( + "bws_cli_dispatch_send() websocket connection is broken(closed)\n"); + /* tell worker to process change of connection state */ + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(bws_cli_conn[h].ctx); + ret = BSC_WEBSOCKET_INVALID_OPERATION; + } else { + ret = BSC_WEBSOCKET_SUCCESS; + } + + pthread_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = %d\n", ret); + return ret; +} diff --git a/ports/linux/websocket-global.c b/ports/linux/websocket-global.c new file mode 100644 index 00000000..1ed0c91c --- /dev/null +++ b/ports/linux/websocket-global.c @@ -0,0 +1,129 @@ +/** + * @file + * @brief Implementation of global websocket functions. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "websocket-global.h" +#include "bacnet/basic/sys/debug.h" + +#if LWS_MAX_SMP <= 1 +#warning \ + "Libwebsockets must be built with LWS_MAX_SMP > 1 (otherwise it does not support thread synchronization in correct way)" +#endif + +static pthread_mutex_t websocket_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +static pthread_mutex_t websocket_dispatch_mutex = + PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + +#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1) + +void bsc_websocket_global_lock(void) +{ + pthread_mutex_lock(&websocket_mutex); +} + +void bsc_websocket_global_unlock(void) +{ + pthread_mutex_unlock(&websocket_mutex); +} + +void bws_dispatch_lock(void) +{ + pthread_mutex_lock(&websocket_dispatch_mutex); +} + +void bws_dispatch_unlock(void) +{ + pthread_mutex_unlock(&websocket_dispatch_mutex); +} + +#else +static volatile int websocket_mutex_cnt = 0; +static volatile int websocket_dispatch_mutex_cnt = 0; + +void bsc_websocket_global_lock_dbg(char *f, int line) +{ + printf( + "bsc_websocket_global_lock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, + line, websocket_mutex_cnt, pthread_self()); + websocket_mutex_cnt++; + fflush(stdout); + pthread_mutex_lock(&websocket_mutex); + printf( + "bsc_websocket_global_lock_dbg() <<< lock_cnt %d tid = %ld\n", + websocket_mutex_cnt, pthread_self()); + fflush(stdout); +} + +void bsc_websocket_global_unlock_dbg(char *f, int line) +{ + printf( + "bsc_websocket_global_unlock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", + f, line, websocket_mutex_cnt, pthread_self()); + websocket_mutex_cnt--; + fflush(stdout); + pthread_mutex_unlock(&websocket_mutex); + printf( + "bsc_websocket_global_unlock_dbg() <<< lock_cnt %d tid = %ld\n", + websocket_mutex_cnt, pthread_self()); + fflush(stdout); +} + +void bws_dispatch_lock_dbg(char *f, int line) +{ + printf( + "bws_dispatch_lock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, + websocket_dispatch_mutex_cnt, pthread_self()); + websocket_dispatch_mutex_cnt++; + fflush(stdout); + pthread_mutex_lock(&websocket_dispatch_mutex); + printf( + "bws_dispatch_lock_dbg() <<< lock_cnt %d tid = %ld\n", + websocket_dispatch_mutex_cnt, pthread_self()); + fflush(stdout); +} + +void bws_dispatch_unlock_dbg(char *f, int line) +{ + printf( + "bws_dispatch_unlock_dbg() >>> %s:%d lock_cnt %d tid = %ld\n", f, line, + websocket_dispatch_mutex_cnt, pthread_self()); + websocket_dispatch_mutex_cnt--; + fflush(stdout); + pthread_mutex_unlock(&websocket_dispatch_mutex); + printf( + "bws_dispatch_unlock_dbg() <<< lock_cnt %d tid = %ld\n", + websocket_dispatch_mutex_cnt, pthread_self()); + fflush(stdout); +} + +#endif + +static bool bsc_websocket_log_initialized = false; + +void bsc_websocket_init_log(void) +{ + bsc_websocket_global_lock(); + if (!bsc_websocket_log_initialized) { + bsc_websocket_log_initialized = true; +#if DEBUG_LIBWEBSOCKETS_ENABLED == 1 + printf("LWS_MAX_SMP = %d", LWS_MAX_SMP); + lws_set_log_level( + LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | + LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY | + LLL_USER | LLL_THREAD, + NULL); +#else + lws_set_log_level(0, NULL); +#endif + } + bsc_websocket_global_unlock(); +} diff --git a/ports/linux/websocket-global.h b/ports/linux/websocket-global.h new file mode 100644 index 00000000..ebed48c0 --- /dev/null +++ b/ports/linux/websocket-global.h @@ -0,0 +1,29 @@ +/** + * @file + * @brief Global websocket functions + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef __BSC_WEBSOCKET_MUTEX_INCLUDED__ +#define __BSC_WEBSOCKET_MUTEX_INCLUDED__ + +#ifndef BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED +#define BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED 0 +#endif + +#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1) +void bsc_websocket_global_lock(void); +void bsc_websocket_global_unlock(void); +#else +void bsc_websocket_global_lock_dbg(char *f, int line); +void bsc_websocket_global_unlock_dbg(char *f, int line); +#define bsc_websocket_global_lock() \ + bsc_websocket_global_lock_dbg(__FILE__, __LINE__); +#define bsc_websocket_global_unlock() \ + bsc_websocket_global_unlock_dbg(__FILE__, __LINE__); +#endif + +void bsc_websocket_init_log(void); + +#endif diff --git a/ports/linux/websocket-srv.c b/ports/linux/websocket-srv.c new file mode 100644 index 00000000..f9afdb24 --- /dev/null +++ b/ports/linux/websocket-srv.c @@ -0,0 +1,1028 @@ +/** + * @file + * @brief Implementation of server websocket interface. + * @author Kirill Neznamov + * @date June 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "bacnet/datalink/bsc/websocket.h" +#include "bacnet/basic/sys/debug.h" +#include "websocket-global.h" +#include + +#define DEBUG_WEBSOCKET_SERVER 0 + +#if DEBUG_WEBSOCKET_SERVER == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN + +#ifndef LWS_PROTOCOL_LIST_TERM +#define LWS_PROTOCOL_LIST_TERM \ + { \ + NULL, NULL, 0, 0, 0, NULL, 0 \ + } +#endif + +typedef enum { + BSC_WEBSOCKET_STATE_IDLE = 0, + BSC_WEBSOCKET_STATE_CONNECTED = 1, + BSC_WEBSOCKET_STATE_DISCONNECTING = 2 +} BSC_WEBSOCKET_STATE; + +/* Some forward function declarations */ + +static int bws_srv_websocket_event( + struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len); + +typedef struct { + struct lws_context *ctx; + struct lws *ws; + BSC_WEBSOCKET_STATE state; + bool want_send_data; + bool can_send_data; + uint8_t *fragment_buffer; + size_t fragment_buffer_size; + size_t fragment_buffer_len; + BACNET_ERROR_CODE err_code; +} BSC_WEBSOCKET_CONNECTION; + +#if BSC_CONF_WEBSOCKET_SERVERS_NUM < 1 +#error "BSC_CONF_WEBSOCKET_SERVERS_NUM must be >= 1" +#endif + +static pthread_mutex_t bws_global_mutex = + PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +static pthread_mutex_t bws_srv_direct_mutex[BSC_CONF_WEBSOCKET_SERVERS_NUM]; +static pthread_mutex_t bws_srv_hub_mutex[BSC_CONF_WEBSOCKET_SERVERS_NUM]; + +#if BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM > 0 +static BSC_WEBSOCKET_CONNECTION + bws_hub_conn[BSC_CONF_WEBSOCKET_SERVERS_NUM] + [BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM] = { 0 }; +#else +static BSC_WEBSOCKET_CONNECTION bws_hub_conn[1][1] = { 0 }; +#endif + +#if BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM > 0 +static BSC_WEBSOCKET_CONNECTION + bws_direct_conn[BSC_CONF_WEBSOCKET_SERVERS_NUM] + [BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM] = { 0 }; +#else +static BSC_WEBSOCKET_CONNECTION bws_direct_conn[1][1] = { 0 }; +#endif + +typedef struct BACNetWebsocketServerContext { + bool used; + struct lws_context *wsctx; + BSC_WEBSOCKET_PROTOCOL proto; + BSC_WEBSOCKET_CONNECTION *conn; + pthread_mutex_t *mutex; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + bool stop_worker; +} BSC_WEBSOCKET_CONTEXT; + +static BSC_WEBSOCKET_CONTEXT bws_hub_ctx[BSC_CONF_WEBSOCKET_SERVERS_NUM] = { + 0 +}; +static BSC_WEBSOCKET_CONTEXT bws_direct_ctx[BSC_CONF_WEBSOCKET_SERVERS_NUM] = { + 0 +}; + +static bool bws_mutex_init(pthread_mutex_t *mutex) +{ + pthread_mutexattr_t attr; + + if (pthread_mutexattr_init(&attr) != 0) { + return false; + } + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { + pthread_mutexattr_destroy(&attr); + return false; + } + + if (pthread_mutex_init(mutex, &attr) != 0) { + pthread_mutexattr_destroy(&attr); + return false; + } + + pthread_mutexattr_destroy(&attr); + return true; +} + +static BSC_WEBSOCKET_CONTEXT *bws_alloc_server_ctx(BSC_WEBSOCKET_PROTOCOL proto) +{ + int i; + BSC_WEBSOCKET_CONTEXT *ctx = (proto == BSC_WEBSOCKET_HUB_PROTOCOL) + ? &bws_hub_ctx[0] + : &bws_direct_ctx[0]; + + pthread_mutex_lock(&bws_global_mutex); + DEBUG_PRINTF("bws_alloc_server_ctx() >>> proto = %d\n", proto); + + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (!ctx[i].used) { + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + ctx[i].mutex = &bws_srv_hub_mutex[i]; + ctx[i].conn = &bws_hub_conn[i][0]; + } else { + ctx[i].mutex = &bws_srv_direct_mutex[i]; + ctx[i].conn = &bws_direct_conn[i][0]; + } + if (!bws_mutex_init(ctx[i].mutex)) { + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + pthread_mutex_unlock(&bws_global_mutex); + return NULL; + } + ctx[i].used = true; + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + pthread_mutex_unlock(&bws_global_mutex); + return &ctx[i]; + } + } + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + pthread_mutex_unlock(&bws_global_mutex); + return NULL; +} + +static void bws_set_disconnect_reason( + BSC_WEBSOCKET_CONTEXT *ctx, BSC_WEBSOCKET_HANDLE h, uint16_t err_code) +{ + switch (err_code) { + case LWS_CLOSE_STATUS_NORMAL: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER; + break; + } + case LWS_CLOSE_STATUS_GOINGAWAY: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES; + break; + } + case LWS_CLOSE_STATUS_PROTOCOL_ERR: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR; + break; + } + case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_NO_STATUS: + case LWS_CLOSE_STATUS_RESERVED: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_INVALID_PAYLOAD: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT; + break; + } + case LWS_CLOSE_STATUS_POLICY_VIOLATION: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY; + break; + } + case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG; + break; + } + case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING; + break; + } + case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE; + break; + } + default: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + } +} + +static void bws_free_server_ctx(BSC_WEBSOCKET_CONTEXT *ctx) +{ + pthread_mutex_lock(&bws_global_mutex); + DEBUG_PRINTF("bws_free_server_ctx() >>> ctx = %p\n", ctx); + ctx->used = false; + ctx->wsctx = NULL; + ctx->conn = NULL; + pthread_mutex_destroy(ctx->mutex); + ctx->mutex = NULL; + ctx->dispatch_func = NULL; + ctx->user_param = NULL; + DEBUG_PRINTF("bws_free_server_ctx() <<< \n"); + pthread_mutex_unlock(&bws_global_mutex); +} + +#if DEBUG_ENABLED == 1 +static bool bws_validate_ctx_pointer(BSC_WEBSOCKET_CONTEXT *ctx) +{ + bool is_validated = false; + int i; + + pthread_mutex_lock(&bws_global_mutex); + + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (ctx == &bws_hub_ctx[i]) { + is_validated = true; + break; + } + } + + if (!is_validated) { + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (ctx == &bws_direct_ctx[i]) { + is_validated = true; + break; + } + } + } + + pthread_mutex_unlock(&bws_global_mutex); + return is_validated; +} +#endif + +static int bws_srv_get_max_sockets(BSC_WEBSOCKET_PROTOCOL proto) +{ + int max = 0; + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + max = BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM; + } else if (proto == BSC_WEBSOCKET_DIRECT_PROTOCOL) { + max = BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM; + } + return max; +} + +static BSC_WEBSOCKET_HANDLE bws_srv_alloc_connection(BSC_WEBSOCKET_CONTEXT *ctx) +{ + int i; + + DEBUG_PRINTF("bws_srv_alloc_connection() >>> ctx = %p\n", ctx); + + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + if (ctx->conn[i].state == BSC_WEBSOCKET_STATE_IDLE) { + memset(&ctx->conn[i], 0, sizeof(ctx->conn[i])); + DEBUG_PRINTF("bws_srv_alloc_connection() <<< ret = %d\n", i); + return i; + } + } + + DEBUG_PRINTF("bws_srv_alloc_connection() <<< ret = " + "BSC_WEBSOCKET_INVALID_HANDLE\n"); + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void +bws_srv_free_connection(BSC_WEBSOCKET_CONTEXT *ctx, BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_srv_free_connection() >>> ctx = %p, h = %d\n", ctx, h); + + if (h >= 0 && h < bws_srv_get_max_sockets(ctx->proto)) { + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE) { + if (ctx->conn[h].fragment_buffer) { + free(ctx->conn[h].fragment_buffer); + ctx->conn[h].fragment_buffer = NULL; + ctx->conn[h].fragment_buffer_len = 0; + ctx->conn[h].fragment_buffer_size = 0; + } + ctx->conn[h].state = BSC_WEBSOCKET_STATE_IDLE; + ctx->conn[h].ws = NULL; + } + } + DEBUG_PRINTF("bws_srv_free_connection() <<<\n"); +} + +static BSC_WEBSOCKET_HANDLE +bws_find_connnection(BSC_WEBSOCKET_CONTEXT *ctx, struct lws *ws) +{ + int i; + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + if (ctx->conn[i].ws == ws && + ctx->conn[i].state != BSC_WEBSOCKET_STATE_IDLE) { + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static int bws_srv_websocket_event( + struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + BSC_WEBSOCKET_HANDLE h; + BSC_WEBSOCKET_CONTEXT *ctx = + (BSC_WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi)); + int ret = 0; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + bool stop_worker; + uint8_t err_code[2]; + uint16_t err; + (void)user; + + DEBUG_PRINTF( + "bws_srv_websocket_event() >>> ctx = %p, user_param = %p, " + "proto = %d, wsi = %p, " + "reason = %d, in = %p, len = %d\n", + ctx, ctx->user_param, ctx->proto, wsi, reason, in, len); + + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: { + pthread_mutex_lock(ctx->mutex); + DEBUG_PRINTF("bws_srv_websocket_event() established connection\n"); + h = bws_srv_alloc_connection(ctx); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF("bws_srv_websocket_event() no free sockets, " + "dropping incoming connection\n"); + pthread_mutex_unlock(ctx->mutex); + return -1; + } + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d set state of" + " socket %d to BACNET_WEBSOCKET_STATE_CONNECTING\n", + ctx, ctx->proto, h); + ctx->conn[h].ws = wsi; + ctx->conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED; + ctx->conn[h].err_code = ERROR_CODE_SUCCESS; + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + pthread_mutex_unlock(ctx->mutex); + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, h, BSC_WEBSOCKET_CONNECTED, 0, + NULL, NULL, 0, user_param); + /* wakeup worker to process pending event */ + lws_cancel_service(ctx->wsctx); + break; + } + case LWS_CALLBACK_CLOSED: { + DEBUG_PRINTF("bws_srv_websocket_event() closed connection\n"); + pthread_mutex_lock(ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + pthread_mutex_unlock(ctx->mutex); + } else { + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p user_param = %p " + "proto %d state of " + "socket %d is %d\n", + ctx, ctx->user_param, ctx->proto, h, ctx->conn[h].state); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + stop_worker = ctx->stop_worker; + err = ctx->conn[h].err_code; + bws_srv_free_connection(ctx, h); + pthread_mutex_unlock(ctx->mutex); + if (!stop_worker) { + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_DISCONNECTED, err, NULL, NULL, 0, + user_param); + } + } + break; + } + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + pthread_mutex_lock(ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) { + err_code[0] = ((uint8_t *)in)[1]; + err_code[1] = ((uint8_t *)in)[0]; + bws_set_disconnect_reason(ctx, h, *((uint16_t *)&err_code)); + } + pthread_mutex_unlock(ctx->mutex); + break; + } + case LWS_CALLBACK_RECEIVE: { + pthread_mutex_lock(ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + pthread_mutex_unlock(ctx->mutex); + } else { + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d " + "received %d bytes of " + "data for websocket %d\n", + ctx, ctx->proto, len, h); + if (!lws_frame_is_binary(wsi)) { + /* According AB.7.5.3 BACnet/SC BVLC Message Exchange, + if a received data frame is not binary, + the WebSocket connection shall be closed with a + status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED. + */ + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d got " + "non-binary frame, " + "close websocket %d\n", + ctx, ctx->proto, h); + lws_close_reason( + wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0); + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = -1\n"); + return -1; + } + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) { + pthread_mutex_unlock(ctx->mutex); + } else { + if (!ctx->conn[h].fragment_buffer) { + ctx->conn[h].fragment_buffer = + malloc(BSC_RX_BUFFER_LEN); + if (!ctx->conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, + 0); + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF( + "bws_srv_websocket_event() <<< ret = " + "-1, allocation of %d bytes failed\n", + len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN + : len); + return -1; + } + ctx->conn[h].fragment_buffer_len = 0; + ctx->conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN; + } + if (ctx->conn[h].fragment_buffer_len + len > + ctx->conn[h].fragment_buffer_size) { + DEBUG_PRINTF( + "bws_srv_websocket_event() realloc buf of %d bytes" + "for socket %d to %d bytes\n", + ctx->conn[h].fragment_buffer_len, h, + ctx->conn[h].fragment_buffer_len + len); + + ctx->conn[h].fragment_buffer = realloc( + ctx->conn[h].fragment_buffer, + ctx->conn[h].fragment_buffer_len + len); + if (!ctx->conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, + 0); + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF( + "bws_srv_websocket_event() <<< ret = -1, " + "re-allocation of %d bytes failed\n", + ctx->conn[h].fragment_buffer_len + len); + return -1; + } + ctx->conn[h].fragment_buffer_size = + ctx->conn[h].fragment_buffer_len + len; + } + DEBUG_PRINTF( + "bws_srv_websocket_event() got next %d bytes for " + "socket %d total_len %d\n", + len, h, ctx->conn[h].fragment_buffer_len); + memcpy( + &ctx->conn[h] + .fragment_buffer[ctx->conn[h].fragment_buffer_len], + in, len); + ctx->conn[h].fragment_buffer_len += len; + if (lws_is_final_fragment(wsi) && !ctx->stop_worker) { + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + pthread_mutex_unlock(ctx->mutex); + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_RECEIVED, 0, NULL, + ctx->conn[h].fragment_buffer, + ctx->conn[h].fragment_buffer_len, user_param); + pthread_mutex_lock(ctx->mutex); + ctx->conn[h].fragment_buffer_len = 0; + pthread_mutex_unlock(ctx->mutex); + } else { + pthread_mutex_unlock(ctx->mutex); + } + } + } + break; + } + case LWS_CALLBACK_SERVER_WRITEABLE: { + pthread_mutex_lock(ctx->mutex); + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d can write\n", ctx, + ctx->proto); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + pthread_mutex_unlock(ctx->mutex); + } else { + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d socket " + "%d state = %d\n", + ctx, ctx->proto, h, ctx->conn[h].state); + + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_DISCONNECTING) { + /* if -1 returned from this callback libwebsocket closes ++ websocket related to wsi */ + ret = -1; + } + + if ((ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE) && + !ctx->stop_worker && ctx->conn[h].want_send_data) { + ctx->conn[h].can_send_data = true; + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + stop_worker = ctx->stop_worker; + pthread_mutex_unlock(ctx->mutex); + if (!stop_worker) { + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, + user_param); + } + pthread_mutex_lock(ctx->mutex); + ctx->conn[h].want_send_data = false; + ctx->conn[h].can_send_data = false; + pthread_mutex_unlock(ctx->mutex); + /* wakeup worker to process internal state */ + lws_cancel_service(ctx->wsctx); + } else { + ctx->conn[h].want_send_data = false; + pthread_mutex_unlock(ctx->mutex); + } + } + break; + } + default: { + break; + } + } + DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = %d\n", ret); + return ret; +} + +static void *bws_srv_worker(void *arg) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)arg; + int i; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + + DEBUG_PRINTF( + "bws_srv_worker() started for ctx %p proto %d user_param %p\n", ctx, + ctx->proto, ctx->user_param); + + pthread_mutex_lock(ctx->mutex); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + pthread_mutex_unlock(ctx->mutex); + + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, 0, BSC_WEBSOCKET_SERVER_STARTED, 0, NULL, + NULL, 0, user_param); + + while (1) { + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d blocked user_param %p\n", ctx, + ctx->proto, ctx->user_param); + pthread_mutex_lock(ctx->mutex); + + if (ctx->stop_worker) { + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param %p proto %d going " + "to stop\n", + ctx, ctx->user_param, ctx->proto); + DEBUG_PRINTF( + "bws_srv_worker() destroy wsctx %p, ctx = %p, " + "user_param = %p\n", + ctx->wsctx, ctx, ctx->user_param); + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result + lws_context_destroy() is not thread safe.More over, on different + platforms the function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_srv_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + pthread_mutex_unlock(ctx->mutex); + bsc_websocket_global_lock(); + lws_context_destroy(ctx->wsctx); + bsc_websocket_global_unlock(); + pthread_mutex_lock(ctx->mutex); + ctx->wsctx = NULL; + DEBUG_PRINTF("bws_srv_worker() set wsctx %p\n", ctx->wsctx); + ctx->stop_worker = false; + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d emitting stop event\n", ctx, + ctx->proto); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param = %p\n", ctx, user_param); + pthread_mutex_unlock(ctx->mutex); + dispatch_func( + ctx, 0, BSC_WEBSOCKET_SERVER_STOPPED, 0, NULL, NULL, 0, + user_param); + bws_free_server_ctx(ctx); + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d stopped\n", ctx, ctx->proto); + return NULL; + } + + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param %p proto %d " + "socket %d(%p) state = %d\n", + ctx, ctx->user_param, ctx->proto, i, &ctx->conn[i], + ctx->conn[i].state); + if (ctx->conn[i].state == BSC_WEBSOCKET_STATE_CONNECTED) { + if (ctx->conn[i].want_send_data) { + DEBUG_PRINTF( + "bws_srv_worker() process request for sending " + "data on socket %d\n", + i); + lws_callback_on_writable(ctx->conn[i].ws); + } + } else if ( + ctx->conn[i].state == BSC_WEBSOCKET_STATE_DISCONNECTING) { + DEBUG_PRINTF( + "bws_srv_worker() process disconnecting event on " + "socket %d\n", + i); + lws_callback_on_writable(ctx->conn[i].ws); + } + } + + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d unblocked\n", ctx, ctx->proto); + pthread_mutex_unlock(ctx->mutex); + + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param %p proto %d going to block on " + "lws_service() call\n", + ctx, ctx->user_param, ctx->proto); + lws_service(ctx->wsctx, 0); + } + + return NULL; +} + +BSC_WEBSOCKET_RET bws_srv_start( + BSC_WEBSOCKET_PROTOCOL proto, + int port, + char *iface, + uint8_t *ca_cert, + size_t ca_cert_size, + uint8_t *cert, + size_t cert_size, + uint8_t *key, + size_t key_size, + size_t timeout_s, + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func, + void *dispatch_func_user_param, + BSC_WEBSOCKET_SRV_HANDLE *sh) +{ + pthread_t thread_id; + struct lws_context_creation_info info = { 0 }; + BSC_WEBSOCKET_CONTEXT *ctx; + pthread_attr_t attr; + int r; + struct lws_protocols protos[] = { { NULL, bws_srv_websocket_event, 0, 0, 0, + NULL, 0 }, + LWS_PROTOCOL_LIST_TERM }; + protos[0].name = (proto == BSC_WEBSOCKET_HUB_PROTOCOL) + ? BSC_WEBSOCKET_HUB_PROTOCOL_STR + : BSC_WEBSOCKET_DIRECT_PROTOCOL_STR; + + DEBUG_PRINTF( + "bws_srv_start() >>> proto = %d port = %d " + "dispatch_func_user_param = %p\n", + proto, port, dispatch_func_user_param); + + if (proto != BSC_WEBSOCKET_HUB_PROTOCOL && + proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) { + DEBUG_PRINTF("bws_srv_start() <<< bad protocol, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size || + !dispatch_func || !timeout_s || !sh) { + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (port < 0 || port > 65535) { + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + ctx = bws_alloc_server_ctx(proto); + + if (!ctx) { + DEBUG_PRINTF( + "bws_srv_start() <<< maximum amount of servers for " + "server proto %d is to small, ret = BSC_WEBSOCKET_NO_RESOURCES\n", + proto); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + bsc_websocket_init_log(); + + pthread_mutex_lock(ctx->mutex); + info.port = port; + info.iface = iface; + info.protocols = protos; + info.gid = -1; + info.uid = -1; + info.server_ssl_cert_mem = cert; + info.server_ssl_cert_mem_len = cert_size; + info.server_ssl_ca_mem = ca_cert; + info.server_ssl_ca_mem_len = ca_cert_size; + info.server_ssl_private_key_mem = key; + info.server_ssl_private_key_mem_len = key_size; + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND; + info.timeout_secs = timeout_s; + info.connect_timeout_secs = timeout_s; + info.user = ctx; + + /* TRICKY: check comments related to lws_context_destroy() call */ + + pthread_mutex_unlock(ctx->mutex); + bsc_websocket_global_lock(); + ctx->wsctx = lws_create_context(&info); + bsc_websocket_global_unlock(); + pthread_mutex_lock(ctx->mutex); + + if (!ctx->wsctx) { + pthread_mutex_unlock(ctx->mutex); + bws_free_server_ctx(ctx); + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + ctx->dispatch_func = dispatch_func; + ctx->user_param = dispatch_func_user_param; + ctx->proto = proto; + r = pthread_attr_init(&attr); + + if (!r) { + r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + } + + if (!r) { + r = pthread_create(&thread_id, &attr, &bws_srv_worker, ctx); + } + + if (r) { + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result lws_context_destroy() + is not thread safe. More over, on different platforms the + function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_srv_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + pthread_mutex_unlock(ctx->mutex); + bsc_websocket_global_lock(); + lws_context_destroy(ctx->wsctx); + bsc_websocket_global_unlock(); + pthread_mutex_lock(ctx->mutex); + ctx->wsctx = NULL; + pthread_mutex_unlock(ctx->mutex); + bws_free_server_ctx(ctx); + DEBUG_PRINTF( + "bws_srv_start() <<< ret = BACNET_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + pthread_attr_destroy(&attr); + pthread_mutex_unlock(ctx->mutex); + *sh = (BSC_WEBSOCKET_SRV_HANDLE)ctx; + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_SUCCESS\n"); + return BSC_WEBSOCKET_SUCCESS; +} + +BSC_WEBSOCKET_RET bws_srv_stop(BSC_WEBSOCKET_SRV_HANDLE sh) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF( + "bws_srv_stop() >>> ctx = %p user_param = %p\n", ctx, ctx->user_param); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_stop() <<< bad websocket handle, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } +#endif + + pthread_mutex_lock(ctx->mutex); + + if (ctx->stop_worker) { + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF( + "bws_srv_stop() <<< ret = BSC_WEBSOCKET_INVALID_OPERATION\n"); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + ctx->stop_worker = true; + /* wake up libwebsockets runloop */ + lws_cancel_service(ctx->wsctx); + pthread_mutex_unlock(ctx->mutex); + + DEBUG_PRINTF("bws_srv_stop() <<< ret = BSC_WEBSOCKET_SUCCESS\n"); + return BSC_WEBSOCKET_SUCCESS; +} + +void bws_srv_disconnect(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + DEBUG_PRINTF("bws_srv_disconnect() >>> sh = %p h = %d\n", sh, h); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_disconnect() <<< bad websocket handle\n"); + return; + } +#endif + + pthread_mutex_lock(ctx->mutex); + if (h >= 0 && h < bws_srv_get_max_sockets(ctx->proto) && + !ctx->stop_worker) { + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process change of connection state */ + ctx->conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(ctx->wsctx); + } + } + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_disconnect() <<<\n"); +} + +void bws_srv_send(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF("bws_srv_send() >>> ctx = %p h = %d\n", ctx, h); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_send() <<< bad websocket handle\n"); + return; + } +#endif + + pthread_mutex_lock(ctx->mutex); + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process send request */ + ctx->conn[h].want_send_data = true; + lws_cancel_service(ctx->wsctx); + } + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_send() <<<\n"); +} + +BSC_WEBSOCKET_RET bws_srv_dispatch_send( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + uint8_t *payload, + size_t payload_size) +{ + int written; + BSC_WEBSOCKET_RET ret; + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF( + "bws_srv_dispatch_send() >>> ctx = %p h = %d payload %p " + "payload_size %d\n", + ctx, h, payload, payload_size); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< bad websocket handle, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } +#endif + + if (h < 0 || h >= bws_srv_get_max_sockets(ctx->proto)) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (!payload || !payload_size) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + pthread_mutex_lock(ctx->mutex); + + if (ctx->stop_worker) { + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_dispatch_send() <<< ret = " + "BSC_WEBSOCKET_INVALID_OPERATION\n"); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + if ((ctx->conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) || + !ctx->conn[h].want_send_data || !ctx->conn[h].can_send_data) { + DEBUG_PRINTF( + "bws_srv_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n"); + pthread_mutex_unlock(ctx->mutex); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + written = + lws_write(ctx->conn[h].ws, payload, payload_size, LWS_WRITE_BINARY); + + DEBUG_PRINTF("bws_srv_dispatch_send() %d bytes is sent\n", written); + + if (written < (int)payload_size) { + DEBUG_PRINTF( + "bws_srv_dispatch_send() websocket connection is broken(closed)\n"); + /* tell worker to process change of connection state */ + ctx->conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(ctx->wsctx); + ret = BSC_WEBSOCKET_INVALID_OPERATION; + } else { + ret = BSC_WEBSOCKET_SUCCESS; + } + + pthread_mutex_unlock(ctx->mutex); + DEBUG_PRINTF("bws_srv_dispatch_send() <<< ret = %d\n", ret); + return ret; +} + +bool bws_srv_get_peer_ip_addr( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + uint8_t *ip_str, + size_t ip_str_len, + uint16_t *port) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + int fd; + struct sockaddr_storage addr; + socklen_t len; + const char *ret = NULL; + + if (!ctx || h < 0 || !ip_str || !ip_str_len || !port || + h >= bws_srv_get_max_sockets(ctx->proto)) { + return false; + } + + pthread_mutex_lock(ctx->mutex); + + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE && + ctx->conn[h].ws != NULL && !ctx->stop_worker) { + len = sizeof(addr); + fd = lws_get_socket_fd(ctx->conn[h].ws); + if (fd != -1) { + getpeername(fd, (struct sockaddr *)&addr, &len); + if (addr.ss_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + *port = ntohs(s->sin_port); + ret = inet_ntop( + AF_INET, &s->sin_addr, (char *)ip_str, ip_str_len); + } else { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + *port = ntohs(s->sin6_port); + ret = inet_ntop( + AF_INET6, &s->sin6_addr, (char *)ip_str, ip_str_len); + } + } + } + + pthread_mutex_unlock(ctx->mutex); + + if (ret) { + return true; + } + + return false; +} diff --git a/ports/win32/bsc-event.c b/ports/win32/bsc-event.c new file mode 100644 index 00000000..6e6891c8 --- /dev/null +++ b/ports/win32/bsc-event.c @@ -0,0 +1,145 @@ +/** + * @file + * @brief Implementation of mutex abstraction used in BACNet secure connect. + * @author Kirill Neznamov + * @date August 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include +#include "bacnet/datalink/bsc/bsc-event.h" + +#define DEBUG_BSC_EVENT 0 + +#if DEBUG_BSC_EVENT == 1 +#define DEBUG_PRINTF printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF(...) +#endif + +struct BSC_Event { + HANDLE mutex; + HANDLE event; + size_t counter; +}; + +BSC_EVENT *bsc_event_init(void) +{ + BSC_EVENT *ret; + + ret = (BSC_EVENT *)malloc(sizeof(BSC_EVENT)); + if (!ret) { + return NULL; + } + + ret->mutex = CreateMutex( + NULL, // default security attributes + FALSE, // initially not owned + NULL); // unnamed mutex + + if (ret->mutex == NULL) { + free(ret); + return NULL; + } + + ret->event = CreateEvent( + NULL, // default security attributes + TRUE, // manual-reset event + FALSE, // initial state is nonsignaled + NULL // unnamed event + ); + + if (ret->event == NULL) { + CloseHandle(ret->mutex); + free(ret); + return NULL; + } + + ret->counter = 0; + + return ret; +} + +void bsc_event_deinit(BSC_EVENT *ev) +{ + CloseHandle(ev->mutex); + CloseHandle(ev->event); + free(ev); +} + +void bsc_event_wait(BSC_EVENT *ev) +{ + DEBUG_PRINTF("bsc_event_wait() >>> ev = %p\n", ev); + WaitForSingleObject(ev->mutex, INFINITE); + DEBUG_PRINTF("bsc_event_wait() counter before %zu\n", ev->counter); + ev->counter++; + ReleaseMutex(ev->mutex); + WaitForSingleObject(ev->event, INFINITE); + WaitForSingleObject(ev->mutex, INFINITE); + DEBUG_PRINTF("bsc_event_wait() before counter %zu\n", ev->counter); + ev->counter--; + DEBUG_PRINTF("bsc_event_wait() counter %zu\n", ev->counter); + if (!ev->counter) { + DEBUG_PRINTF("bsc_event_wait() reset event\n"); + ReleaseMutex(ev->mutex); + ResetEvent(ev->event); + } else { + DEBUG_PRINTF("bsc_event_wait() set event\n"); + ReleaseMutex(ev->mutex); + } + DEBUG_PRINTF("bsc_event_wait() <<< ev = %p\n", ev); +} + +bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout) +{ + bool timedout = false; + DWORD ret; + + DEBUG_PRINTF("bsc_event_timedwait() >>> ev = %p\n", ev); + WaitForSingleObject(ev->mutex, INFINITE); + DEBUG_PRINTF("bsc_event_timedwait() counter before %zu\n", ev->counter); + ev->counter++; + DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter); + ReleaseMutex(ev->mutex); + + ret = WaitForSingleObject(ev->event, ms_timeout); + WaitForSingleObject(ev->mutex, INFINITE); + + DEBUG_PRINTF("bsc_event_timedwait() before counter %zu\n", ev->counter); + ev->counter--; + DEBUG_PRINTF("bsc_event_timedwait() counter %zu\n", ev->counter); + if (!ev->counter) { + ReleaseMutex(ev->mutex); + if (ret == WAIT_OBJECT_0) { + ResetEvent(ev->event); + } + } else { + ReleaseMutex(ev->mutex); + } + DEBUG_PRINTF( + "bsc_event_timedwait() <<< ret = %d\n", + ret == WAIT_OBJECT_0 ? true : false); + return ret == WAIT_OBJECT_0 ? true : false; +} + +void bsc_event_signal(BSC_EVENT *ev) +{ + DEBUG_PRINTF("bsc_event_signal() >>> ev = %p\n", ev); + SetEvent(ev->event); + DEBUG_PRINTF("bsc_event_signal() <<< ev = %p\n", ev); +} + +void bsc_wait(int seconds) +{ + Sleep(seconds * 1000); +} + +void bsc_wait_ms(int mseconds) +{ + Sleep(mseconds); +} diff --git a/ports/win32/websocket-cli.c b/ports/win32/websocket-cli.c new file mode 100644 index 00000000..34968251 --- /dev/null +++ b/ports/win32/websocket-cli.c @@ -0,0 +1,769 @@ +/** + * @file + * @brief Implementation of websocket client interface. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include "bacnet/datalink/bsc/websocket.h" +#include "bacnet/basic/sys/debug.h" +#include "websocket-global.h" + +#define DEBUG_WEBSOCKET_CLIENT 0 + +#if DEBUG_WEBSOCKET_CLIENT == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF(...) +#endif + +#ifndef LWS_PROTOCOL_LIST_TERM +#define LWS_PROTOCOL_LIST_TERM \ + { \ + NULL, NULL, 0, 0, 0, NULL, 0 \ + } +#endif + +#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN + +typedef enum { + BSC_WEBSOCKET_STATE_IDLE = 0, + BSC_WEBSOCKET_STATE_CONNECTING = 1, + BSC_WEBSOCKET_STATE_CONNECTED = 2, + BSC_WEBSOCKET_STATE_DISCONNECTING = 3 +} BSC_WEBSOCKET_STATE; + +typedef struct { + struct lws_context *ctx; + struct lws *ws; + BSC_WEBSOCKET_STATE state; + bool want_send_data; + bool can_send_data; + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + uint8_t *fragment_buffer; + size_t fragment_buffer_size; + size_t fragment_buffer_len; + char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN]; + BACNET_ERROR_CODE err_code; +} BSC_WEBSOCKET_CONNECTION; + +/* Some forward function declarations */ + +static int bws_cli_websocket_event( + struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len); + +static const char *bws_hub_protocol = BSC_WEBSOCKET_HUB_PROTOCOL_STR; +static const char *bws_direct_protocol = BSC_WEBSOCKET_DIRECT_PROTOCOL_STR; + +static HANDLE bws_cli_mutex = NULL; + +/* Websockets protocol defined in BACnet/SC \S AB.7.1. */ + +static struct lws_protocols bws_cli_direct_protocol[] = { + { BSC_WEBSOCKET_DIRECT_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL, + 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static struct lws_protocols bws_cli_hub_protocol[] = { + { BSC_WEBSOCKET_HUB_PROTOCOL_STR, bws_cli_websocket_event, 0, 0, 0, NULL, + 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static lws_retry_bo_t bws_retry; + +static BSC_WEBSOCKET_CONNECTION bws_cli_conn[BSC_CLIENT_WEBSOCKETS_MAX_NUM] = { + 0 +}; + +static BSC_WEBSOCKET_HANDLE bws_cli_alloc_connection(void) +{ + int i; + + for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) { + if (bws_cli_conn[i].state == BSC_WEBSOCKET_STATE_IDLE) { + memset(&bws_cli_conn[i], 0, sizeof(bws_cli_conn[i])); + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void bws_cli_free_connection(BSC_WEBSOCKET_HANDLE h) +{ + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_IDLE) { + if (bws_cli_conn[h].fragment_buffer) { + free(bws_cli_conn[h].fragment_buffer); + bws_cli_conn[h].fragment_buffer = NULL; + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = 0; + } + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_IDLE; + bws_cli_conn[h].ws = NULL; + bws_cli_conn[h].ctx = NULL; + } + } +} + +static BSC_WEBSOCKET_HANDLE bws_cli_find_connnection(struct lws *ws) +{ + int i; + for (i = 0; i < BSC_CLIENT_WEBSOCKETS_MAX_NUM; i++) { + if (bws_cli_conn[i].ws == ws && + bws_cli_conn[i].state != BSC_WEBSOCKET_STATE_IDLE) { + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void bws_set_err_desc(BSC_WEBSOCKET_HANDLE h, char *err_desc) +{ + size_t len; + if (bws_cli_conn[h].err_code == ERROR_CODE_SUCCESS) { + len = strlen(err_desc) >= sizeof(bws_cli_conn[h].err_desc) + ? sizeof(bws_cli_conn[h].err_desc) - 1 + : strlen(err_desc); + + memcpy(bws_cli_conn[h].err_desc, err_desc, len); + bws_cli_conn[h].err_desc[len] = 0; + + if (strstr(err_desc, "tls:")) { + bws_cli_conn[h].err_code = ERROR_CODE_TLS_ERROR; + } else { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + } + } +} + +static void bws_set_disconnect_reason(BSC_WEBSOCKET_HANDLE h, uint16_t err_code) +{ + bws_cli_conn[h].err_desc[0] = 0; + switch (err_code) { + case LWS_CLOSE_STATUS_NORMAL: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER; + break; + } + case LWS_CLOSE_STATUS_GOINGAWAY: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES; + break; + } + case LWS_CLOSE_STATUS_PROTOCOL_ERR: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR; + break; + } + case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_NO_STATUS: + case LWS_CLOSE_STATUS_RESERVED: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_INVALID_PAYLOAD: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT; + break; + } + case LWS_CLOSE_STATUS_POLICY_VIOLATION: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY; + break; + } + case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG; + break; + } + case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING; + break; + } + case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE; + break; + } + default: { + bws_cli_conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + } +} + +static int bws_cli_websocket_event( + struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + BSC_WEBSOCKET_HANDLE h; + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + uint8_t err_code[2]; + + (void)user; + + DEBUG_PRINTF( + "bws_cli_websocket_event() >>> reason = %d, user = %p, in = %p\n", + reason, user, in); + + switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED: { + bsc_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + + DEBUG_PRINTF("bws_cli_websocket_event() connection established\n"); + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED; + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + bsc_mutex_unlock(&bws_cli_mutex); + dispatch_func( + h, BSC_WEBSOCKET_CONNECTED, 0, NULL, NULL, 0, user_param); + break; + } + case LWS_CALLBACK_CLIENT_RECEIVE: { + bsc_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + DEBUG_PRINTF( + "bws_cli_websocket_event() received %d bytes of data\n", len); + if (!lws_frame_is_binary(wsi)) { + /* According AB.7.5.3 BACnet/SC BVLC Message Exchange, + if a received data frame is not binary, + the WebSocket connection shall be closed with a + status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED. + */ + DEBUG_PRINTF( + "bws_cli_websocket_event() got non-binary frame, " + "close connection for socket %d\n", + h); + lws_close_reason( + wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = -1\n"); + return -1; + } + if (bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) { + bsc_mutex_unlock(&bws_cli_mutex); + } else { + if (!bws_cli_conn[h].fragment_buffer) { + DEBUG_PRINTF( + "bws_cli_websocket_event() alloc %d bytes for " + "socket %d\n", + len, h); + bws_cli_conn[h].fragment_buffer = malloc(BSC_RX_BUFFER_LEN); + if (!bws_cli_conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_websocket_event() <<< ret = -1, " + "allocation of %d bytes failed\n", + len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN : len); + return -1; + } + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN; + } + if (bws_cli_conn[h].fragment_buffer_len + len > + bws_cli_conn[h].fragment_buffer_size) { + DEBUG_PRINTF( + "bws_cli_websocket_event() realloc buf of %d bytes" + "for socket %d to %d bytes\n", + bws_cli_conn[h].fragment_buffer_len, h, + bws_cli_conn[h].fragment_buffer_len + len); + bws_cli_conn[h].fragment_buffer = realloc( + bws_cli_conn[h].fragment_buffer, + bws_cli_conn[h].fragment_buffer_len + len); + if (!bws_cli_conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, 0); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_websocket_event() <<< ret = -1, " + "re-allocation of %d bytes failed\n", + bws_cli_conn[h].fragment_buffer_len + len); + return -1; + } + bws_cli_conn[h].fragment_buffer_size = + bws_cli_conn[h].fragment_buffer_len + len; + } + DEBUG_PRINTF( + "bws_cli_websocket_event() got next %d bytes for " + "socket %d\n", + len, h); + memcpy( + &bws_cli_conn[h] + .fragment_buffer[bws_cli_conn[h].fragment_buffer_len], + in, len); + bws_cli_conn[h].fragment_buffer_len += len; + + if (lws_is_final_fragment(wsi)) { + DEBUG_PRINTF( + "bws_cli_websocket_event() last fragment received\n"); + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + bsc_mutex_unlock(&bws_cli_mutex); + dispatch_func( + h, BSC_WEBSOCKET_RECEIVED, 0, NULL, + &bws_cli_conn[h].fragment_buffer[0], + bws_cli_conn[h].fragment_buffer_len, user_param); + bsc_mutex_lock(&bws_cli_mutex); + bws_cli_conn[h].fragment_buffer_len = 0; + bsc_mutex_unlock(&bws_cli_mutex); + } else { + bsc_mutex_unlock(&bws_cli_mutex); + } + } + break; + } + case LWS_CALLBACK_CLIENT_WRITEABLE: { + bsc_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF( + "bws_cli_websocket_event() can not find websocket handle " + "for wsi %p\n", + wsi); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; + } + + DEBUG_PRINTF( + "bws_cli_websocket_event() can write, state = %d\n", + bws_cli_conn[h].state); + DEBUG_PRINTF( + "bws_cli_websocket_event() ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, bws_cli_conn[h].can_send_data); + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED && + bws_cli_conn[h].want_send_data) { + bws_cli_conn[h].can_send_data = true; + dispatch_func = bws_cli_conn[h].dispatch_func; + user_param = bws_cli_conn[h].user_param; + bsc_mutex_unlock(&bws_cli_mutex); + dispatch_func( + h, BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, user_param); + bsc_mutex_lock(&bws_cli_mutex); + bws_cli_conn[h].want_send_data = false; + bws_cli_conn[h].can_send_data = false; + DEBUG_PRINTF( + "bws_cli_websocket_event() was send , ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + bsc_mutex_unlock(&bws_cli_mutex); + /* wakeup worker to process internal state */ + lws_cancel_service(bws_cli_conn[h].ctx); + } else { + bws_cli_conn[h].want_send_data = false; + DEBUG_PRINTF( + "bws_cli_websocket_event() no send, ws = %d, cs = %d\n", + bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + bsc_mutex_unlock(&bws_cli_mutex); + } + break; + } + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + bsc_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) { + err_code[0] = ((uint8_t *)in)[1]; + err_code[1] = ((uint8_t *)in)[0]; + bws_set_disconnect_reason(h, *((uint16_t *)&err_code)); + } + bsc_mutex_unlock(&bws_cli_mutex); + break; + } + case LWS_CALLBACK_CLIENT_CLOSED: + case LWS_CALLBACK_CLOSED: + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: { + bsc_mutex_lock(&bws_cli_mutex); + h = bws_cli_find_connnection(wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE) { + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR && in) { + bws_set_err_desc(h, (char *)in); + } + bsc_mutex_unlock(&bws_cli_mutex); + /* wakeup worker to process pending event */ + lws_cancel_service(bws_cli_conn[h].ctx); + } else { + bsc_mutex_unlock(&bws_cli_mutex); + } + break; + } + default: { + break; + } + } + DEBUG_PRINTF("bws_cli_websocket_event() <<< ret = 0\n"); + return 0; +} + +static DWORD WINAPI bws_cli_worker(LPVOID arg) +{ + BSC_WEBSOCKET_CONNECTION *conn = (BSC_WEBSOCKET_CONNECTION *)arg; + BSC_WEBSOCKET_HANDLE h = (BSC_WEBSOCKET_HANDLE)(conn - &bws_cli_conn[0]); + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func; + void *user_param; + char err_desc[BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN]; + uint16_t err_code; + + srand((unsigned)GetCurrentThreadId()); + + while (1) { + DEBUG_PRINTF("bws_cli_worker() try mutex h = %d\n", h); + bsc_mutex_lock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() mutex locked h = %d\n", h); + if (conn->state == BSC_WEBSOCKET_STATE_CONNECTED) { + if (conn->want_send_data) { + DEBUG_PRINTF( + "bws_cli_worker() process request for sending data\n"); + lws_callback_on_writable(conn->ws); + } + } else if (conn->state == BSC_WEBSOCKET_STATE_DISCONNECTING) { + DEBUG_PRINTF("bws_cli_worker() process disconnecting event\n"); + DEBUG_PRINTF("bws_cli_worker() destroy ctx %p\n", conn->ctx); + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result + lws_context_destroy() is not thread safe. More over, on different + platforms the function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + bsc_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + lws_context_destroy(conn->ctx); + bsc_websocket_global_unlock(); + bsc_mutex_lock(&bws_cli_mutex); + dispatch_func = conn->dispatch_func; + user_param = conn->user_param; + err_code = conn->err_code; + if (err_code != ERROR_CODE_SUCCESS) { + memcpy(err_desc, conn->err_desc, sizeof(err_desc)); + } + bws_cli_free_connection(h); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() unlock mutex\n"); + dispatch_func( + h, BSC_WEBSOCKET_DISCONNECTED, err_code, + err_code != ERROR_CODE_SUCCESS ? err_desc : NULL, NULL, 0, + user_param); + return 0; + } + DEBUG_PRINTF("bws_cli_worker() unlock mutex\n"); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_worker() going to block on lws_service() call\n"); + lws_service(conn->ctx, 0); + } + + return 0; +} + +BSC_WEBSOCKET_RET bws_cli_connect( + BSC_WEBSOCKET_PROTOCOL proto, + char *url, + uint8_t *ca_cert, + size_t ca_cert_size, + uint8_t *cert, + size_t cert_size, + uint8_t *key, + size_t key_size, + size_t timeout_s, + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func, + void *dispatch_func_user_param, + BSC_WEBSOCKET_HANDLE *out_handle) +{ + struct lws_context_creation_info info; + char tmp_url[BSC_WSURL_MAX_LEN]; + const char *prot = NULL, *addr = NULL, *path = NULL; + int port = -1; + BSC_WEBSOCKET_HANDLE h; + struct lws_client_connect_info cinfo; + HANDLE thread; + size_t len; + + DEBUG_PRINTF("bws_cli_connect() >>> proto = %d, url = %s\n", proto, url); + + if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size || + !url || !out_handle || !timeout_s || !dispatch_func) { + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + *out_handle = BSC_WEBSOCKET_INVALID_HANDLE; + + if (proto != BSC_WEBSOCKET_HUB_PROTOCOL && + proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) { + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + memset(&info, 0, sizeof(info)); + memset(&cinfo, 0, sizeof(cinfo)); + + len = strlen(url) >= sizeof(tmp_url) ? sizeof(tmp_url) - 1 : strlen(url); + memcpy(tmp_url, url, len); + tmp_url[len] = 0; + + bsc_websocket_init_log(); + bsc_mutex_lock(&bws_cli_mutex); + + if (lws_parse_uri(tmp_url, &prot, &addr, &port, &path) != 0 || port == -1 || + !prot || !addr || !path) { + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (strcmp(prot, "wss") != 0) { + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + h = bws_cli_alloc_connection(); + + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_CONNECTING; + bws_cli_conn[h].fragment_buffer = NULL; + bws_cli_conn[h].fragment_buffer_len = 0; + bws_cli_conn[h].fragment_buffer_size = 0; + bws_cli_conn[h].dispatch_func = dispatch_func; + bws_cli_conn[h].user_param = dispatch_func_user_param; + info.port = CONTEXT_PORT_NO_LISTEN; + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + info.protocols = bws_cli_hub_protocol; + } else { + info.protocols = bws_cli_direct_protocol; + } + info.gid = -1; + info.uid = -1; + info.client_ssl_cert_mem = cert; + info.client_ssl_cert_mem_len = (unsigned int)cert_size; + info.client_ssl_ca_mem = ca_cert; + info.client_ssl_ca_mem_len = (unsigned int)ca_cert_size; + info.client_ssl_key_mem = key; + info.client_ssl_key_mem_len = (unsigned int)key_size; + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND; + info.timeout_secs = (unsigned int)timeout_s; + info.connect_timeout_secs = (unsigned int)timeout_s; + + /* TRICKY: check comments related to lws_context_destroy() call */ + + bsc_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + bws_cli_conn[h].ctx = lws_create_context(&info); + bsc_websocket_global_unlock(); + bsc_mutex_lock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_connect() created ctx %p\n", bws_cli_conn[h].ctx); + + if (!bws_cli_conn[h].ctx) { + bws_cli_free_connection(h); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + bws_cli_conn[h].ws = NULL; + cinfo.context = bws_cli_conn[h].ctx; + cinfo.address = addr; + cinfo.origin = cinfo.address; + cinfo.host = cinfo.address; + cinfo.port = port; + cinfo.path = path; + cinfo.pwsi = &bws_cli_conn[h].ws; + cinfo.alpn = "h2;http/1.1"; + bws_retry.secs_since_valid_ping = 3; + bws_retry.secs_since_valid_hangup = 10; + cinfo.retry_and_idle_policy = &bws_retry; + cinfo.ssl_connection = LCCSCF_USE_SSL | + LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_SELFSIGNED; + + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + cinfo.protocol = bws_hub_protocol; + } else { + cinfo.protocol = bws_direct_protocol; + } + + bws_cli_conn[h].err_code = ERROR_CODE_SUCCESS; + *out_handle = h; + bsc_mutex_unlock(&bws_cli_mutex); + bsc_websocket_global_lock(); + lws_client_connect_via_info(&cinfo); + bsc_websocket_global_unlock(); + + thread = CreateThread(NULL, 0, &bws_cli_worker, &bws_cli_conn[h], 0, NULL); + + if (thread == NULL) { + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result lws_context_destroy() + is not thread safe. More over, on different platforms the + function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_cli_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + bsc_websocket_global_lock(); + lws_context_destroy(bws_cli_conn[h].ctx); + bsc_websocket_global_unlock(); + bsc_mutex_lock(&bws_cli_mutex); + bws_cli_free_connection(h); + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF( + "bws_cli_connect() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + DEBUG_PRINTF("bws_cli_connect() <<< ret = %d\n", BSC_WEBSOCKET_SUCCESS); + return BSC_WEBSOCKET_SUCCESS; +} + +void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_cli_disconnect() >>> h = %d\n", h); + + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + bsc_mutex_lock(&bws_cli_mutex); + + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTING || + bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process change of connection state */ + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(bws_cli_conn[h].ctx); + } + + bsc_mutex_unlock(&bws_cli_mutex); + } + + DEBUG_PRINTF("bws_cli_disconnect() <<<\n"); +} + +void bws_cli_send(BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_cli_send() >>> h = %d\n", h); + + if (h >= 0 && h < BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + bsc_mutex_lock(&bws_cli_mutex); + + if (bws_cli_conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process send request */ + bws_cli_conn[h].want_send_data = true; + DEBUG_PRINTF("bws_cli_send() cs = 1\n"); + lws_cancel_service(bws_cli_conn[h].ctx); + } + + bsc_mutex_unlock(&bws_cli_mutex); + } + + DEBUG_PRINTF("bws_cli_send() <<<\n"); +} + +BSC_WEBSOCKET_RET bws_cli_dispatch_send( + BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size) +{ + int written; + BSC_WEBSOCKET_RET ret; + + DEBUG_PRINTF( + "bws_cli_dispatch_send() >>> h = %d, payload = %p, payload_size = %d\n", + h, payload, payload_size); + + if (h < 0 || h >= BSC_CLIENT_WEBSOCKETS_MAX_NUM) { + DEBUG_PRINTF( + "bws_cli_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + bsc_mutex_lock(&bws_cli_mutex); + + if ((bws_cli_conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) || + !bws_cli_conn[h].want_send_data || !bws_cli_conn[h].can_send_data) { + DEBUG_PRINTF( + "bws_cli_dispatch_send() state = %d, ws = %d, cs = %d\n", + bws_cli_conn[h].state, bws_cli_conn[h].want_send_data, + bws_cli_conn[h].can_send_data); + DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = " + "BSC_WEBSOCKET_INVALID_OPERATION\n"); + bsc_mutex_unlock(&bws_cli_mutex); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + written = + lws_write(bws_cli_conn[h].ws, payload, payload_size, LWS_WRITE_BINARY); + + DEBUG_PRINTF("bws_cli_dispatch_send() %d bytes is sent\n", written); + + if (written < (int)payload_size) { + DEBUG_PRINTF( + "bws_cli_dispatch_send() websocket connection is broken(closed)\n"); + /* tell worker to process change of connection state */ + bws_cli_conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(bws_cli_conn[h].ctx); + ret = BSC_WEBSOCKET_INVALID_OPERATION; + } else { + ret = BSC_WEBSOCKET_SUCCESS; + } + + bsc_mutex_unlock(&bws_cli_mutex); + DEBUG_PRINTF("bws_cli_dispatch_send() <<< ret = %d\n", ret); + return ret; +} diff --git a/ports/win32/websocket-global.c b/ports/win32/websocket-global.c new file mode 100644 index 00000000..59efa94f --- /dev/null +++ b/ports/win32/websocket-global.c @@ -0,0 +1,80 @@ +/** + * @file + * @brief Implementation of global websocket functions. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include "websocket-global.h" + +static HANDLE websocket_mutex = NULL; +static HANDLE websocket_dispatch_mutex = NULL; + +static void bsc_init_mutex(volatile HANDLE *m) +{ + HANDLE p; + if (*m == NULL) { + p = CreateMutex(NULL, FALSE, NULL); + if (InterlockedCompareExchangePointer((PVOID *)m, (PVOID)p, NULL) != + NULL) { + CloseHandle(p); + } + } +} + +void bsc_mutex_lock(volatile HANDLE *m) +{ + bsc_init_mutex(m); + WaitForSingleObject(*m, INFINITE); +} + +void bsc_mutex_unlock(volatile HANDLE *m) +{ + ReleaseMutex(*m); +} + +void bsc_websocket_global_lock(void) +{ + bsc_mutex_lock(&websocket_mutex); +} + +void bsc_websocket_global_unlock(void) +{ + bsc_mutex_unlock(&websocket_mutex); +} + +void bws_dispatch_lock(void) +{ + bsc_mutex_lock(&websocket_dispatch_mutex); +} + +void bws_dispatch_unlock(void) +{ + bsc_mutex_unlock(&websocket_dispatch_mutex); +} + +static BOOL bsc_websocket_log_initialized = FALSE; + +void bsc_websocket_init_log(void) +{ + bsc_websocket_global_lock(); + if (!bsc_websocket_log_initialized) { + bsc_websocket_log_initialized = TRUE; +#if DEBUG_LIBWEBSOCKETS_ENABLED == 1 + printf("LWS_MAX_SMP = %d", LWS_MAX_SMP); + lws_set_log_level( + LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | + LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY | + LLL_USER | LLL_THREAD, + NULL); +#else + lws_set_log_level(0, NULL); +#endif + } + bsc_websocket_global_unlock(); +} diff --git a/ports/win32/websocket-global.h b/ports/win32/websocket-global.h new file mode 100644 index 00000000..fbd3603d --- /dev/null +++ b/ports/win32/websocket-global.h @@ -0,0 +1,18 @@ +/** + * @file + * @brief Global websocket functions. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef __BSC_WEBSOCKET_MUTEX_INCLUDED__ +#define __BSC_WEBSOCKET_MUTEX_INCLUDED__ + +#include + +void bsc_mutex_lock(volatile HANDLE *m); +void bsc_mutex_unlock(volatile HANDLE *m); +void bsc_websocket_global_lock(void); +void bsc_websocket_global_unlock(void); +void bsc_websocket_init_log(void); +#endif diff --git a/ports/win32/websocket-srv.c b/ports/win32/websocket-srv.c new file mode 100644 index 00000000..04c60c4b --- /dev/null +++ b/ports/win32/websocket-srv.c @@ -0,0 +1,1004 @@ +/** + * @file + * @brief Implementation of server websocket interface. + * @author Kirill Neznamov + * @date June 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include "bacnet/datalink/bsc/websocket.h" +#include "bacnet/basic/sys/debug.h" +#include "websocket-global.h" + +#define DEBUG_WEBSOCKET_SERVER 0 + +#if DEBUG_WEBSOCKET_SERVER == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF(...) +#endif + +#define BSC_RX_BUFFER_LEN BSC_WEBSOCKET_RX_BUFFER_LEN + +#ifndef LWS_PROTOCOL_LIST_TERM +#define LWS_PROTOCOL_LIST_TERM \ + { \ + NULL, NULL, 0, 0, 0, NULL, 0 \ + } +#endif + +typedef enum { + BSC_WEBSOCKET_STATE_IDLE = 0, + BSC_WEBSOCKET_STATE_CONNECTED = 1, + BSC_WEBSOCKET_STATE_DISCONNECTING = 2 +} BSC_WEBSOCKET_STATE; + +/* Some forward function declarations */ + +static int bws_srv_websocket_event( + struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len); + +typedef struct { + struct lws_context *ctx; + struct lws *ws; + BSC_WEBSOCKET_STATE state; + bool want_send_data; + bool can_send_data; + uint8_t *fragment_buffer; + size_t fragment_buffer_size; + size_t fragment_buffer_len; + BACNET_ERROR_CODE err_code; +} BSC_WEBSOCKET_CONNECTION; + +#if BSC_CONF_WEBSOCKET_SERVERS_NUM < 1 +#error "BSC_CONF_WEBSOCKET_SERVERS_NUM must be >= 1" +#endif + +static HANDLE bws_global_mutex = NULL; +static HANDLE bws_srv_direct_mutex[BSC_CONF_WEBSOCKET_SERVERS_NUM]; +static HANDLE bws_srv_hub_mutex[BSC_CONF_WEBSOCKET_SERVERS_NUM]; + +#if BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM > 0 +static BSC_WEBSOCKET_CONNECTION + bws_hub_conn[BSC_CONF_WEBSOCKET_SERVERS_NUM] + [BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM] = { 0 }; +#else +static BSC_WEBSOCKET_CONNECTION bws_hub_conn[1][1] = { 0 }; +#endif + +#if BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM > 0 +static BSC_WEBSOCKET_CONNECTION + bws_direct_conn[BSC_CONF_WEBSOCKET_SERVERS_NUM] + [BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM] = { 0 }; +#else +static BSC_WEBSOCKET_CONNECTION bws_direct_conn[1][1] = { 0 }; +#endif + +typedef struct BACNetWebsocketServerContext { + bool used; + struct lws_context *wsctx; + BSC_WEBSOCKET_PROTOCOL proto; + BSC_WEBSOCKET_CONNECTION *conn; + HANDLE mutex; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + bool stop_worker; +} BSC_WEBSOCKET_CONTEXT; + +static BSC_WEBSOCKET_CONTEXT bws_hub_ctx[BSC_CONF_WEBSOCKET_SERVERS_NUM] = { + 0 +}; +static BSC_WEBSOCKET_CONTEXT bws_direct_ctx[BSC_CONF_WEBSOCKET_SERVERS_NUM] = { + 0 +}; + +static bool bws_mutex_init(HANDLE *mutex) +{ + *mutex = CreateMutex(NULL, FALSE, NULL); + if (!(*mutex)) { + return false; + } + return true; +} + +static BSC_WEBSOCKET_CONTEXT *bws_alloc_server_ctx(BSC_WEBSOCKET_PROTOCOL proto) +{ + int i; + BSC_WEBSOCKET_CONTEXT *ctx = (proto == BSC_WEBSOCKET_HUB_PROTOCOL) + ? &bws_hub_ctx[0] + : &bws_direct_ctx[0]; + + bsc_mutex_lock(&bws_global_mutex); + DEBUG_PRINTF("bws_alloc_server_ctx() >>> proto = %d\n", proto); + + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (!ctx[i].used) { + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + ctx[i].mutex = &bws_srv_hub_mutex[i]; + ctx[i].conn = &bws_hub_conn[i][0]; + } else { + ctx[i].mutex = &bws_srv_direct_mutex[i]; + ctx[i].conn = &bws_direct_conn[i][0]; + } + if (!bws_mutex_init(&ctx[i].mutex)) { + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + bsc_mutex_unlock(&bws_global_mutex); + return NULL; + } + ctx[i].used = true; + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + bsc_mutex_unlock(&bws_global_mutex); + return &ctx[i]; + } + } + DEBUG_PRINTF("bws_alloc_server_ctx() <<< ret = %p\n", &ctx[i]); + bsc_mutex_unlock(&bws_global_mutex); + return NULL; +} + +static void bws_set_disconnect_reason( + BSC_WEBSOCKET_CONTEXT *ctx, BSC_WEBSOCKET_HANDLE h, uint16_t err_code) +{ + switch (err_code) { + case LWS_CLOSE_STATUS_NORMAL: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER; + break; + } + case LWS_CLOSE_STATUS_GOINGAWAY: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ENDPOINT_LEAVES; + break; + } + case LWS_CLOSE_STATUS_PROTOCOL_ERR: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_PROTOCOL_ERROR; + break; + } + case LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_NO_STATUS: + case LWS_CLOSE_STATUS_RESERVED: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + case LWS_CLOSE_STATUS_ABNORMAL_CLOSE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_NOT_ACCEPTED; + break; + } + case LWS_CLOSE_STATUS_INVALID_PAYLOAD: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_INCONSISTENT; + break; + } + case LWS_CLOSE_STATUS_POLICY_VIOLATION: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_DATA_AGAINST_POLICY; + break; + } + case LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_FRAME_TOO_LONG; + break; + } + case LWS_CLOSE_STATUS_EXTENSION_REQUIRED: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_EXTENSION_MISSING; + break; + } + case LWS_CLOSE_STATUS_UNEXPECTED_CONDITION: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_REQUEST_UNAVAILABLE; + break; + } + default: { + ctx->conn[h].err_code = ERROR_CODE_WEBSOCKET_ERROR; + break; + } + } +} + +static void bws_free_server_ctx(BSC_WEBSOCKET_CONTEXT *ctx) +{ + bsc_mutex_lock(&bws_global_mutex); + DEBUG_PRINTF("bws_free_server_ctx() >>> ctx = %p\n", ctx); + ctx->used = false; + ctx->wsctx = NULL; + ctx->conn = NULL; + CloseHandle(ctx->mutex); + ctx->mutex = NULL; + ctx->dispatch_func = NULL; + ctx->user_param = NULL; + DEBUG_PRINTF("bws_free_server_ctx() <<< \n"); + bsc_mutex_unlock(&bws_global_mutex); +} + +#if DEBUG_ENABLED == 1 +static bool bws_validate_ctx_pointer(BSC_WEBSOCKET_CONTEXT *ctx) +{ + bool is_validated = false; + int i; + + bsc_mutex_lock(&bws_global_mutex); + + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (ctx == &bws_hub_ctx[i]) { + is_validated = true; + break; + } + } + + if (!is_validated) { + for (i = 0; i < BSC_CONF_WEBSOCKET_SERVERS_NUM; i++) { + if (ctx == &bws_direct_ctx[i]) { + is_validated = true; + break; + } + } + } + + bsc_mutex_unlock(&bws_global_mutex); + return is_validated; +} +#endif + +static int bws_srv_get_max_sockets(BSC_WEBSOCKET_PROTOCOL proto) +{ + int max = 0; + if (proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + max = BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM; + } else if (proto == BSC_WEBSOCKET_DIRECT_PROTOCOL) { + max = BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM; + } + return max; +} + +static BSC_WEBSOCKET_HANDLE bws_srv_alloc_connection(BSC_WEBSOCKET_CONTEXT *ctx) +{ + int i; + + DEBUG_PRINTF("bws_srv_alloc_connection() >>> ctx = %p\n", ctx); + + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + if (ctx->conn[i].state == BSC_WEBSOCKET_STATE_IDLE) { + memset(&ctx->conn[i], 0, sizeof(ctx->conn[i])); + DEBUG_PRINTF("bws_srv_alloc_connection() <<< ret = %d\n", i); + return i; + } + } + + DEBUG_PRINTF("bws_srv_alloc_connection() <<< ret = " + "BSC_WEBSOCKET_INVALID_HANDLE\n"); + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static void +bws_srv_free_connection(BSC_WEBSOCKET_CONTEXT *ctx, BSC_WEBSOCKET_HANDLE h) +{ + DEBUG_PRINTF("bws_srv_free_connection() >>> ctx = %p, h = %d\n", ctx, h); + + if (h >= 0 && h < bws_srv_get_max_sockets(ctx->proto)) { + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE) { + if (ctx->conn[h].fragment_buffer) { + free(ctx->conn[h].fragment_buffer); + ctx->conn[h].fragment_buffer = NULL; + ctx->conn[h].fragment_buffer_len = 0; + ctx->conn[h].fragment_buffer_size = 0; + } + ctx->conn[h].state = BSC_WEBSOCKET_STATE_IDLE; + ctx->conn[h].ws = NULL; + } + } + DEBUG_PRINTF("bws_srv_free_connection() <<<\n"); +} + +static BSC_WEBSOCKET_HANDLE +bws_find_connnection(BSC_WEBSOCKET_CONTEXT *ctx, struct lws *ws) +{ + int i; + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + if (ctx->conn[i].ws == ws && + ctx->conn[i].state != BSC_WEBSOCKET_STATE_IDLE) { + return i; + } + } + return BSC_WEBSOCKET_INVALID_HANDLE; +} + +static int bws_srv_websocket_event( + struct lws *wsi, + enum lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + BSC_WEBSOCKET_HANDLE h; + BSC_WEBSOCKET_CONTEXT *ctx = + (BSC_WEBSOCKET_CONTEXT *)lws_context_user(lws_get_context(wsi)); + int ret = 0; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + bool stop_worker; + uint8_t err_code[2]; + uint16_t err; + (void)user; + + DEBUG_PRINTF( + "bws_srv_websocket_event() >>> ctx = %p, user_param = %p, " + "proto = %d, wsi = %p, " + "reason = %d, in = %p, len = %d\n", + ctx, ctx->user_param, ctx->proto, wsi, reason, in, len); + + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: { + bsc_mutex_lock(&ctx->mutex); + DEBUG_PRINTF("bws_srv_websocket_event() established connection\n"); + h = bws_srv_alloc_connection(ctx); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + DEBUG_PRINTF("bws_srv_websocket_event() no free sockets, " + "dropping incoming connection\n"); + bsc_mutex_unlock(&ctx->mutex); + return -1; + } + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d set state of" + " socket %d to BACNET_WEBSOCKET_STATE_CONNECTING\n", + ctx, ctx->proto, h); + ctx->conn[h].ws = wsi; + ctx->conn[h].state = BSC_WEBSOCKET_STATE_CONNECTED; + ctx->conn[h].err_code = ERROR_CODE_SUCCESS; + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + bsc_mutex_unlock(&ctx->mutex); + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, h, BSC_WEBSOCKET_CONNECTED, 0, + NULL, NULL, 0, user_param); + /* wakeup worker to process pending event */ + lws_cancel_service(ctx->wsctx); + break; + } + case LWS_CALLBACK_CLOSED: { + DEBUG_PRINTF("bws_srv_websocket_event() closed connection\n"); + bsc_mutex_lock(&ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + bsc_mutex_unlock(&ctx->mutex); + } else { + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p user_param = %p " + "proto %d state of " + "socket %d is %d\n", + ctx, ctx->user_param, ctx->proto, h, ctx->conn[h].state); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + stop_worker = ctx->stop_worker; + err = ctx->conn[h].err_code; + bws_srv_free_connection(ctx, h); + bsc_mutex_unlock(&ctx->mutex); + if (!stop_worker) { + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_DISCONNECTED, err, NULL, NULL, 0, + user_param); + } + } + break; + } + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + bsc_mutex_lock(&ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h != BSC_WEBSOCKET_INVALID_HANDLE && len >= 2) { + err_code[0] = ((uint8_t *)in)[1]; + err_code[1] = ((uint8_t *)in)[0]; + bws_set_disconnect_reason(ctx, h, *((uint16_t *)&err_code)); + } + bsc_mutex_unlock(&ctx->mutex); + break; + } + case LWS_CALLBACK_RECEIVE: { + bsc_mutex_lock(&ctx->mutex); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + bsc_mutex_unlock(&ctx->mutex); + } else { + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d " + "received %d bytes of " + "data for websocket %d\n", + ctx, ctx->proto, len, h); + if (!lws_frame_is_binary(wsi)) { + /* According AB.7.5.3 BACnet/SC BVLC Message Exchange, + if a received data frame is not binary, + the WebSocket connection shall be closed with a + status code of 1003 -WEBSOCKET_DATA_NOT_ACCEPTED. + */ + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d got " + "non-binary frame, " + "close websocket %d\n", + ctx, ctx->proto, h); + lws_close_reason( + wsi, LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE, NULL, 0); + bsc_mutex_unlock(&ctx->mutex); + DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = -1\n"); + return -1; + } + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) { + bsc_mutex_unlock(&ctx->mutex); + } else { + if (!ctx->conn[h].fragment_buffer) { + ctx->conn[h].fragment_buffer = + malloc(BSC_RX_BUFFER_LEN); + if (!ctx->conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, + 0); + bsc_mutex_unlock(&ctx->mutex); + DEBUG_PRINTF( + "bws_srv_websocket_event() <<< ret = " + "-1, allocation of %d bytes failed\n", + len <= BSC_RX_BUFFER_LEN ? BSC_RX_BUFFER_LEN + : len); + return -1; + } + ctx->conn[h].fragment_buffer_len = 0; + ctx->conn[h].fragment_buffer_size = BSC_RX_BUFFER_LEN; + } + if (ctx->conn[h].fragment_buffer_len + len > + ctx->conn[h].fragment_buffer_size) { + DEBUG_PRINTF( + "bws_srv_websocket_event() realloc buf of %d bytes" + "for socket %d to %d bytes\n", + ctx->conn[h].fragment_buffer_len, h, + ctx->conn[h].fragment_buffer_len + len); + + ctx->conn[h].fragment_buffer = realloc( + ctx->conn[h].fragment_buffer, + ctx->conn[h].fragment_buffer_len + len); + if (!ctx->conn[h].fragment_buffer) { + lws_close_reason( + wsi, LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE, NULL, + 0); + bsc_mutex_unlock(&ctx->mutex); + DEBUG_PRINTF( + "bws_srv_websocket_event() <<< ret = -1, " + "re-allocation of %d bytes failed\n", + ctx->conn[h].fragment_buffer_len + len); + return -1; + } + ctx->conn[h].fragment_buffer_size = + ctx->conn[h].fragment_buffer_len + len; + } + DEBUG_PRINTF( + "bws_srv_websocket_event() got next %d bytes for " + "socket %d total_len %d\n", + len, h, ctx->conn[h].fragment_buffer_len); + memcpy( + &ctx->conn[h] + .fragment_buffer[ctx->conn[h].fragment_buffer_len], + in, len); + ctx->conn[h].fragment_buffer_len += len; + if (lws_is_final_fragment(wsi) && !ctx->stop_worker) { + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + bsc_mutex_unlock(&ctx->mutex); + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_RECEIVED, 0, NULL, + &ctx->conn[h].fragment_buffer[0], + ctx->conn[h].fragment_buffer_len, user_param); + bsc_mutex_lock(&ctx->mutex); + ctx->conn[h].fragment_buffer_len = 0; + bsc_mutex_unlock(&ctx->mutex); + } else { + bsc_mutex_unlock(&ctx->mutex); + } + } + } + break; + } + case LWS_CALLBACK_SERVER_WRITEABLE: { + bsc_mutex_lock(&ctx->mutex); + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d can write\n", ctx, + ctx->proto); + h = bws_find_connnection(ctx, wsi); + if (h == BSC_WEBSOCKET_INVALID_HANDLE) { + bsc_mutex_unlock(&ctx->mutex); + } else { + DEBUG_PRINTF( + "bws_srv_websocket_event() ctx %p proto %d socket " + "%d state = %d\n", + ctx, ctx->proto, h, ctx->conn[h].state); + + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_DISCONNECTING) { + /* if -1 returned from this callback libwebsocket closes ++ websocket related to wsi */ + ret = -1; + } + + if ((ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE) && + !ctx->stop_worker && ctx->conn[h].want_send_data) { + ctx->conn[h].can_send_data = true; + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + stop_worker = ctx->stop_worker; + bsc_mutex_unlock(&ctx->mutex); + if (!stop_worker) { + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, h, + BSC_WEBSOCKET_SENDABLE, 0, NULL, NULL, 0, + user_param); + } + bsc_mutex_lock(&ctx->mutex); + ctx->conn[h].want_send_data = false; + ctx->conn[h].can_send_data = false; + bsc_mutex_unlock(&ctx->mutex); + /* wakeup worker to process internal state */ + lws_cancel_service(ctx->wsctx); + } else { + ctx->conn[h].want_send_data = false; + bsc_mutex_unlock(&ctx->mutex); + } + } + break; + } + default: { + break; + } + } + DEBUG_PRINTF("bws_srv_websocket_event() <<< ret = %d\n", ret); + return ret; +} + +static DWORD WINAPI bws_srv_worker(LPVOID arg) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)arg; + int i; + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func; + void *user_param; + + DEBUG_PRINTF( + "bws_srv_worker() started for ctx %p proto %d user_param %p\n", ctx, + ctx->proto, ctx->user_param); + + srand((unsigned)GetCurrentThreadId()); + + bsc_mutex_lock(&ctx->mutex); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + bsc_mutex_unlock(&ctx->mutex); + + dispatch_func( + (BSC_WEBSOCKET_SRV_HANDLE)ctx, 0, BSC_WEBSOCKET_SERVER_STARTED, 0, NULL, + NULL, 0, user_param); + + while (1) { + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d blocked user_param %p\n", ctx, + ctx->proto, ctx->user_param); + bsc_mutex_lock(&ctx->mutex); + + if (ctx->stop_worker) { + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param %p proto %d going " + "to stop\n", + ctx, ctx->user_param, ctx->proto); + DEBUG_PRINTF( + "bws_srv_worker() destroy wsctx %p, ctx = %p, " + "user_param = %p\n", + ctx->wsctx, ctx, ctx->user_param); + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result + lws_context_destroy() is not thread safe.More over, on different + platforms the function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_srv_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + bsc_mutex_unlock(&ctx->mutex); + bsc_websocket_global_lock(); + lws_context_destroy(ctx->wsctx); + bsc_websocket_global_unlock(); + bsc_mutex_lock(&ctx->mutex); + ctx->wsctx = NULL; + DEBUG_PRINTF("bws_srv_worker() set wsctx %p\n", ctx->wsctx); + ctx->stop_worker = false; + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d emitting stop event\n", ctx, + ctx->proto); + dispatch_func = ctx->dispatch_func; + user_param = ctx->user_param; + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param = %p\n", ctx, user_param); + bsc_mutex_unlock(&ctx->mutex); + dispatch_func( + ctx, 0, BSC_WEBSOCKET_SERVER_STOPPED, 0, NULL, NULL, 0, + user_param); + bws_free_server_ctx(ctx); + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d stopped\n", ctx, ctx->proto); + return 0; + } + + for (i = 0; i < bws_srv_get_max_sockets(ctx->proto); i++) { + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param %p proto %d " + "socket %d(%p) state = %d\n", + ctx, ctx->user_param, ctx->proto, i, &ctx->conn[i], + ctx->conn[i].state); + if (ctx->conn[i].state == BSC_WEBSOCKET_STATE_CONNECTED) { + if (ctx->conn[i].want_send_data) { + DEBUG_PRINTF( + "bws_srv_worker() process request for sending " + "data on socket %d\n", + i); + lws_callback_on_writable(ctx->conn[i].ws); + } + } else if ( + ctx->conn[i].state == BSC_WEBSOCKET_STATE_DISCONNECTING) { + DEBUG_PRINTF( + "bws_srv_worker() process disconnecting event on " + "socket %d\n", + i); + lws_callback_on_writable(ctx->conn[i].ws); + } + } + + DEBUG_PRINTF( + "bws_srv_worker() ctx %p proto %d unblocked\n", ctx, ctx->proto); + bsc_mutex_unlock(&ctx->mutex); + + DEBUG_PRINTF( + "bws_srv_worker() ctx %p user_param %p proto %d going to block on " + "lws_service() call\n", + ctx, ctx->user_param, ctx->proto); + lws_service(ctx->wsctx, 0); + } + + return 0; +} + +BSC_WEBSOCKET_RET bws_srv_start( + BSC_WEBSOCKET_PROTOCOL proto, + int port, + char *iface, + uint8_t *ca_cert, + size_t ca_cert_size, + uint8_t *cert, + size_t cert_size, + uint8_t *key, + size_t key_size, + size_t timeout_s, + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func, + void *dispatch_func_user_param, + BSC_WEBSOCKET_SRV_HANDLE *sh) +{ + HANDLE thread; + struct lws_context_creation_info info = { 0 }; + BSC_WEBSOCKET_CONTEXT *ctx; + + struct lws_protocols protos[] = { { (proto == BSC_WEBSOCKET_HUB_PROTOCOL) + ? BSC_WEBSOCKET_HUB_PROTOCOL_STR + : BSC_WEBSOCKET_DIRECT_PROTOCOL_STR, + bws_srv_websocket_event, 0, 0, 0, NULL, + 0 }, + LWS_PROTOCOL_LIST_TERM }; + DEBUG_PRINTF( + "bws_srv_start() >>> proto = %d port = %d " + "dispatch_func_user_param = %p\n", + proto, port, dispatch_func_user_param); + + if (proto != BSC_WEBSOCKET_HUB_PROTOCOL && + proto != BSC_WEBSOCKET_DIRECT_PROTOCOL) { + DEBUG_PRINTF("bws_srv_start() <<< bad protocol, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (!ca_cert || !ca_cert_size || !cert || !cert_size || !key || !key_size || + !dispatch_func || !timeout_s || !sh) { + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (port < 0 || port > 65535) { + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + ctx = bws_alloc_server_ctx(proto); + + if (!ctx) { + DEBUG_PRINTF( + "bws_srv_start() <<< maximum amount of servers for " + "server proto %d is to small, ret = BSC_WEBSOCKET_NO_RESOURCES\n", + proto); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + bsc_websocket_init_log(); + + bsc_mutex_lock(&ctx->mutex); + info.port = port; + info.iface = iface; + info.protocols = protos; + info.gid = -1; + info.uid = -1; + info.server_ssl_cert_mem = cert; + info.server_ssl_cert_mem_len = (unsigned int)cert_size; + info.server_ssl_ca_mem = ca_cert; + info.server_ssl_ca_mem_len = (unsigned int)ca_cert_size; + info.server_ssl_private_key_mem = key; + info.server_ssl_private_key_mem_len = (unsigned int)key_size; + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.options |= LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND; + info.timeout_secs = (unsigned int)timeout_s; + info.connect_timeout_secs = (unsigned int)timeout_s; + info.user = ctx; + + /* TRICKY: check comments related to lws_context_destroy() call */ + + bsc_mutex_unlock(&ctx->mutex); + bsc_websocket_global_lock(); + ctx->wsctx = lws_create_context(&info); + bsc_websocket_global_unlock(); + bsc_mutex_lock(&ctx->mutex); + + if (!ctx->wsctx) { + bsc_mutex_unlock(&ctx->mutex); + bws_free_server_ctx(ctx); + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + ctx->dispatch_func = dispatch_func; + ctx->user_param = dispatch_func_user_param; + ctx->proto = proto; + + thread = CreateThread(NULL, 0, &bws_srv_worker, ctx, 0, NULL); + + if (thread == NULL) { + /* TRICKY: Libwebsockets API is not designed to be used from + multipe service threads, as a result lws_context_destroy() + is not thread safe. More over, on different platforms the + function behaves in different ways. Call of + lws_context_destroy() leads to several calls of + bws_srv_websocket_event() callback (LWS_CALLBACK_CLOSED, + etc..). But under some OS (MacOSx) that callback is + called from context of the bws_cli_worker() thread and + under some other OS (linux) the callback is called from + internal libwebsockets lib thread. That's why + bws_cli_mutex must be unlocked before + lws_context_destroy() call. To ensure that nobody calls + lws_context_destroy() from some parallel thread it is + protected by global websocket mutex. + */ + bsc_mutex_unlock(&ctx->mutex); + bsc_websocket_global_lock(); + lws_context_destroy(ctx->wsctx); + bsc_websocket_global_unlock(); + bsc_mutex_lock(&ctx->mutex); + ctx->wsctx = NULL; + bsc_mutex_unlock(&ctx->mutex); + bws_free_server_ctx(ctx); + DEBUG_PRINTF( + "bws_srv_start() <<< ret = BACNET_WEBSOCKET_NO_RESOURCES\n"); + return BSC_WEBSOCKET_NO_RESOURCES; + } + + bsc_mutex_unlock(&ctx->mutex); + *sh = (BSC_WEBSOCKET_SRV_HANDLE)ctx; + DEBUG_PRINTF("bws_srv_start() <<< ret = BSC_WEBSOCKET_SUCCESS\n"); + return BSC_WEBSOCKET_SUCCESS; +} + +BSC_WEBSOCKET_RET bws_srv_stop(BSC_WEBSOCKET_SRV_HANDLE sh) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF( + "bws_srv_stop() >>> ctx = %p user_param = %p\n", ctx, ctx->user_param); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_stop() <<< bad websocket handle, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } +#endif + + bsc_mutex_lock(&ctx->mutex); + + if (ctx->stop_worker) { + bsc_mutex_unlock(&ctx->mutex); + DEBUG_PRINTF( + "bws_srv_stop() <<< ret = BSC_WEBSOCKET_INVALID_OPERATION\n"); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + ctx->stop_worker = true; + /* wake up libwebsockets runloop */ + lws_cancel_service(ctx->wsctx); + bsc_mutex_unlock(&ctx->mutex); + + DEBUG_PRINTF("bws_srv_stop() <<< ret = BSC_WEBSOCKET_SUCCESS\n"); + return BSC_WEBSOCKET_SUCCESS; +} + +void bws_srv_disconnect(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + DEBUG_PRINTF("bws_srv_disconnect() >>> sh = %p h = %d\n", sh, h); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_disconnect() <<< bad websocket handle\n"); + return; + } +#endif + + bsc_mutex_lock(&ctx->mutex); + if (h >= 0 && h < bws_srv_get_max_sockets(ctx->proto) && + !ctx->stop_worker) { + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process change of connection state */ + ctx->conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(ctx->wsctx); + } + } + bsc_mutex_unlock(&ctx->mutex); + DEBUG_PRINTF("bws_srv_disconnect() <<<\n"); +} + +void bws_srv_send(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF("bws_srv_send() >>> ctx = %p h = %d\n", ctx, h); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_send() <<< bad websocket handle\n"); + return; + } +#endif + + bsc_mutex_lock(&ctx->mutex); + if (ctx->conn[h].state == BSC_WEBSOCKET_STATE_CONNECTED) { + /* tell worker to process send request */ + ctx->conn[h].want_send_data = true; + lws_cancel_service(ctx->wsctx); + } + bsc_mutex_unlock(&ctx->mutex); + DEBUG_PRINTF("bws_srv_send() <<<\n"); +} + +BSC_WEBSOCKET_RET bws_srv_dispatch_send( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + uint8_t *payload, + size_t payload_size) +{ + int written; + BSC_WEBSOCKET_RET ret; + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + + DEBUG_PRINTF( + "bws_srv_dispatch_send() >>> ctx = %p h = %d payload %p " + "payload_size %d\n", + ctx, h, payload, payload_size); + +#if DEBUG_ENABLED == 1 + if (!bws_validate_ctx_pointer(ctx)) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< bad websocket handle, ret = " + "BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } +#endif + + if (h < 0 || h >= bws_srv_get_max_sockets(ctx->proto)) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + if (!payload || !payload_size) { + DEBUG_PRINTF("bws_srv_dispatch_send() <<< BSC_WEBSOCKET_BAD_PARAM\n"); + return BSC_WEBSOCKET_BAD_PARAM; + } + + bsc_mutex_lock(&ctx->mutex); + + if (ctx->stop_worker) { + bsc_mutex_unlock(&ctx->mutex); + DEBUG_PRINTF("bws_srv_dispatch_send() <<< ret = " + "BSC_WEBSOCKET_INVALID_OPERATION\n"); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + if ((ctx->conn[h].state != BSC_WEBSOCKET_STATE_CONNECTED) || + !ctx->conn[h].want_send_data || !ctx->conn[h].can_send_data) { + DEBUG_PRINTF( + "bws_srv_dispatch_send() <<< ret = BACNET_WEBSOCKET_BAD_PARAM\n"); + bsc_mutex_unlock(&ctx->mutex); + return BSC_WEBSOCKET_INVALID_OPERATION; + } + + written = + lws_write(ctx->conn[h].ws, payload, payload_size, LWS_WRITE_BINARY); + + DEBUG_PRINTF("bws_srv_dispatch_send() %d bytes is sent\n", written); + + if (written < (int)payload_size) { + DEBUG_PRINTF( + "bws_srv_dispatch_send() websocket connection is broken(closed)\n"); + /* tell worker to process change of connection state */ + ctx->conn[h].state = BSC_WEBSOCKET_STATE_DISCONNECTING; + lws_cancel_service(ctx->wsctx); + ret = BSC_WEBSOCKET_INVALID_OPERATION; + } else { + ret = BSC_WEBSOCKET_SUCCESS; + } + + bsc_mutex_unlock(&ctx->mutex); + DEBUG_PRINTF("bws_srv_dispatch_send() <<< ret = %d\n", ret); + return ret; +} + +bool bws_srv_get_peer_ip_addr( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + uint8_t *ip_str, + size_t ip_str_len, + uint16_t *port) +{ + BSC_WEBSOCKET_CONTEXT *ctx = (BSC_WEBSOCKET_CONTEXT *)sh; + lws_sockfd_type fd; + struct sockaddr_storage addr; + socklen_t len; + const char *ret = NULL; + + if (!ctx || h < 0 || !ip_str || !ip_str_len || !port || + h >= bws_srv_get_max_sockets(ctx->proto)) { + return false; + } + + bsc_mutex_lock(&ctx->mutex); + + if (ctx->conn[h].state != BSC_WEBSOCKET_STATE_IDLE && + ctx->conn[h].ws != NULL && !ctx->stop_worker) { + len = sizeof(addr); + fd = lws_get_socket_fd(ctx->conn[h].ws); + if (fd != -1) { + getpeername(fd, (struct sockaddr *)&addr, &len); + if (addr.ss_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + *port = ntohs(s->sin_port); + ret = inet_ntop( + AF_INET, &s->sin_addr, (char *)ip_str, ip_str_len); + } else { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + *port = ntohs(s->sin6_port); + ret = inet_ntop( + AF_INET6, &s->sin6_addr, (char *)ip_str, ip_str_len); + } + } + } + + bsc_mutex_unlock(&ctx->mutex); + + if (ret) { + return true; + } + + return false; +} diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index 4bb0284a..2a1f82c5 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -17,12 +17,13 @@ #include #include #endif +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ #include "bacnet/access_rule.h" -#include "bacnet/bacenum.h" #include "bacnet/bacdcode.h" #include "bacnet/bacint.h" #include "bacnet/bacreal.h" -#include "bacnet/bacdef.h" #include "bacnet/bacapp.h" #include "bacnet/bactext.h" #include "bacnet/datetime.h" @@ -30,6 +31,7 @@ #include "bacnet/bacaction.h" #include "bacnet/lighting.h" #include "bacnet/hostnport.h" +#include "bacnet/secure_connect.h" #include "bacnet/weeklyschedule.h" #include "bacnet/calendar_entry.h" #include "bacnet/special_event.h" @@ -518,6 +520,24 @@ int bacapp_encode_application_data( apdu_len = bacnet_channel_value_type_encode( apdu, &value->type.Channel_Value); break; +#endif +#if defined(BACAPP_SECURE_CONNECT) + case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST: + apdu_len = bacapp_encode_SCFailedConnectionRequest( + apdu, &value->type.SC_Failed_Req); + break; + case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS: + apdu_len = bacapp_encode_SCHubFunctionConnection( + apdu, &value->type.SC_Hub_Function_Status); + break; + case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS: + apdu_len = bacapp_encode_SCDirectConnection( + apdu, &value->type.SC_Direct_Status); + break; + case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS: + apdu_len = bacapp_encode_SCHubConnection( + apdu, &value->type.SC_Hub_Status); + break; #endif default: break; @@ -535,6 +555,7 @@ int bacapp_encode_application_data( * @param len_value_type Count of bytes of given tag * @param value Pointer to the application value structure, * used to store the decoded value to. + * @note Decodes only the 13 primitive application data types! * * @return Number of octets consumed (could be zero). * Parameter value->tag set to MAX_BACNET_APPLICATION_TAG when @@ -936,6 +957,10 @@ int bacapp_encode_context_data_value( case BACNET_APPLICATION_TAG_SCALE: case BACNET_APPLICATION_TAG_SHED_LEVEL: case BACNET_APPLICATION_TAG_ACCESS_RULE: + case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST: + case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS: + case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS: + case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS: /* complex data is enclosed in open/close tags */ len = encode_opening_tag(apdu, context_tag_number); apdu_len += len; @@ -1316,6 +1341,19 @@ int bacapp_known_property_tag( /* BACnetAccessRule */ return BACNET_APPLICATION_TAG_ACCESS_RULE; + case PROP_SC_FAILED_CONNECTION_REQUESTS: + return BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST; + + case PROP_SC_HUB_FUNCTION_CONNECTION_STATUS: + return BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS; + + case PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS: + return BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS; + + case PROP_SC_PRIMARY_HUB_CONNECTION_STATUS: + case PROP_SC_FAILOVER_HUB_CONNECTION_STATUS: + return BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS; + default: return -1; } @@ -1625,6 +1663,24 @@ int bacapp_decode_application_tag_value( apdu_len = bacnet_channel_value_decode( apdu, apdu_size, &value->type.Channel_Value); break; +#endif +#if defined(BACAPP_SECURE_CONNECT) + case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST: + apdu_len = bacapp_decode_SCFailedConnectionRequest( + apdu, apdu_size, &value->type.SC_Failed_Req); + break; + case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS: + apdu_len = bacapp_decode_SCHubFunctionConnection( + apdu, apdu_size, &value->type.SC_Hub_Function_Status); + break; + case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS: + apdu_len = bacapp_decode_SCDirectConnection( + apdu, apdu_size, &value->type.SC_Direct_Status); + break; + case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS: + apdu_len = bacapp_decode_SCHubConnection( + apdu, apdu_size, &value->type.SC_Hub_Status); + break; #endif default: break; @@ -3539,6 +3595,28 @@ int bacapp_snprintf_value( str, str_len, &value->type.Object_Property_Reference); break; #endif +#if defined(BACAPP_SECURE_CONNECT) + case BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST: + ret_val = bacapp_snprintf_SCFailedConnectionRequest( + str, str_len, &value->type.SC_Failed_Req); + break; + + case BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS: + ret_val = bacapp_snprintf_SCHubFunctionConnection( + str, str_len, &value->type.SC_Hub_Function_Status); + break; + + case BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS: + ret_val = bacapp_snprintf_SCDirectConnection( + str, str_len, &value->type.SC_Direct_Status); + break; + + case BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS: + ret_val = bacapp_snprintf_SCHubConnection( + str, str_len, &value->type.SC_Hub_Status); + break; + +#endif #if defined(BACAPP_DESTINATION) case BACNET_APPLICATION_TAG_DESTINATION: ret_val = bacnet_destination_to_ascii( diff --git a/src/bacnet/bacapp.h b/src/bacnet/bacapp.h index 6be770b1..2b4994cd 100644 --- a/src/bacnet/bacapp.h +++ b/src/bacnet/bacapp.h @@ -29,6 +29,7 @@ #include "bacnet/calendar_entry.h" #include "bacnet/special_event.h" #include "bacnet/channel_value.h" +#include "bacnet/secure_connect.h" #ifndef BACAPP_PRINT_ENABLED #if PRINT_ENABLED @@ -167,6 +168,12 @@ typedef struct BACnet_Application_Data_Value { #endif #if defined(BACAPP_CHANNEL_VALUE) BACNET_CHANNEL_VALUE Channel_Value; +#endif +#if defined(BACAPP_SECURE_CONNECT) + BACNET_SC_FAILED_CONNECTION_REQUEST SC_Failed_Req; + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS SC_Hub_Function_Status; + BACNET_SC_DIRECT_CONNECTION_STATUS SC_Direct_Status; + BACNET_SC_HUB_CONNECTION_STATUS SC_Hub_Status; #endif } type; /* simple linked list if needed */ diff --git a/src/bacnet/bacdef.h b/src/bacnet/bacdef.h index b055933e..d6f2e299 100644 --- a/src/bacnet/bacdef.h +++ b/src/bacnet/bacdef.h @@ -148,7 +148,9 @@ typedef uint32_t BACNET_ARRAY_INDEX; Ethernet = 6 bytes BACnet/IPv4 = 6 bytes LonTalk = 7 bytes - BACnet/IPv6 = 3 bytes (VMAC) */ + BACnet/IPv6 = 3 bytes (VMAC) + BACnet/SC = 6 bytes (VMAC) + */ #define MAX_MAC_LEN 7 struct BACnet_Device_Address { diff --git a/src/bacnet/bacenum.h b/src/bacnet/bacenum.h index 55ecc4ee..0332cbcc 100644 --- a/src/bacnet/bacenum.h +++ b/src/bacnet/bacenum.h @@ -1599,6 +1599,14 @@ typedef enum { BACNET_APPLICATION_TAG_XY_COLOR, /* BACnetColorCommand */ BACNET_APPLICATION_TAG_COLOR_COMMAND, + /* BACNET_SC_FAILED_CONNECTION_REQUEST */ + BACNET_APPLICATION_TAG_SC_FAILED_CONNECTION_REQUEST, + /* BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS */ + BACNET_APPLICATION_TAG_SC_HUB_FUNCTION_CONNECTION_STATUS, + /* BACNET_SC_DIRECT_CONNECTION_STATUS */ + BACNET_APPLICATION_TAG_SC_DIRECT_CONNECTION_STATUS, + /* BACNET_SC_HUB_CONNECTION_STATUS */ + BACNET_APPLICATION_TAG_SC_HUB_CONNECTION_STATUS, /* BACnetBDTEntry */ BACNET_APPLICATION_TAG_BDT_ENTRY, /* BACnetFDTEntry */ @@ -2172,6 +2180,10 @@ typedef enum { compilers will allocate adequate sized datatype for enum which is used to store decoding */ ERROR_CODE_PROPRIETARY_FIRST = 256, + /* some error codes for internal stack usage */ + ERROR_CODE_ = 65535, + ERROR_CODE_DISCARD = 65534, + ERROR_CODE_DEFAULT = 65535, ERROR_CODE_PROPRIETARY_LAST = 65535 } BACNET_ERROR_CODE; @@ -2592,6 +2604,10 @@ typedef enum { PORT_TYPE_NON_BACNET = 8, PORT_TYPE_BIP6 = 9, PORT_TYPE_SERIAL = 10, + /* For BACnet/SC network port implementations with + a Protocol_Revision 24 and higher, BACnet/SC network ports shall be + represented by a Network Port object at the BACNET_APPLICATION + protocol level with network type of SECURE_CONNECT. */ PORT_TYPE_BSC = 11, /* Enumerated values 0-63 are reserved for definition by ASHRAE. Enumerated values 64-255 may be used by others subject to the @@ -2993,4 +3009,20 @@ typedef enum BACnetAuditOperation { AUDIT_OPERATION_PROPRIETARY_MAX = 63 } BACNET_AUDIT_OPERATION; +typedef enum BACnetSCHubConnectorState { + /* FIXME: prefix with typedef name as much as possible */ + BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION = 0, + BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY = 1, + BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER = 2, + BACNET_SC_HUB_CONNECTOR_STATE_MAX = 3 +} BACNET_SC_HUB_CONNECTOR_STATE; + +typedef enum BACnetSCConnectionState { + BACNET_SC_CONNECTION_STATE_NOT_CONNECTED = 0, + BACNET_SC_CONNECTION_STATE_CONNECTED = 1, + BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS = 2, + BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT = 3, + BACNET_SC_CONNECTION_STATE_MAX = 4 +} BACNET_SC_CONNECTION_STATE; + #endif /* end of BACENUM_H */ diff --git a/src/bacnet/basic/object/client/device-client.c b/src/bacnet/basic/object/client/device-client.c index 75698c6e..ae5ff9ed 100644 --- a/src/bacnet/basic/object/client/device-client.c +++ b/src/bacnet/basic/object/client/device-client.c @@ -133,6 +133,28 @@ static object_functions_t Object_Table[] = { NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, +#endif +#if defined(BACFILE) + { OBJECT_FILE, + bacfile_init, + bacfile_count, + bacfile_index_to_instance, + bacfile_valid_instance, + bacfile_object_name, + bacfile_read_property, + bacfile_write_property, + BACfile_Property_Lists, + NULL /* ReadRangeInfo */, + NULL /* Iterator */, + NULL /* Value_Lists */, + NULL /* COV */, + NULL /* COV Clear */, + NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, + NULL /* Remove_List_Element */, + NULL /* Create */, + NULL /* Delete */, + NULL /* Timer */ }, #endif { MAX_BACNET_OBJECT_TYPE, NULL /* Init */, diff --git a/src/bacnet/basic/object/netport.c b/src/bacnet/basic/object/netport.c index ab12ee8c..5147dc5e 100644 --- a/src/bacnet/basic/object/netport.c +++ b/src/bacnet/basic/object/netport.c @@ -25,6 +25,7 @@ #include "bacnet/basic/object/device.h" /* me */ #include "bacnet/basic/object/netport.h" +#include #if defined(BACDL_BIP6) #include "bacnet/datalink/bvlc6.h" @@ -92,6 +93,11 @@ struct mstp_port { uint8_t Max_Info_Frames; }; +struct bsc_port { + uint8_t MAC_Address[6]; + BACNET_SC_PARAMS Parameters; +}; + struct object_data { uint32_t Instance_Number; const char *Object_Name; @@ -111,11 +117,14 @@ struct object_data { struct bacnet_ipv6_port IPv6; struct ethernet_port Ethernet; struct mstp_port MSTP; + struct bsc_port BSC; } Network; }; + #ifndef BACNET_NETWORK_PORTS_MAX #define BACNET_NETWORK_PORTS_MAX 1 #endif + static struct object_data Object_List[BACNET_NETWORK_PORTS_MAX]; /* These three arrays are used by the ReadPropertyMultiple handler */ @@ -194,6 +203,53 @@ static const int BIP6_Port_Properties_Optional[] = { -1 }; +static const int BSC_Port_Properties_Optional[] = { + PROP_NETWORK_NUMBER, + PROP_NETWORK_NUMBER_QUALITY, + PROP_APDU_LENGTH, + PROP_MAC_ADDRESS, + PROP_BACNET_IP_MODE, + PROP_IP_ADDRESS, + PROP_BACNET_IP_UDP_PORT, + PROP_IP_SUBNET_MASK, + PROP_IP_DEFAULT_GATEWAY, + PROP_IP_DNS_SERVER, + PROP_MAX_BVLC_LENGTH_ACCEPTED, + PROP_MAX_NPDU_LENGTH_ACCEPTED, + PROP_SC_PRIMARY_HUB_URI, + PROP_SC_FAILOVER_HUB_URI, + PROP_SC_MINIMUM_RECONNECT_TIME, + PROP_SC_MAXIMUM_RECONNECT_TIME, + PROP_SC_CONNECT_WAIT_TIMEOUT, + PROP_SC_DISCONNECT_WAIT_TIMEOUT, + PROP_SC_HEARTBEAT_TIMEOUT, + PROP_SC_HUB_CONNECTOR_STATE, + PROP_OPERATIONAL_CERTIFICATE_FILE, + PROP_ISSUER_CERTIFICATE_FILES, + PROP_CERTIFICATE_SIGNING_REQUEST_FILE, +/*SC optional*/ +#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE + PROP_ROUTING_TABLE, +#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */ +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + PROP_SC_PRIMARY_HUB_CONNECTION_STATUS, + PROP_SC_FAILOVER_HUB_CONNECTION_STATUS, + PROP_SC_HUB_FUNCTION_ENABLE, + PROP_SC_HUB_FUNCTION_ACCEPT_URIS, + PROP_SC_HUB_FUNCTION_BINDING, + PROP_SC_HUB_FUNCTION_CONNECTION_STATUS, +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + PROP_SC_DIRECT_CONNECT_INITIATE_ENABLE, + PROP_SC_DIRECT_CONNECT_ACCEPT_ENABLE, + PROP_SC_DIRECT_CONNECT_ACCEPT_URIS, + PROP_SC_DIRECT_CONNECT_BINDING, + PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS, +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + PROP_SC_FAILED_CONNECTION_REQUESTS, + -1 +}; + static const int Network_Port_Properties_Proprietary[] = { -1 }; /** @@ -229,6 +285,9 @@ void Network_Port_Property_List( case PORT_TYPE_BIP: *pOptional = BIP_Port_Properties_Optional; break; + case PORT_TYPE_BSC: + *pOptional = BSC_Port_Properties_Optional; + break; case PORT_TYPE_BIP6: *pOptional = BIP6_Port_Properties_Optional; break; @@ -590,6 +649,16 @@ uint8_t Network_Port_Type(uint32_t object_instance) index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { port_type = Object_List[index].Network_Type; +#if (BACNET_PROTOCOL_REVISION >= 17) && (BACNET_PROTOCOL_REVISION <= 23) + /* For BACnet/SC network port implementations with + a protocol revision Protocol_Revision 17 and higher through 23, + BACnet/SC network ports shall be represented by a Network Port + object at the BACNET_APPLICATION protocol level with + a proprietary network type value. */ + if (port_type == PORT_TYPE_BSC) { + port_type = PORT_TYPE_BSC_INTERIM; + } +#endif } return port_type; @@ -612,7 +681,18 @@ bool Network_Port_Type_Set(uint32_t object_instance, uint8_t value) index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { +#if (BACNET_PROTOCOL_REVISION >= 17) && (BACNET_PROTOCOL_REVISION <= 23) + /* For BACnet/SC network port implementations with + a protocol revision Protocol_Revision 17 and higher through 23, + BACnet/SC network ports shall be represented by a Network Port + object at the BACNET_APPLICATION protocol level with + a proprietary network type value. */ + if (value == PORT_TYPE_BSC_INTERIM) { + value = PORT_TYPE_BSC; + } +#endif Object_List[index].Network_Type = value; + status = true; } @@ -748,6 +828,10 @@ uint8_t Network_Port_MAC_Address_Value( mac = &Object_List[index].Network.IPv6.MAC_Address[0]; mac_len = sizeof(Object_List[index].Network.IPv6.MAC_Address); break; + case PORT_TYPE_BSC: + mac = &Object_List[index].Network.BSC.MAC_Address[0]; + mac_len = sizeof(Object_List[index].Network.BSC.MAC_Address); + break; default: break; } @@ -824,6 +908,10 @@ bool Network_Port_MAC_Address_Set( mac_dest = &Object_List[index].Network.IPv6.MAC_Address[0]; mac_size = sizeof(Object_List[index].Network.IPv6.MAC_Address); break; + case PORT_TYPE_BSC: + mac_dest = &Object_List[index].Network.BSC.MAC_Address[0]; + mac_size = sizeof(Object_List[index].Network.BSC.MAC_Address); + break; default: break; } @@ -3540,6 +3628,178 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_character_string(&apdu[0], &char_string); break; +#ifdef BACDL_BSC + case PROP_MAX_BVLC_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_Max_BVLC_Length_Accepted(rpdata->object_instance)); + break; + case PROP_MAX_NPDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_Max_NPDU_Length_Accepted(rpdata->object_instance)); + break; + case PROP_SC_PRIMARY_HUB_URI: + Network_Port_SC_Primary_Hub_URI( + rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SC_FAILOVER_HUB_URI: + Network_Port_SC_Failover_Hub_URI( + rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SC_MINIMUM_RECONNECT_TIME: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_SC_Minimum_Reconnect_Time( + rpdata->object_instance)); + break; + case PROP_SC_MAXIMUM_RECONNECT_TIME: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_SC_Maximum_Reconnect_Time( + rpdata->object_instance)); + break; + case PROP_SC_CONNECT_WAIT_TIMEOUT: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_SC_Connect_Wait_Timeout(rpdata->object_instance)); + break; + case PROP_SC_DISCONNECT_WAIT_TIMEOUT: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_SC_Disconnect_Wait_Timeout( + rpdata->object_instance)); + break; + case PROP_SC_HEARTBEAT_TIMEOUT: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_SC_Heartbeat_Timeout(rpdata->object_instance)); + break; + case PROP_SC_HUB_CONNECTOR_STATE: + apdu_len = encode_application_enumerated( + &apdu[0], + Network_Port_SC_Hub_Connector_State(rpdata->object_instance)); + break; + case PROP_OPERATIONAL_CERTIFICATE_FILE: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_Operational_Certificate_File( + rpdata->object_instance)); + break; + case PROP_ISSUER_CERTIFICATE_FILES: + apdu_len = bacnet_array_encode( + rpdata->object_instance, rpdata->array_index, + Network_Port_Issuer_Certificate_File_Encode, + BACNET_ISSUER_CERT_FILE_MAX, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + break; + case PROP_CERTIFICATE_SIGNING_REQUEST_FILE: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_Certificate_Signing_Request_File( + rpdata->object_instance)); + break; + /* SC optionals */ +#if BACNET_SECURE_CONNECT_ROUTING_TABLE + case PROP_ROUTING_TABLE: + apdu_len = Network_Port_Routing_Table_Encode( + rpdata->object_instance, apdu, apdu_size); + break; +#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */ +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + case PROP_SC_PRIMARY_HUB_CONNECTION_STATUS: + apdu_len = bacapp_encode_SCHubConnection( + &apdu[0], + Network_Port_SC_Primary_Hub_Connection_Status( + rpdata->object_instance)); + break; + case PROP_SC_FAILOVER_HUB_CONNECTION_STATUS: + apdu_len = bacapp_encode_SCHubConnection( + &apdu[0], + Network_Port_SC_Failover_Hub_Connection_Status( + rpdata->object_instance)); + break; + case PROP_SC_HUB_FUNCTION_ENABLE: + apdu_len = encode_application_boolean( + &apdu[0], + Network_Port_SC_Hub_Function_Enable(rpdata->object_instance)); + break; + case PROP_SC_HUB_FUNCTION_ACCEPT_URIS: + apdu_len = bacnet_array_encode( + rpdata->object_instance, rpdata->array_index, + Network_Port_SC_Hub_Function_Accept_URI_Encode, + BACNET_SC_DIRECT_ACCEPT_URI_MAX, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + break; + case PROP_SC_HUB_FUNCTION_BINDING: + Network_Port_SC_Hub_Function_Binding( + rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SC_HUB_FUNCTION_CONNECTION_STATUS: + apdu_len = Network_Port_SC_Hub_Function_Connection_Status_Encode( + rpdata->object_instance, apdu, apdu_size); + break; +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + case PROP_SC_DIRECT_CONNECT_INITIATE_ENABLE: + apdu_len = encode_application_boolean( + &apdu[0], + Network_Port_SC_Direct_Connect_Initiate_Enable( + rpdata->object_instance)); + break; + case PROP_SC_DIRECT_CONNECT_ACCEPT_ENABLE: + apdu_len = encode_application_boolean( + &apdu[0], + Network_Port_SC_Direct_Connect_Accept_Enable( + rpdata->object_instance)); + break; + case PROP_SC_DIRECT_CONNECT_ACCEPT_URIS: + apdu_len = bacnet_array_encode( + rpdata->object_instance, rpdata->array_index, + Network_Port_SC_Direct_Connect_Accept_URI_Encode, + BACNET_SC_DIRECT_ACCEPT_URI_MAX, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + break; + case PROP_SC_DIRECT_CONNECT_BINDING: + Network_Port_SC_Direct_Connect_Binding( + rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS: + apdu_len = Network_Port_SC_Direct_Connect_Connection_Status_Encode( + rpdata->object_instance, apdu, apdu_size); + break; +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + case PROP_SC_FAILED_CONNECTION_REQUESTS: + apdu_len = Network_Port_SC_Failed_Connection_Requests_Encode( + rpdata->object_instance, apdu, apdu_size); + break; +#endif /* BACDL_BSC */ default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -3693,6 +3953,11 @@ bool Network_Port_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; } + if (!status && (wp_data->error_code == ERROR_CODE_OTHER)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + return status; } @@ -3715,7 +3980,7 @@ int Network_Port_Read_Range_BDT(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest) * ReadRange service handler for the BACnet/IP FDT. * * @param apdu - place to encode the data - * @param apdu - BACNET_READ_RANGE_DATA data + * @param pRequest - BACNET_READ_RANGE_DATA data * * @return number of bytes encoded */ @@ -3845,7 +4110,16 @@ void Network_Port_Changes_Discard(void) */ void Network_Port_Cleanup(void) { - /* do something interesting */ +#if defined(BACDL_BSC) && defined(BACNET_SECURE_CONNECT_ROUTING_TABLE) + unsigned index = 0; + for (index = 0; index < BACNET_NETWORK_PORTS_MAX; index++) { + BACNET_SC_PARAMS *sc = &Object_List[index].Network.BSC.Parameters; + if (sc->Routing_Table) { + Keylist_Delete(sc->Routing_Table); + sc->Routing_Table = NULL; + } + } +#endif } /** @@ -3853,5 +4127,58 @@ void Network_Port_Cleanup(void) */ void Network_Port_Init(void) { + unsigned index = 0; + +#ifdef BACDL_BSC + BACNET_SC_PARAMS *sc; +#endif /* BACDL_BSC */ + /* do something interesting */ + + for (index = 0; index < BACNET_NETWORK_PORTS_MAX; index++) { + memset(&Object_List[index], 0, sizeof(Object_List[index])); +#ifdef BACDL_BSC + Object_List[index].Network_Type = PORT_TYPE_BSC; + sc = &Object_List[index].Network.BSC.Parameters; + Object_List[index].Activate_Changes = + Network_Port_SC_Pending_Params_Apply; + Object_List[index].Discard_Changes = + Network_Port_SC_Pending_Params_Discard; +#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE + sc->Routing_Table = Keylist_Create(); +#endif + sc->SC_Failed_Connection_Requests_Count = 0; +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + sc->SC_Hub_Function_Connection_Status_Count = 0; +#endif +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + sc->SC_Direct_Connect_Connection_Status_Count = 0; +#endif + (void)sc; +#endif /* BACDL_BSC */ + } } + +#ifdef BACDL_BSC +/** + * For a given object instance-number, gets SC parameters structure + * + * @param object_instance - object-instance number of the object + * + * @return SC params structure + */ +BACNET_SC_PARAMS *Network_Port_SC_Params(uint32_t object_instance) +{ + BACNET_SC_PARAMS *param = NULL; + unsigned index = 0; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + if (Object_List[index].Network_Type == PORT_TYPE_BSC) { + param = &Object_List[index].Network.BSC.Parameters; + } + } + + return param; +} +#endif diff --git a/src/bacnet/basic/object/netport_internal.h b/src/bacnet/basic/object/netport_internal.h new file mode 100644 index 00000000..39ffaa78 --- /dev/null +++ b/src/bacnet/basic/object/netport_internal.h @@ -0,0 +1,107 @@ +/** + * @file + * @brief Helper Network port objects to implement secure connect attributes + * @author Mikhail Antropov + * @date December 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_NETPORT_INTERNAL_H +#define BACNET_NETPORT_INTERNAL_H +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/basic/sys/keylist.h" +#include "bacnet/apdu.h" +#include "bacnet/datetime.h" +#include "bacnet/hostnport.h" +#include "bacnet/datalink/bsc/bsc-conf.h" +#include "bacnet/basic/object/sc_netport.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Support + - SC_Hub_Function_Connection_Status + - SC_Direct_Connect_Connection_Status + - SC_FailedConnectionRequests +*/ + +typedef struct BACnetSCAttributes_T { + BACNET_UNSIGNED_INTEGER Max_BVLC_Length_Accepted; + BACNET_UNSIGNED_INTEGER Max_BVLC_Length_Accepted_dirty; + BACNET_UNSIGNED_INTEGER Max_NPDU_Length_Accepted; + BACNET_UNSIGNED_INTEGER Max_NPDU_Length_Accepted_dirty; + char SC_Primary_Hub_URI[BACNET_URI_LENGTH]; + char SC_Primary_Hub_URI_dirty[BACNET_URI_LENGTH]; + char SC_Failover_Hub_URI[BACNET_URI_LENGTH]; + char SC_Failover_Hub_URI_dirty[BACNET_URI_LENGTH]; + BACNET_UNSIGNED_INTEGER SC_Minimum_Reconnect_Time; + BACNET_UNSIGNED_INTEGER SC_Minimum_Reconnect_Time_dirty; + BACNET_UNSIGNED_INTEGER SC_Maximum_Reconnect_Time; + BACNET_UNSIGNED_INTEGER SC_Maximum_Reconnect_Time_dirty; + BACNET_UNSIGNED_INTEGER SC_Connect_Wait_Timeout; + BACNET_UNSIGNED_INTEGER SC_Connect_Wait_Timeout_dirty; + BACNET_UNSIGNED_INTEGER SC_Disconnect_Wait_Timeout; + BACNET_UNSIGNED_INTEGER SC_Disconnect_Wait_Timeout_dirty; + BACNET_UNSIGNED_INTEGER SC_Heartbeat_Timeout; + BACNET_UNSIGNED_INTEGER SC_Heartbeat_Timeout_dirty; + BACNET_SC_HUB_CONNECTOR_STATE SC_Hub_Connector_State; + uint32_t Operational_Certificate_File; + uint32_t Issuer_Certificate_Files[BACNET_ISSUER_CERT_FILE_MAX]; + uint32_t Certificate_Signing_Request_File; + /* Optional params */ +#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE + OS_Keylist Routing_Table; +#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */ +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + BACNET_SC_HUB_CONNECTION_STATUS SC_Primary_Hub_Connection_Status; + BACNET_SC_HUB_CONNECTION_STATUS SC_Failover_Hub_Connection_Status; + bool SC_Hub_Function_Enable; + bool SC_Hub_Function_Enable_dirty; + char SC_Hub_Function_Accept_URIs + [BACNET_SC_HUB_URI_MAX * (BACNET_URI_LENGTH + 1)]; + char SC_Hub_Function_Accept_URIs_dirty + [BACNET_SC_HUB_URI_MAX * (BACNET_URI_LENGTH + 1)]; + char SC_Hub_Function_Binding[BACNET_BINDING_STRING_LENGTH]; + char SC_Hub_Function_Binding_dirty[BACNET_BINDING_STRING_LENGTH]; + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS + SC_Hub_Function_Connection_Status + [BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM]; + uint8_t SC_Hub_Function_Connection_Status_Count; + uint16_t Hub_Server_Port; +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + bool SC_Direct_Connect_Initiate_Enable; + bool SC_Direct_Connect_Initiate_Enable_dirty; + bool SC_Direct_Connect_Accept_Enable; + bool SC_Direct_Connect_Accept_Enable_dirty; + char SC_Direct_Connect_Accept_URIs + [BACNET_SC_DIRECT_ACCEPT_URI_MAX * (BACNET_URI_LENGTH + 1)]; + char SC_Direct_Connect_Accept_URIs_dirty + [BACNET_SC_DIRECT_ACCEPT_URI_MAX * (BACNET_URI_LENGTH + 1)]; + char SC_Direct_Connect_Binding[BACNET_BINDING_STRING_LENGTH]; + char SC_Direct_Connect_Binding_dirty[BACNET_BINDING_STRING_LENGTH]; + BACNET_SC_DIRECT_CONNECTION_STATUS SC_Direct_Connect_Connection_Status + [BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM]; + uint8_t SC_Direct_Connect_Connection_Status_Count; + uint16_t Direct_Server_Port; +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + BACNET_SC_FAILED_CONNECTION_REQUEST SC_Failed_Connection_Requests + [BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM]; + uint8_t SC_Failed_Connection_Requests_Count; + uint32_t Certificate_Key_File; + BACNET_UUID Local_UUID; +} BACNET_SC_PARAMS; + +BACNET_SC_PARAMS *Network_Port_SC_Params(uint32_t object_instance); +void Network_Port_SC_Pending_Params_Apply(uint32_t object_instance); +void Network_Port_SC_Pending_Params_Discard(uint32_t object_instance); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETPORT_INTERNAL_H */ diff --git a/src/bacnet/basic/object/sc_netport.c b/src/bacnet/basic/object/sc_netport.c new file mode 100644 index 00000000..464361c6 --- /dev/null +++ b/src/bacnet/basic/object/sc_netport.c @@ -0,0 +1,2056 @@ +/** + * @file + * @brief Helper Network port objects for implement secure connect attributes + * @author Mikhail Antropov + * @date June 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* me */ +#include +#include +#include +#include +#include + +#define SC_MIN_RECONNECT_MIN 2 +#define SC_MIN_RECONNECT_MAX 300 + +#define SC_MAX_RECONNECT_MIN 2 +#define SC_MAX_RECONNECT_MAX 600 + +#define SC_WAIT_CONNECT_MIN 5 +#define SC_WAIT_CONNECT_MAX 300 + +static int +string_splite(const char *str, char separator, int *indexes, int length); +static bool +string_subsstr(char *str, int length, int index, const char *substr); + +static void sc_binding_parse(char *str, uint16_t *port, char **ifname) +{ + char *p = NULL; + + *port = 0; + *ifname = NULL; + + if (str != NULL) { + for (p = str; (*p != 0) && (*p != BACNET_SC_BINDING_SEPARATOR); ++p) + ; + + if (p != str) { + if (*p == BACNET_SC_BINDING_SEPARATOR) { + *p = 0; + *port = (uint16_t)strtol(p + 1, NULL, 0); + *ifname = (char *)str; + } else { + *port = (uint16_t)strtol(str, NULL, 0); + *ifname = NULL; + } + } + } +} + +BACNET_UNSIGNED_INTEGER +Network_Port_Max_BVLC_Length_Accepted(uint32_t object_instance) +{ + BACNET_UNSIGNED_INTEGER max_length = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + max_length = params->Max_BVLC_Length_Accepted; + } + + return max_length; +} + +bool Network_Port_Max_BVLC_Length_Accepted_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->Max_BVLC_Length_Accepted = value; + + return true; +} + +bool Network_Port_Max_BVLC_Length_Accepted_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->Max_BVLC_Length_Accepted_dirty = value; + + return true; +} + +BACNET_UNSIGNED_INTEGER +Network_Port_Max_NPDU_Length_Accepted(uint32_t object_instance) +{ + BACNET_UNSIGNED_INTEGER max_length = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + max_length = params->Max_NPDU_Length_Accepted; + } + + return max_length; +} + +bool Network_Port_Max_NPDU_Length_Accepted_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->Max_NPDU_Length_Accepted = value; + + return true; +} + +bool Network_Port_Max_NPDU_Length_Accepted_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->Max_NPDU_Length_Accepted_dirty = value; + + return true; +} + +bool Network_Port_SC_Primary_Hub_URI( + uint32_t object_instance, BACNET_CHARACTER_STRING *str) +{ + bool status = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + status = characterstring_init_ansi(str, params->SC_Primary_Hub_URI); + } + + return status; +} + +const char *Network_Port_SC_Primary_Hub_URI_char(uint32_t object_instance) +{ + char *p = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params && params->SC_Primary_Hub_URI[0]) { + p = params->SC_Primary_Hub_URI; + } + + return p; +} + +bool Network_Port_SC_Primary_Hub_URI_Set( + uint32_t object_instance, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (str) { + snprintf( + params->SC_Primary_Hub_URI, sizeof(params->SC_Primary_Hub_URI), + "%s", str); + } else { + params->SC_Primary_Hub_URI[0] = 0; + } + + return true; +} + +bool Network_Port_SC_Primary_Hub_URI_Dirty_Set( + uint32_t object_instance, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (str) { + snprintf( + params->SC_Primary_Hub_URI_dirty, + sizeof(params->SC_Primary_Hub_URI_dirty), "%s", str); + } else { + params->SC_Primary_Hub_URI_dirty[0] = 0; + } + + Network_Port_Changes_Pending_Set(object_instance, true); + + return true; +} + +bool Network_Port_SC_Failover_Hub_URI( + uint32_t object_instance, BACNET_CHARACTER_STRING *str) +{ + bool status = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + status = characterstring_init_ansi(str, params->SC_Failover_Hub_URI); + } + + return status; +} + +const char *Network_Port_SC_Failover_Hub_URI_char(uint32_t object_instance) +{ + char *p = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params && params->SC_Failover_Hub_URI[0]) { + p = params->SC_Failover_Hub_URI; + } + + return p; +} + +bool Network_Port_SC_Failover_Hub_URI_Set( + uint32_t object_instance, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (str) { + snprintf( + params->SC_Failover_Hub_URI, sizeof(params->SC_Failover_Hub_URI), + "%s", str); + } else { + params->SC_Failover_Hub_URI[0] = 0; + } + + return true; +} + +bool Network_Port_SC_Failover_Hub_URI_Dirty_Set( + uint32_t object_instance, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (str) { + snprintf( + params->SC_Failover_Hub_URI_dirty, + sizeof(params->SC_Failover_Hub_URI_dirty), "%s", str); + } else { + params->SC_Failover_Hub_URI_dirty[0] = 0; + } + + Network_Port_Changes_Pending_Set(object_instance, true); + + return true; +} + +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Minimum_Reconnect_Time(uint32_t object_instance) +{ + BACNET_UNSIGNED_INTEGER timeout = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + timeout = params->SC_Minimum_Reconnect_Time; + } + + return timeout; +} + +bool Network_Port_SC_Minimum_Reconnect_Time_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if ((value < SC_MIN_RECONNECT_MIN) || (value > SC_MIN_RECONNECT_MAX)) { + return false; + } + + params->SC_Minimum_Reconnect_Time = value; + + return true; +} + +bool Network_Port_SC_Minimum_Reconnect_Time_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if ((value < SC_MIN_RECONNECT_MIN) || (value > SC_MIN_RECONNECT_MAX)) { + return false; + } + + params->SC_Minimum_Reconnect_Time_dirty = value; + + return true; +} + +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Maximum_Reconnect_Time(uint32_t object_instance) +{ + BACNET_UNSIGNED_INTEGER timeout = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + timeout = params->SC_Maximum_Reconnect_Time; + } + + return timeout; +} + +bool Network_Port_SC_Maximum_Reconnect_Time_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if ((value < SC_MAX_RECONNECT_MIN) || (value > SC_MAX_RECONNECT_MAX)) { + return false; + } + + params->SC_Maximum_Reconnect_Time = value; + + return true; +} + +bool Network_Port_SC_Maximum_Reconnect_Time_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if ((value < SC_MAX_RECONNECT_MIN) || (value > SC_MAX_RECONNECT_MAX)) { + return false; + } + + params->SC_Maximum_Reconnect_Time_dirty = value; + + return true; +} + +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Connect_Wait_Timeout(uint32_t object_instance) +{ + BACNET_UNSIGNED_INTEGER timeout = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + timeout = params->SC_Connect_Wait_Timeout; + } + + return timeout; +} + +bool Network_Port_SC_Connect_Wait_Timeout_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if ((value < SC_WAIT_CONNECT_MIN) || (value > SC_WAIT_CONNECT_MAX)) { + return false; + } + + params->SC_Connect_Wait_Timeout = value; + + return true; +} + +bool Network_Port_SC_Connect_Wait_Timeout_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if ((value < SC_WAIT_CONNECT_MIN) || (value > SC_WAIT_CONNECT_MAX)) { + return false; + } + + params->SC_Connect_Wait_Timeout_dirty = value; + + return true; +} + +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Disconnect_Wait_Timeout(uint32_t object_instance) +{ + BACNET_UNSIGNED_INTEGER timeout = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + timeout = params->SC_Disconnect_Wait_Timeout; + } + + return timeout; +} + +bool Network_Port_SC_Disconnect_Wait_Timeout_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Disconnect_Wait_Timeout = value; + + return true; +} + +bool Network_Port_SC_Disconnect_Wait_Timeout_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Disconnect_Wait_Timeout_dirty = value; + + return true; +} + +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Heartbeat_Timeout(uint32_t object_instance) +{ + BACNET_UNSIGNED_INTEGER timeout = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + timeout = params->SC_Heartbeat_Timeout; + } + + return timeout; +} + +bool Network_Port_SC_Heartbeat_Timeout_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Heartbeat_Timeout = value; + + return true; +} + +bool Network_Port_SC_Heartbeat_Timeout_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Heartbeat_Timeout_dirty = value; + + return true; +} + +BACNET_SC_HUB_CONNECTOR_STATE +Network_Port_SC_Hub_Connector_State(uint32_t object_instance) +{ + BACNET_SC_HUB_CONNECTOR_STATE state = + BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + state = params->SC_Hub_Connector_State; + } + + return state; +} + +bool Network_Port_SC_Hub_Connector_State_Set( + uint32_t object_instance, BACNET_SC_HUB_CONNECTOR_STATE value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Hub_Connector_State = value; + + return true; +} + +uint32_t Network_Port_Operational_Certificate_File(uint32_t object_instance) +{ + uint32_t id = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + id = params->Operational_Certificate_File; + } + + return id; +} + +bool Network_Port_Operational_Certificate_File_Set( + uint32_t object_instance, uint32_t value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->Operational_Certificate_File = value; + + return true; +} + +uint32_t +Network_Port_Issuer_Certificate_File(uint32_t object_instance, uint8_t index) +{ + uint32_t id = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params && (index < BACNET_ISSUER_CERT_FILE_MAX)) { + id = params->Issuer_Certificate_Files[index]; + } + + return id; +} + +bool Network_Port_Issuer_Certificate_File_Set( + uint32_t object_instance, uint8_t index, uint32_t value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params || (index >= BACNET_ISSUER_CERT_FILE_MAX)) { + return false; + } + + params->Issuer_Certificate_Files[index] = value; + + return true; +} + +/** + * @brief Encode one BACnetARRAY property element; a function template + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to n for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for invalid array index + */ +int Network_Port_Issuer_Certificate_File_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = 0; + uint32_t file_instance = 0; + + if (index >= BACNET_ISSUER_CERT_FILE_MAX) { + apdu_len = BACNET_STATUS_ERROR; + } else { + file_instance = + Network_Port_Issuer_Certificate_File(object_instance, index); + apdu_len = encode_application_unsigned(apdu, file_instance); + } + + return apdu_len; +} + +uint32_t Network_Port_Certificate_Signing_Request_File(uint32_t object_instance) +{ + uint32_t id = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + id = params->Certificate_Signing_Request_File; + } + + return id; +} + +bool Network_Port_Certificate_Signing_Request_File_Set( + uint32_t object_instance, uint32_t value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->Certificate_Signing_Request_File = value; + + return true; +} + +#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE + +#if 0 +/* This function will needed if the Network_Port_Routing_Table_Find() + or Network_Port_Routing_Table_Get() will return individual fields, + not the entire struct. */ +static bool MAC_Address_Get(uint8_t network_type, uint8_t *mac, + BACNET_OCTET_STRING *mac_address) +{ + bool status = false; + uint8_t ip_mac[4 + 2] = { 0 }; + size_t mac_len = 0; + + switch (network_type) { + case PORT_TYPE_ETHERNET: + mac_len = 6; + break; + case PORT_TYPE_MSTP: + mac_len = 1; + break; + case PORT_TYPE_BIP: + memcpy(&ip_mac[0], mac, 4); + /* convert port from host-byte-order to network-byte-order */ + encode_unsigned16(&ip_mac[4], *(uint16_t*)(mac + 4)); + mac = &ip_mac[0]; + mac_len = sizeof(ip_mac); + break; + case PORT_TYPE_BIP6: + mac_len = 3; + break; + default: + break; + } + if (mac) { + status = octetstring_init(mac_address, mac, mac_len); + } + return status; +} +#endif + +static bool MAC_Address_Set( + uint8_t network_type, uint8_t *mac_dest, uint8_t *mac_src, uint8_t mac_len) +{ + bool status = false; + size_t mac_size = 0; + uint8_t ip_mac[4 + 2] = { 0 }; + + switch (network_type) { + case PORT_TYPE_ETHERNET: + mac_size = 6; + break; + case PORT_TYPE_MSTP: + mac_size = 1; + break; + case PORT_TYPE_BIP: + memcpy(&ip_mac[0], mac_src, 4); + /* convert port from host-byte-order to network-byte-order */ + decode_unsigned16(&mac_src[4], (uint16_t *)&ip_mac[4]); + mac_src = &ip_mac[0]; + mac_len = sizeof(ip_mac); + break; + case PORT_TYPE_BIP6: + mac_size = 3; + break; + default: + break; + } + if (mac_src && mac_dest && (mac_len == mac_size)) { + memcpy(mac_dest, mac_src, mac_size); + status = true; + } + return status; +} + +BACNET_ROUTER_ENTRY *Network_Port_Routing_Table_Find( + uint32_t object_instance, uint16_t network_number) +{ + BACNET_ROUTER_ENTRY *entry = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return entry; + } + + entry = Keylist_Data(params->Routing_Table, network_number); + + return entry; +} + +BACNET_ROUTER_ENTRY * +Network_Port_Routing_Table_Get(uint32_t object_instance, uint8_t index) +{ + BACNET_ROUTER_ENTRY *entry = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return entry; + } + + entry = Keylist_Data_Index(params->Routing_Table, index); + + return entry; +} + +bool Network_Port_Routing_Table_Add( + uint32_t object_instance, + uint16_t network_number, + uint8_t *mac, + uint8_t mac_len, + uint8_t status, + uint8_t performance_index) +{ + bool st = false; + uint8_t network_type; + BACNET_ROUTER_ENTRY *entry; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + network_type = Network_Port_Type(object_instance); + entry = calloc(1, sizeof(BACNET_ROUTER_ENTRY)); + if (!entry) { + return false; + } + + entry->Network_Number = network_number; + MAC_Address_Set(network_type, entry->Mac_Address, mac, mac_len); + entry->Status = status; + entry->Performance_Index = performance_index; + st = Keylist_Data_Add(params->Routing_Table, network_number, entry); + + return st; +} + +bool Network_Port_Routing_Table_Delete( + uint32_t object_instance, uint16_t network_number) +{ + BACNET_ROUTER_ENTRY *entry; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + entry = Keylist_Data_Delete(params->Routing_Table, network_number); + free(entry); + + return true; +} + +bool Network_Port_Routing_Table_Delete_All(uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + Keylist_Delete(params->Routing_Table); + params->Routing_Table = Keylist_Create(); + + return true; +} + +uint8_t Network_Port_Routing_Table_Count(uint32_t object_instance) +{ + uint8_t count = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return 0; + } + + count = Keylist_Count(params->Routing_Table); + + return count; +} + +/** + * @brief Handle a request to encode all the the routing table entries + * @param apdu [out] Buffer in which the APDU contents are built. + * @param max_apdu [in] Max length of the APDU buffer. + * + * @return How many bytes were encoded in the buffer, or + * BACNET_STATUS_ABORT if the response would not fit within the buffer. + */ +int Network_Port_Routing_Table_Encode( + uint32_t object_instance, uint8_t *apdu, int max_apdu) +{ + BACNET_ROUTER_ENTRY *entry = NULL; + int apdu_len = 0; + unsigned index = 0; + unsigned size = 0; + + size = Network_Port_Routing_Table_Count(object_instance); + for (index = 0; index < size; index++) { + entry = Network_Port_Routing_Table_Get(object_instance, index); + apdu_len += bacapp_encode_RouterEntry(NULL, entry); + } + if (apdu_len > max_apdu) { + return BACNET_STATUS_ABORT; + } + apdu_len = 0; + for (index = 0; index < size; index++) { + entry = Network_Port_Routing_Table_Get(object_instance, index); + apdu_len += bacapp_encode_RouterEntry(&apdu[apdu_len], entry); + } + + return apdu_len; +} + +#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */ + +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + +BACNET_SC_HUB_CONNECTION_STATUS * +Network_Port_SC_Primary_Hub_Connection_Status(uint32_t object_instance) +{ + BACNET_SC_HUB_CONNECTION_STATUS *connection = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + connection = ¶ms->SC_Primary_Hub_Connection_Status; + } + + return connection; +} + +bool Network_Port_SC_Primary_Hub_Connection_Status_Set( + uint32_t object_instance, + BACNET_SC_CONNECTION_STATE state, + BACNET_DATE_TIME *connect_ts, + BACNET_DATE_TIME *disconnect_ts, + BACNET_ERROR_CODE error, + const char *error_details) +{ + BACNET_SC_HUB_CONNECTION_STATUS *st; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + st = ¶ms->SC_Primary_Hub_Connection_Status; + st->State = state; + st->Connect_Timestamp = *connect_ts; + st->Disconnect_Timestamp = *disconnect_ts; + st->Error = error; + if (error_details) { + bsc_copy_str( + st->Error_Details, error_details, sizeof(st->Error_Details)); + } else { + st->Error_Details[0] = 0; + } + + return true; +} + +BACNET_SC_HUB_CONNECTION_STATUS * +Network_Port_SC_Failover_Hub_Connection_Status(uint32_t object_instance) +{ + BACNET_SC_HUB_CONNECTION_STATUS *connection = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + connection = ¶ms->SC_Failover_Hub_Connection_Status; + } + + return connection; +} + +bool Network_Port_SC_Failover_Hub_Connection_Status_Set( + uint32_t object_instance, + BACNET_SC_CONNECTION_STATE state, + BACNET_DATE_TIME *connect_ts, + BACNET_DATE_TIME *disconnect_ts, + BACNET_ERROR_CODE error, + const char *error_details) +{ + BACNET_SC_HUB_CONNECTION_STATUS *st; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + st = ¶ms->SC_Failover_Hub_Connection_Status; + st->State = state; + st->Connect_Timestamp = *connect_ts; + st->Disconnect_Timestamp = *disconnect_ts; + st->Error = error; + if (error_details) { + bsc_copy_str( + st->Error_Details, error_details, sizeof(st->Error_Details)); + } else { + st->Error_Details[0] = 0; + } + + return true; +} + +bool Network_Port_SC_Hub_Function_Enable(uint32_t object_instance) +{ + bool enable = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + enable = params->SC_Hub_Function_Enable; + } + + return enable; +} + +bool Network_Port_SC_Hub_Function_Enable_Set( + uint32_t object_instance, bool value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Hub_Function_Enable = value; + + return true; +} + +bool Network_Port_SC_Hub_Function_Enable_Dirty_Set( + uint32_t object_instance, bool value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Hub_Function_Enable_dirty = value; + + Network_Port_Changes_Pending_Set(object_instance, true); + + return true; +} + +bool Network_Port_SC_Hub_Function_Accept_URI( + uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str) +{ + bool status = false; + int idx[BACNET_SC_DIRECT_ACCEPT_URI_MAX + 1] = { 0 }; + int len; + + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params && (index < BACNET_SC_DIRECT_ACCEPT_URI_MAX)) { + len = string_splite( + params->SC_Hub_Function_Accept_URIs, ' ', idx, sizeof(idx)); + if (index < len) { + status = characterstring_init_ansi_safe( + str, params->SC_Hub_Function_Accept_URIs + idx[index], + idx[index + 1] - idx[index] - 1); + } else { + status = characterstring_init_ansi(str, ""); + } + } + + return status; +} + +bool Network_Port_SC_Hub_Function_Accept_URI_Set( + uint32_t object_instance, uint8_t index, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params || (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX)) { + return false; + } + + string_subsstr( + params->SC_Hub_Function_Accept_URIs, + sizeof(params->SC_Hub_Function_Accept_URIs), index, str); + + return true; +} + +/** + * @brief Encode a BACnetARRAY property element; a function template + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to n for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for invalid array index + */ +int Network_Port_SC_Hub_Function_Accept_URI_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = 0; + BACNET_CHARACTER_STRING uri = { 0 }; + + if (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX) { + apdu_len = BACNET_STATUS_ERROR; + } else { + if (Network_Port_SC_Hub_Function_Accept_URI( + object_instance, index, &uri)) { + apdu_len = encode_application_character_string(apdu, &uri); + } + } + + return apdu_len; +} + +bool Network_Port_SC_Hub_Function_Accept_URI_Dirty_Set( + uint32_t object_instance, uint8_t index, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params || (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX)) { + return false; + } + + string_subsstr( + params->SC_Hub_Function_Accept_URIs_dirty, + sizeof(params->SC_Hub_Function_Accept_URIs_dirty), index, str); + + return true; +} + +const char * +Network_Port_SC_Hub_Function_Accept_URIs_char(uint32_t object_instance) +{ + char *p = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + p = params->SC_Hub_Function_Accept_URIs; + } + + return p; +} + +bool Network_Port_SC_Hub_Function_Accept_URIs_Set( + uint32_t object_instance, const char *str) +{ + bool status = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + if (str) { + snprintf( + params->SC_Hub_Function_Accept_URIs, + sizeof(params->SC_Hub_Function_Accept_URIs), "%s", str); + } else { + params->SC_Hub_Function_Accept_URIs[0] = 0; + } + } + + return status; +} + +bool Network_Port_SC_Hub_Function_Binding( + uint32_t object_instance, BACNET_CHARACTER_STRING *str) +{ + bool status = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + char tmp[sizeof(params->SC_Hub_Function_Binding)]; + + if (params) { + snprintf( + tmp, sizeof(tmp), "%s:%d", + params->SC_Hub_Function_Binding + sizeof(uint16_t), + *(uint16_t *)params->SC_Hub_Function_Binding); + + status = characterstring_init_ansi(str, tmp); + } + + return status; +} + +void Network_Port_SC_Hub_Function_Binding_get( + uint32_t object_instance, uint16_t *port, char **ifname) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + *port = 0; + *ifname = NULL; + + if (params && params->SC_Hub_Function_Binding[0]) { + *port = *(uint16_t *)params->SC_Hub_Function_Binding; + *ifname = params->SC_Hub_Function_Binding + sizeof(uint16_t); + if (**ifname == 0) { + *ifname = NULL; + } + } +} + +bool Network_Port_SC_Hub_Function_Binding_Set( + uint32_t object_instance, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + uint16_t port; + char *ifname; + char tmp[BACNET_BINDING_STRING_LENGTH]; + + if (!params) { + return false; + } + + if (str && str[0]) { + bsc_copy_str(tmp, str, sizeof(tmp)); + sc_binding_parse(tmp, &port, &ifname); + *(uint16_t *)params->SC_Hub_Function_Binding = port; + if (ifname) { + bsc_copy_str( + params->SC_Hub_Function_Binding + sizeof(uint16_t), ifname, + sizeof(params->SC_Hub_Function_Binding) - sizeof(uint16_t)); + } else { + *(params->SC_Hub_Function_Binding + sizeof(uint16_t)) = 0; + } + } else { + memset( + params->SC_Hub_Function_Binding, 0, + sizeof(params->SC_Hub_Function_Binding)); + } + + return true; +} + +bool Network_Port_SC_Hub_Function_Binding_Dirty_Set( + uint32_t object_instance, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (str) { + snprintf( + params->SC_Hub_Function_Binding_dirty, + sizeof(params->SC_Hub_Function_Binding_dirty), "%s", str); + } else { + params->SC_Hub_Function_Binding_dirty[0] = 0; + } + + Network_Port_Changes_Pending_Set(object_instance, true); + + return true; +} + +BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS * +Network_Port_SC_Hub_Function_Connection_Status_Get( + uint32_t object_instance, uint8_t index) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (!params) { + return NULL; + } + + if (index >= params->SC_Hub_Function_Connection_Status_Count) { + return NULL; + } + + return ¶ms->SC_Hub_Function_Connection_Status[index]; +} + +bool Network_Port_SC_Hub_Function_Connection_Status_Add( + uint32_t object_instance, + BACNET_SC_CONNECTION_STATE state, + BACNET_DATE_TIME *connect_ts, + BACNET_DATE_TIME *disconnect_ts, + BACNET_HOST_N_PORT_DATA *peer_address, + uint8_t *peer_VMAC, + uint8_t *peer_UUID, + BACNET_ERROR_CODE error, + const char *error_details) +{ + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *st; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (params->SC_Hub_Function_Connection_Status_Count + 1 > + ARRAY_SIZE(params->SC_Hub_Function_Connection_Status)) { + return false; + } + + st = ¶ms->SC_Hub_Function_Connection_Status + [params->SC_Hub_Function_Connection_Status_Count]; + params->SC_Hub_Function_Connection_Status_Count += 1; + + st->State = state; + st->Connect_Timestamp = *connect_ts; + st->Disconnect_Timestamp = *disconnect_ts; + st->Peer_Address = *peer_address; + memcpy(st->Peer_VMAC, peer_VMAC, sizeof(st->Peer_VMAC)); + memcpy( + st->Peer_UUID.uuid.uuid128, peer_UUID, + sizeof(st->Peer_UUID.uuid.uuid128)); + st->Error = error; + if (error_details) { + bsc_copy_str( + st->Error_Details, error_details, sizeof(st->Error_Details)); + } else { + st->Error_Details[0] = 0; + } + + return true; +} + +bool Network_Port_SC_Hub_Function_Connection_Status_Delete_All( + uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Hub_Function_Connection_Status_Count = 0; + + return true; +} + +int Network_Port_SC_Hub_Function_Connection_Status_Count( + uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return 0; + } + + return params->SC_Hub_Function_Connection_Status_Count; +} + +/** + * @brief Handle a request to encode all the the entries + * @param object_instance [in] BACnet network port object instance number + * @param apdu [out] Buffer in which the APDU contents are built. + * @param max_apdu [in] Max length of the APDU buffer. + * + * @return How many bytes were encoded in the buffer, or + * BACNET_STATUS_ABORT if the response would not fit within the buffer. + */ +int Network_Port_SC_Hub_Function_Connection_Status_Encode( + uint32_t object_instance, uint8_t *apdu, int max_apdu) +{ + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *entry = NULL; + int apdu_len = 0; + unsigned index = 0; + unsigned size = 0; + + size = + Network_Port_SC_Hub_Function_Connection_Status_Count(object_instance); + for (index = 0; index < size; index++) { + entry = Network_Port_SC_Hub_Function_Connection_Status_Get( + object_instance, index); + apdu_len += bacapp_encode_SCHubFunctionConnection(NULL, entry); + } + if (apdu_len > max_apdu) { + return BACNET_STATUS_ABORT; + } + apdu_len = 0; + for (index = 0; index < size; index++) { + entry = Network_Port_SC_Hub_Function_Connection_Status_Get( + object_instance, index); + apdu_len += + bacapp_encode_SCHubFunctionConnection(&apdu[apdu_len], entry); + } + + return apdu_len; +} + +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ + +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + +bool Network_Port_SC_Direct_Connect_Initiate_Enable(uint32_t object_instance) +{ + bool enable = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + enable = params->SC_Direct_Connect_Initiate_Enable; + } + + return enable; +} + +bool Network_Port_SC_Direct_Connect_Initiate_Enable_Set( + uint32_t object_instance, bool value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Direct_Connect_Initiate_Enable = value; + + return true; +} +bool Network_Port_SC_Direct_Connect_Initiate_Enable_Dirty_Set( + uint32_t object_instance, bool value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Direct_Connect_Initiate_Enable_dirty = value; + + Network_Port_Changes_Pending_Set(object_instance, true); + + return true; +} + +bool Network_Port_SC_Direct_Connect_Accept_Enable(uint32_t object_instance) +{ + bool enable = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + enable = params->SC_Direct_Connect_Accept_Enable; + } + + return enable; +} + +bool Network_Port_SC_Direct_Connect_Accept_Enable_Set( + uint32_t object_instance, bool value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Direct_Connect_Accept_Enable = value; + + return true; +} + +bool Network_Port_SC_Direct_Connect_Accept_Enable_Dirty_Set( + uint32_t object_instance, bool value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Direct_Connect_Accept_Enable_dirty = value; + + Network_Port_Changes_Pending_Set(object_instance, true); + + return true; +} + +/* return number of urls, */ +/* indexes are fill first position of urls plus position of end string */ +static int +string_splite(const char *str, char separator, int *indexes, int length) +{ + int i; + int k = 0; + indexes[0] = 0; + for (i = 0; str[i] != 0 && k < length; i++) { + if (str[i] == separator) { + k++; + indexes[k] = i + 1; + } + } + + if (k < length) { + k++; + indexes[k] = i + 1; + } + + return k; +} + +static bool string_subsstr(char *str, int length, int index, const char *substr) +{ + int idx[BACNET_SC_DIRECT_ACCEPT_URI_MAX + 1] = { 0 }; + int len; + char *head; + char *tail_old; + char *tail_new; + + len = string_splite(str, ' ', idx, sizeof(idx)); + if (index < len) { + head = str + idx[index]; + tail_old = str + idx[index + 1] - 1; + tail_new = head + strlen(substr); + if (tail_new + strlen(tail_old) + 1 - str >= length) { + return false; + } + + memmove(tail_new, tail_old, strlen(tail_old) + 1); + memcpy(head, substr, strlen(substr)); + } else { + head = str + strlen(str); + if (head != str) { + *head = ' '; + head++; + } + if (head + strlen(substr) + 1 - str >= length) { + return false; + } + memcpy(head, substr, strlen(substr) + 1); + } + + return true; +} + +bool Network_Port_SC_Direct_Connect_Accept_URI( + uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str) +{ + bool status = false; + int idx[BACNET_SC_DIRECT_ACCEPT_URI_MAX + 1] = { 0 }; + int len; + + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params && (index < BACNET_SC_DIRECT_ACCEPT_URI_MAX)) { + len = string_splite( + params->SC_Direct_Connect_Accept_URIs, ' ', idx, sizeof(idx)); + if (index < len) { + status = characterstring_init_ansi_safe( + str, params->SC_Direct_Connect_Accept_URIs + idx[index], + idx[index + 1] - idx[index] - 1); + } else { + status = characterstring_init_ansi(str, ""); + } + } + + return status; +} + +bool Network_Port_SC_Direct_Connect_Accept_URI_Set( + uint32_t object_instance, uint8_t index, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params || (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX)) { + return false; + } + + string_subsstr( + params->SC_Direct_Connect_Accept_URIs, + sizeof(params->SC_Direct_Connect_Accept_URIs), index, str); + + return true; +} + +/** + * @brief Encode a BACnetARRAY property element; a function template + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to n for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX + */ +int Network_Port_SC_Direct_Connect_Accept_URI_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = 0; + BACNET_CHARACTER_STRING uri = { 0 }; + + if (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX) { + apdu_len = BACNET_STATUS_ERROR; + } else { + if (Network_Port_SC_Direct_Connect_Accept_URI( + object_instance, index, &uri)) { + apdu_len = encode_application_character_string(apdu, &uri); + } + } + + return apdu_len; +} + +bool Network_Port_SC_Direct_Connect_Accept_URI_Dirty_Set( + uint32_t object_instance, uint8_t index, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params || (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX)) { + return false; + } + + string_subsstr( + params->SC_Direct_Connect_Accept_URIs_dirty, + sizeof(params->SC_Direct_Connect_Accept_URIs_dirty), index, str); + + return true; +} + +char *Network_Port_SC_Direct_Connect_Accept_URIs_char(uint32_t object_instance) +{ + char *p = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + p = params->SC_Direct_Connect_Accept_URIs; + } + + return p; +} + +bool Network_Port_SC_Direct_Connect_Accept_URIs_Set( + uint32_t object_instance, const char *str) +{ + bool status = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + if (str) { + snprintf( + params->SC_Direct_Connect_Accept_URIs, + sizeof(params->SC_Direct_Connect_Accept_URIs), "%s", str); + } else { + params->SC_Direct_Connect_Accept_URIs[0] = 0; + } + } + + return status; +} + +bool Network_Port_SC_Direct_Connect_Accept_URIs_Dirty_Set( + uint32_t object_instance, const char *str) +{ + bool status = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + if (params) { + if (str) { + snprintf( + params->SC_Direct_Connect_Accept_URIs_dirty, + sizeof(params->SC_Direct_Connect_Accept_URIs_dirty), "%s", str); + } else { + params->SC_Direct_Connect_Accept_URIs_dirty[0] = 0; + } + } + + return status; +} + +bool Network_Port_SC_Direct_Connect_Binding( + uint32_t object_instance, BACNET_CHARACTER_STRING *str) +{ + bool status = false; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + char tmp[sizeof(params->SC_Direct_Connect_Binding)]; + + if (params) { + snprintf( + tmp, sizeof(tmp), "%s:%d", + params->SC_Direct_Connect_Binding + sizeof(uint16_t), + *(uint16_t *)params->SC_Direct_Connect_Binding); + + status = characterstring_init_ansi(str, tmp); + } + + return status; +} + +void Network_Port_SC_Direct_Connect_Binding_get( + uint32_t object_instance, uint16_t *port, char **ifname) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + + *port = 0; + *ifname = NULL; + if (params && params->SC_Direct_Connect_Binding[0]) { + *port = *(uint16_t *)params->SC_Direct_Connect_Binding; + *ifname = params->SC_Direct_Connect_Binding + sizeof(uint16_t); + if (**ifname == 0) { + *ifname = NULL; + } + } +} + +bool Network_Port_SC_Direct_Connect_Binding_Set( + uint32_t object_instance, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + uint16_t port; + char *ifname; + char tmp[BACNET_BINDING_STRING_LENGTH]; + + if (!params) { + return false; + } + + if (str && str[0]) { + bsc_copy_str(tmp, str, sizeof(tmp)); + sc_binding_parse(tmp, &port, &ifname); + *(uint16_t *)params->SC_Direct_Connect_Binding = port; + if (ifname) { + bsc_copy_str( + params->SC_Direct_Connect_Binding + sizeof(uint16_t), ifname, + sizeof(params->SC_Direct_Connect_Binding) - sizeof(uint16_t)); + } else { + *(params->SC_Direct_Connect_Binding + sizeof(uint16_t)) = 0; + } + } else { + memset( + params->SC_Direct_Connect_Binding, 0, + sizeof(params->SC_Direct_Connect_Binding)); + } + + return true; +} + +bool Network_Port_SC_Direct_Connect_Binding_Dirty_Set( + uint32_t object_instance, const char *str) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (str) { + snprintf( + params->SC_Direct_Connect_Binding_dirty, + sizeof(params->SC_Direct_Connect_Binding_dirty), "%s", str); + } else { + params->SC_Direct_Connect_Binding_dirty[0] = 0; + } + + Network_Port_Changes_Pending_Set(object_instance, true); + + return true; +} + +BACNET_SC_DIRECT_CONNECTION_STATUS * +Network_Port_SC_Direct_Connect_Connection_Status_Get( + uint32_t object_instance, uint8_t index) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return NULL; + } + + if (index >= params->SC_Direct_Connect_Connection_Status_Count) { + return NULL; + } + + return ¶ms->SC_Direct_Connect_Connection_Status[index]; +} + +bool Network_Port_SC_Direct_Connect_Connection_Status_Add( + uint32_t object_instance, + const char *uri, + BACNET_SC_CONNECTION_STATE state, + BACNET_DATE_TIME *connect_ts, + BACNET_DATE_TIME *disconnect_ts, + BACNET_HOST_N_PORT_DATA *peer_address, + uint8_t *peer_VMAC, + uint8_t *peer_UUID, + BACNET_ERROR_CODE error, + const char *error_details) +{ + BACNET_SC_DIRECT_CONNECTION_STATUS *st; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (params->SC_Direct_Connect_Connection_Status_Count + 1 > + ARRAY_SIZE(params->SC_Direct_Connect_Connection_Status)) { + return false; + } + + st = ¶ms->SC_Direct_Connect_Connection_Status + [params->SC_Direct_Connect_Connection_Status_Count]; + params->SC_Direct_Connect_Connection_Status_Count += 1; + + if (uri) { + bsc_copy_str(st->URI, uri, sizeof(st->URI)); + } else { + st->URI[0] = 0; + } + st->State = state; + st->Connect_Timestamp = *connect_ts; + st->Disconnect_Timestamp = *disconnect_ts; + + memcpy(&st->Peer_Address, peer_address, sizeof(st->Peer_Address)); + memcpy(st->Peer_VMAC, peer_VMAC, sizeof(st->Peer_VMAC)); + memcpy( + st->Peer_UUID.uuid.uuid128, peer_UUID, + sizeof(st->Peer_UUID.uuid.uuid128)); + st->Error = error; + if (error_details) { + bsc_copy_str( + st->Error_Details, error_details, sizeof(st->Error_Details)); + } else { + st->Error_Details[0] = 0; + } + + return true; +} + +bool Network_Port_SC_Direct_Connect_Connection_Status_Delete_All( + uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Direct_Connect_Connection_Status_Count = 0; + + return true; +} + +int Network_Port_SC_Direct_Connect_Connection_Status_Count( + uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return 0; + } + + return params->SC_Direct_Connect_Connection_Status_Count; +} + +/** + * @brief Handle a request to encode all the the entries + * @param object_instance [in] BACnet network port object instance number + * @param apdu [out] Buffer in which the APDU contents are built. + * @param max_apdu [in] Max length of the APDU buffer. + * + * @return How many bytes were encoded in the buffer, or + * BACNET_STATUS_ABORT if the response would not fit within the buffer. + */ +int Network_Port_SC_Direct_Connect_Connection_Status_Encode( + uint32_t object_instance, uint8_t *apdu, int max_apdu) +{ + BACNET_SC_DIRECT_CONNECTION_STATUS *entry = NULL; + int apdu_len = 0; + unsigned index = 0; + unsigned size = 0; + + size = + Network_Port_SC_Direct_Connect_Connection_Status_Count(object_instance); + for (index = 0; index < size; index++) { + entry = Network_Port_SC_Direct_Connect_Connection_Status_Get( + object_instance, index); + apdu_len += bacapp_encode_SCDirectConnection(NULL, entry); + } + if (apdu_len > max_apdu) { + return BACNET_STATUS_ABORT; + } + apdu_len = 0; + for (index = 0; index < size; index++) { + entry = Network_Port_SC_Direct_Connect_Connection_Status_Get( + object_instance, index); + apdu_len += bacapp_encode_SCDirectConnection(&apdu[apdu_len], entry); + } + + return apdu_len; +} +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + +BACNET_SC_FAILED_CONNECTION_REQUEST * +Network_Port_SC_Failed_Connection_Requests_Get( + uint32_t object_instance, uint8_t index) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return NULL; + } + + if (index >= params->SC_Failed_Connection_Requests_Count) { + return NULL; + } + + return ¶ms->SC_Failed_Connection_Requests[index]; +} + +bool Network_Port_SC_Failed_Connection_Requests_Add( + uint32_t object_instance, + BACNET_DATE_TIME *ts, + BACNET_HOST_N_PORT_DATA *peer_address, + uint8_t *peer_VMAC, + uint8_t *peer_UUID, + BACNET_ERROR_CODE error, + const char *error_details) +{ + BACNET_SC_FAILED_CONNECTION_REQUEST *entry; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + if (params->SC_Failed_Connection_Requests_Count + 1 > + ARRAY_SIZE(params->SC_Failed_Connection_Requests)) { + return false; + } + + entry = ¶ms->SC_Failed_Connection_Requests + [params->SC_Failed_Connection_Requests_Count]; + params->SC_Failed_Connection_Requests_Count += 1; + + entry->Timestamp = *ts; + memcpy(&entry->Peer_Address, peer_address, sizeof(entry->Peer_Address)); + memcpy(entry->Peer_VMAC, peer_VMAC, sizeof(entry->Peer_VMAC)); + memcpy( + entry->Peer_UUID.uuid.uuid128, peer_UUID, + sizeof(entry->Peer_UUID.uuid.uuid128)); + entry->Error = error; + if (error_details) { + bsc_copy_str( + entry->Error_Details, error_details, sizeof(entry->Error_Details)); + } else { + entry->Error_Details[0] = 0; + } + + return true; +} + +bool Network_Port_SC_Failed_Connection_Requests_Delete_All( + uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->SC_Failed_Connection_Requests_Count = 0; + + return true; +} + +int Network_Port_SC_Failed_Connection_Requests_Count(uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return 0; + } + + return params->SC_Failed_Connection_Requests_Count; +} + +/** + * @brief Handle a request to encode all the the entries + * @param object_instance [in] BACnet network port object instance number + * @param apdu [out] Buffer in which the APDU contents are built. + * @param max_apdu [in] Max length of the APDU buffer. + * @return How many bytes were encoded in the buffer, or + * BACNET_STATUS_ABORT if the response would not fit within the buffer. + */ +int Network_Port_SC_Failed_Connection_Requests_Encode( + uint32_t object_instance, uint8_t *apdu, int max_apdu) +{ + BACNET_SC_FAILED_CONNECTION_REQUEST *entry = NULL; + int apdu_len = 0; + unsigned index = 0; + unsigned size = 0; + + size = Network_Port_SC_Failed_Connection_Requests_Count(object_instance); + for (index = 0; index < size; index++) { + entry = Network_Port_SC_Failed_Connection_Requests_Get( + object_instance, index); + apdu_len += bacapp_encode_SCFailedConnectionRequest(NULL, entry); + } + if (apdu_len > max_apdu) { + return BACNET_STATUS_ABORT; + } + apdu_len = 0; + for (index = 0; index < size; index++) { + entry = Network_Port_SC_Failed_Connection_Requests_Get( + object_instance, index); + apdu_len += + bacapp_encode_SCFailedConnectionRequest(&apdu[apdu_len], entry); + } + + return apdu_len; +} + +uint32_t Network_Port_Certificate_Key_File(uint32_t object_instance) +{ + uint32_t id = 0; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return 0; + } + + id = params->Certificate_Key_File; + + return id; +} + +bool Network_Port_Certificate_Key_File_Set( + uint32_t object_instance, uint32_t value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + params->Certificate_Key_File = value; + + return true; +} + +const BACNET_UUID *Network_Port_SC_Local_UUID(uint32_t object_instance) +{ + const BACNET_UUID *uuid = NULL; + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return NULL; + } + + uuid = ¶ms->Local_UUID; + + return uuid; +} + +bool Network_Port_SC_Local_UUID_Set( + uint32_t object_instance, BACNET_UUID *value) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return false; + } + + memcpy(¶ms->Local_UUID, value, sizeof(*value)); + + return true; +} + +void Network_Port_SC_Pending_Params_Apply(uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + if (!params) { + return; + } + + params->Max_BVLC_Length_Accepted = params->Max_BVLC_Length_Accepted_dirty; + params->Max_NPDU_Length_Accepted = params->Max_NPDU_Length_Accepted_dirty; + params->SC_Minimum_Reconnect_Time = params->SC_Minimum_Reconnect_Time_dirty; + params->SC_Maximum_Reconnect_Time = params->SC_Maximum_Reconnect_Time_dirty; + params->SC_Connect_Wait_Timeout = params->SC_Connect_Wait_Timeout_dirty; + params->SC_Disconnect_Wait_Timeout = + params->SC_Disconnect_Wait_Timeout_dirty; + params->SC_Heartbeat_Timeout = params->SC_Heartbeat_Timeout_dirty; + +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + memcpy( + params->SC_Primary_Hub_URI, params->SC_Primary_Hub_URI_dirty, + sizeof(params->SC_Primary_Hub_URI)); + memcpy( + params->SC_Failover_Hub_URI, params->SC_Failover_Hub_URI_dirty, + sizeof(params->SC_Failover_Hub_URI)); + + params->SC_Hub_Function_Enable = params->SC_Hub_Function_Enable_dirty; + + memcpy( + params->SC_Hub_Function_Accept_URIs, + params->SC_Hub_Function_Accept_URIs_dirty, + sizeof(params->SC_Hub_Function_Accept_URIs)); + + Network_Port_SC_Hub_Function_Binding_Set( + object_instance, params->SC_Hub_Function_Binding_dirty); +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ + +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + params->SC_Direct_Connect_Initiate_Enable = + params->SC_Direct_Connect_Initiate_Enable_dirty; + params->SC_Direct_Connect_Accept_Enable = + params->SC_Direct_Connect_Accept_Enable_dirty; + + memcpy( + params->SC_Direct_Connect_Accept_URIs, + params->SC_Direct_Connect_Accept_URIs_dirty, + sizeof(params->SC_Direct_Connect_Accept_URIs)); + + Network_Port_SC_Direct_Connect_Binding_Set( + object_instance, params->SC_Direct_Connect_Binding_dirty); +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ +} + +void Network_Port_SC_Pending_Params_Discard(uint32_t object_instance) +{ + BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance); + uint16_t port; + char *ifname; + + if (!params) { + return; + } + + (void)port; + (void)ifname; + + params->Max_BVLC_Length_Accepted_dirty = params->Max_BVLC_Length_Accepted; + params->Max_NPDU_Length_Accepted_dirty = params->Max_NPDU_Length_Accepted; + params->SC_Minimum_Reconnect_Time_dirty = params->SC_Minimum_Reconnect_Time; + params->SC_Maximum_Reconnect_Time_dirty = params->SC_Maximum_Reconnect_Time; + params->SC_Connect_Wait_Timeout_dirty = params->SC_Connect_Wait_Timeout; + params->SC_Disconnect_Wait_Timeout_dirty = + params->SC_Disconnect_Wait_Timeout; + params->SC_Heartbeat_Timeout_dirty = params->SC_Heartbeat_Timeout; + +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + memcpy( + params->SC_Primary_Hub_URI_dirty, params->SC_Primary_Hub_URI, + sizeof(params->SC_Primary_Hub_URI_dirty)); + memcpy( + params->SC_Failover_Hub_URI_dirty, params->SC_Failover_Hub_URI, + sizeof(params->SC_Failover_Hub_URI_dirty)); + + params->SC_Hub_Function_Enable_dirty = params->SC_Hub_Function_Enable; + + memcpy( + params->SC_Hub_Function_Accept_URIs_dirty, + params->SC_Hub_Function_Accept_URIs, + sizeof(params->SC_Hub_Function_Accept_URIs)); + + Network_Port_SC_Hub_Function_Binding_get(object_instance, &port, &ifname); + snprintf( + params->SC_Hub_Function_Binding_dirty, + sizeof(params->SC_Hub_Function_Binding_dirty), "%s:%d", ifname, port); +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ + +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + params->SC_Direct_Connect_Initiate_Enable_dirty = + params->SC_Direct_Connect_Initiate_Enable; + params->SC_Direct_Connect_Accept_Enable_dirty = + params->SC_Direct_Connect_Accept_Enable; + + memcpy( + params->SC_Direct_Connect_Accept_URIs_dirty, + params->SC_Direct_Connect_Accept_URIs, + sizeof(params->SC_Direct_Connect_Accept_URIs)); + + Network_Port_SC_Direct_Connect_Binding_get(object_instance, &port, &ifname); + snprintf( + params->SC_Direct_Connect_Binding_dirty, + sizeof(params->SC_Direct_Connect_Binding_dirty), "%s:%d", ifname, port); +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ +} diff --git a/src/bacnet/basic/object/sc_netport.h b/src/bacnet/basic/object/sc_netport.h new file mode 100644 index 00000000..fdeb1781 --- /dev/null +++ b/src/bacnet/basic/object/sc_netport.h @@ -0,0 +1,415 @@ +/** + * @file + * @brief Helper Network port objects to implement secure connect attributes + * @author Mikhail Antropov + * @date June 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_SC_NETPORT_H +#define BACNET_SC_NETPORT_H +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/basic/sys/keylist.h" +#include "bacnet/bacapp.h" +#include "bacnet/apdu.h" +#include "bacnet/secure_connect.h" +#include "bacnet/datalink/bsc/bsc-conf.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" + +/* BACnet file instance numbers */ +#ifndef BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE +#define BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE \ + BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE +#endif +#ifndef BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE +#define BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE \ + BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE +#endif +#ifndef BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE +#define BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE \ + BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE +#endif +#ifndef BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE +#define BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE \ + BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define BACNET_SC_HUB_URI_MAX 2 +#ifndef BACNET_SC_DIRECT_ACCEPT_URI_MAX +#define BACNET_SC_DIRECT_ACCEPT_URI_MAX 2 +#endif + +#define BACNET_ISSUER_CERT_FILE_MAX 2 +#define BACNET_BINDING_STRING_LENGTH 80 + +#define BACNET_SC_BINDING_SEPARATOR ':' + +/* */ +/* getter / setter */ +/* */ + +BACNET_STACK_EXPORT +BACNET_UNSIGNED_INTEGER +Network_Port_Max_BVLC_Length_Accepted(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_Max_BVLC_Length_Accepted_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); +bool Network_Port_Max_BVLC_Length_Accepted_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); + +BACNET_STACK_EXPORT +BACNET_UNSIGNED_INTEGER +Network_Port_Max_NPDU_Length_Accepted(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_Max_NPDU_Length_Accepted_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); +bool Network_Port_Max_NPDU_Length_Accepted_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); + +BACNET_STACK_EXPORT +bool Network_Port_SC_Primary_Hub_URI( + uint32_t object_instance, BACNET_CHARACTER_STRING *str); +const char *Network_Port_SC_Primary_Hub_URI_char(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Primary_Hub_URI_Set( + uint32_t object_instance, const char *str); +bool Network_Port_SC_Primary_Hub_URI_Dirty_Set( + uint32_t object_instance, const char *str); + +BACNET_STACK_EXPORT +bool Network_Port_SC_Failover_Hub_URI( + uint32_t object_instance, BACNET_CHARACTER_STRING *str); +const char *Network_Port_SC_Failover_Hub_URI_char(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Failover_Hub_URI_Set( + uint32_t object_instance, const char *str); +bool Network_Port_SC_Failover_Hub_URI_Dirty_Set( + uint32_t object_instance, const char *str); + +BACNET_STACK_EXPORT +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Minimum_Reconnect_Time(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Minimum_Reconnect_Time_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); +bool Network_Port_SC_Minimum_Reconnect_Time_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); + +BACNET_STACK_EXPORT +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Maximum_Reconnect_Time(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Maximum_Reconnect_Time_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); +bool Network_Port_SC_Maximum_Reconnect_Time_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); + +BACNET_STACK_EXPORT +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Connect_Wait_Timeout(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Connect_Wait_Timeout_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); +bool Network_Port_SC_Connect_Wait_Timeout_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); + +BACNET_STACK_EXPORT +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Disconnect_Wait_Timeout(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Disconnect_Wait_Timeout_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); +bool Network_Port_SC_Disconnect_Wait_Timeout_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); + +BACNET_STACK_EXPORT +BACNET_UNSIGNED_INTEGER +Network_Port_SC_Heartbeat_Timeout(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Heartbeat_Timeout_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); +bool Network_Port_SC_Heartbeat_Timeout_Dirty_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_CONNECTOR_STATE +Network_Port_SC_Hub_Connector_State(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Connector_State_Set( + uint32_t object_instance, BACNET_SC_HUB_CONNECTOR_STATE value); + +BACNET_STACK_EXPORT +uint32_t Network_Port_Operational_Certificate_File(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_Operational_Certificate_File_Set( + uint32_t object_instance, uint32_t value); + +BACNET_STACK_EXPORT +uint32_t +Network_Port_Issuer_Certificate_File(uint32_t object_instance, uint8_t index); +BACNET_STACK_EXPORT +bool Network_Port_Issuer_Certificate_File_Set( + uint32_t object_instance, uint8_t index, uint32_t value); +BACNET_STACK_EXPORT +int Network_Port_Issuer_Certificate_File_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu); + +BACNET_STACK_EXPORT +uint32_t +Network_Port_Certificate_Signing_Request_File(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_Certificate_Signing_Request_File_Set( + uint32_t object_instance, uint32_t value); + +BACNET_STACK_EXPORT +BACNET_ROUTER_ENTRY *Network_Port_Routing_Table_Find( + uint32_t object_instance, uint16_t Network_Number); +BACNET_STACK_EXPORT +BACNET_ROUTER_ENTRY * +Network_Port_Routing_Table_Get(uint32_t object_instance, uint8_t index); +BACNET_STACK_EXPORT +bool Network_Port_Routing_Table_Add( + uint32_t object_instance, + uint16_t network_number, + uint8_t *mac, + uint8_t mac_len, + uint8_t status, + uint8_t performance_index); +BACNET_STACK_EXPORT +bool Network_Port_Routing_Table_Delete( + uint32_t object_instance, uint16_t Network_Number); +BACNET_STACK_EXPORT +bool Network_Port_Routing_Table_Delete_All(uint32_t object_instance); +BACNET_STACK_EXPORT +uint8_t Network_Port_Routing_Table_Count(uint32_t object_instance); +BACNET_STACK_EXPORT +int Network_Port_Routing_Table_Encode( + uint32_t object_instance, uint8_t *apdu, int max_apdu); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_CONNECTION_STATUS * +Network_Port_SC_Primary_Hub_Connection_Status(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Primary_Hub_Connection_Status_Set( + uint32_t object_instance, + BACNET_SC_CONNECTION_STATE state, + BACNET_DATE_TIME *connect_ts, + BACNET_DATE_TIME *disconnect_ts, + BACNET_ERROR_CODE error, + const char *error_details); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_CONNECTION_STATUS * +Network_Port_SC_Failover_Hub_Connection_Status(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Failover_Hub_Connection_Status_Set( + uint32_t object_instance, + BACNET_SC_CONNECTION_STATE state, + BACNET_DATE_TIME *connect_ts, + BACNET_DATE_TIME *disconnect_ts, + BACNET_ERROR_CODE error, + const char *error_details); + +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Function_Enable(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Function_Enable_Set( + uint32_t object_instance, bool value); +bool Network_Port_SC_Hub_Function_Enable_Dirty_Set( + uint32_t object_instance, bool value); + +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Function_Accept_URI( + uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str); +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Function_Accept_URI_Set( + uint32_t object_instance, uint8_t index, const char *str); +BACNET_STACK_EXPORT +int Network_Port_SC_Hub_Function_Accept_URI_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu); + +bool Network_Port_SC_Hub_Function_Accept_URI_Dirty_Set( + uint32_t object_instance, uint8_t index, const char *str); +const char * +Network_Port_SC_Hub_Function_Accept_URIs_char(uint32_t object_instance); +bool Network_Port_SC_Hub_Function_Accept_URIs_Set( + uint32_t object_instance, const char *str); + +/* + Internal representation of the SC_Hub_Function_Binding value is a + struct { + uint16 port; + char interface_name[BACNET_BINDING_STRING_LENGTH - sizeof(uint16)]; + } + External representation is string e.q "interface_name:port_number". + + The Network_Port_SC_Hub_Function_Binding() and + the Network_Port_SC_Hub_Function_Binding_Set() do transform from/to. + + The Network_Port_SC_Hub_Function_Binding_Dirty sets value like + external representation. +*/ +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Function_Binding( + uint32_t object_instance, BACNET_CHARACTER_STRING *str); +void Network_Port_SC_Hub_Function_Binding_get( + uint32_t object_instance, uint16_t *port, char **ifname); +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Function_Binding_Set( + uint32_t object_instance, const char *str); +bool Network_Port_SC_Hub_Function_Binding_Dirty_Set( + uint32_t object_instance, const char *str); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS * +Network_Port_SC_Hub_Function_Connection_Status_Get( + uint32_t object_instance, uint8_t index); +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Function_Connection_Status_Add( + uint32_t object_instance, + BACNET_SC_CONNECTION_STATE state, + BACNET_DATE_TIME *connect_ts, + BACNET_DATE_TIME *disconnect_ts, + BACNET_HOST_N_PORT_DATA *peer_address, + uint8_t *peer_VMAC, + uint8_t *peer_UUID, + BACNET_ERROR_CODE error, + const char *error_details); +BACNET_STACK_EXPORT +bool Network_Port_SC_Hub_Function_Connection_Status_Delete_All( + uint32_t object_instance); +BACNET_STACK_EXPORT +int Network_Port_SC_Hub_Function_Connection_Status_Count( + uint32_t object_instance); +BACNET_STACK_EXPORT +int Network_Port_SC_Hub_Function_Connection_Status_Encode( + uint32_t object_instance, uint8_t *apdu, int max_apdu); + +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Initiate_Enable(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Initiate_Enable_Set( + uint32_t object_instance, bool value); +bool Network_Port_SC_Direct_Connect_Initiate_Enable_Dirty_Set( + uint32_t object_instance, bool value); + +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Accept_Enable(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Accept_Enable_Set( + uint32_t object_instance, bool value); +bool Network_Port_SC_Direct_Connect_Accept_Enable_Dirty_Set( + uint32_t object_instance, bool value); + +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Accept_URI( + uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str); +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Accept_URI_Set( + uint32_t object_instance, uint8_t index, const char *str); +BACNET_STACK_EXPORT +int Network_Port_SC_Direct_Connect_Accept_URI_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu); + +bool Network_Port_SC_Direct_Connect_Accept_URI_Dirty_Set( + uint32_t object_instance, uint8_t index, const char *str); +char *Network_Port_SC_Direct_Connect_Accept_URIs_char(uint32_t object_instance); +bool Network_Port_SC_Direct_Connect_Accept_URIs_Set( + uint32_t object_instance, const char *str); + +/* + Internal representation of the SC_Direct_Connect_Binding value is a + struct { + uint16 port; + char interface_name[BACNET_BINDING_STRING_LENGTH - sizeof(uint16)]; + } + External representation is string e.q "interface_name:port_number". + + The Network_Port_SC_Direct_Connect_Binding() and + the Network_Port_SC_Direct_Connect_Binding_Set() do transform from/to. + + The Network_Port_SC_Direct_Connect_Binding_Dirty sets value like + external representation. +*/ +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Binding( + uint32_t object_instance, BACNET_CHARACTER_STRING *str); +void Network_Port_SC_Direct_Connect_Binding_get( + uint32_t object_instance, uint16_t *port, char **ifname); +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Binding_Set( + uint32_t object_instance, const char *str); +bool Network_Port_SC_Direct_Connect_Binding_Dirty_Set( + uint32_t object_instance, const char *str); + +BACNET_STACK_EXPORT +BACNET_SC_DIRECT_CONNECTION_STATUS * +Network_Port_SC_Direct_Connect_Connection_Status_Get( + uint32_t object_instance, uint8_t index); +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Connection_Status_Add( + uint32_t object_instance, + const char *uri, + BACNET_SC_CONNECTION_STATE state, + BACNET_DATE_TIME *connect_ts, + BACNET_DATE_TIME *disconnect_ts, + BACNET_HOST_N_PORT_DATA *peer_address, + uint8_t *peer_VMAC, + uint8_t *peer_UUID, + BACNET_ERROR_CODE error, + const char *error_details); +BACNET_STACK_EXPORT +bool Network_Port_SC_Direct_Connect_Connection_Status_Delete_All( + uint32_t object_instance); +BACNET_STACK_EXPORT +int Network_Port_SC_Direct_Connect_Connection_Status_Count( + uint32_t object_instance); +BACNET_STACK_EXPORT +int Network_Port_SC_Direct_Connect_Connection_Status_Encode( + uint32_t object_instance, uint8_t *apdu, int max_apdu); + +BACNET_STACK_EXPORT +BACNET_SC_FAILED_CONNECTION_REQUEST * +Network_Port_SC_Failed_Connection_Requests_Get( + uint32_t object_instance, uint8_t index); +BACNET_STACK_EXPORT +bool Network_Port_SC_Failed_Connection_Requests_Add( + uint32_t object_instance, + BACNET_DATE_TIME *ts, + BACNET_HOST_N_PORT_DATA *peer_address, + uint8_t *peer_VMAC, + uint8_t *peer_UUID, + BACNET_ERROR_CODE error, + const char *error_details); +BACNET_STACK_EXPORT +bool Network_Port_SC_Failed_Connection_Requests_Delete_All( + uint32_t object_instance); +BACNET_STACK_EXPORT +int Network_Port_SC_Failed_Connection_Requests_Count(uint32_t object_instance); +BACNET_STACK_EXPORT +int Network_Port_SC_Failed_Connection_Requests_Encode( + uint32_t object_instance, uint8_t *apdu, int max_apdu); + +BACNET_STACK_EXPORT +uint32_t Network_Port_Certificate_Key_File(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_Certificate_Key_File_Set( + uint32_t object_instance, uint32_t value); + +BACNET_STACK_EXPORT +const BACNET_UUID *Network_Port_SC_Local_UUID(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Network_Port_SC_Local_UUID_Set( + uint32_t object_instance, BACNET_UUID *value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/src/bacnet/config.h b/src/bacnet/config.h index 68fe8948..8cf3f215 100644 --- a/src/bacnet/config.h +++ b/src/bacnet/config.h @@ -31,6 +31,7 @@ #define BACDL_MSTP #define BACDL_BIP #define BACDL_BIP6 +#define BACDL_BSC #endif #if defined(BACDL_ETHERNET) @@ -65,6 +66,13 @@ #define BACDL_SOME_DATALINK_ENABLED 1 #endif +#if defined(BACDL_BSC) +#if defined(BACDL_SOME_DATALINK_ENABLED) +#define BACDL_MULTIPLE 1 +#endif +#define BACDL_SOME_DATALINK_ENABLED 1 +#endif + #if defined(BACDL_CUSTOM) #if defined(BACDL_SOME_DATALINK_ENABLED) #define BACDL_MULTIPLE 1 @@ -144,6 +152,27 @@ #endif #endif +#if defined(BACDL_BSC) +#ifndef SC_NETPORT_BVLC_MAX +#define SC_NETPORT_BVLC_MAX 1500 +#endif +#ifndef SC_NETPORT_NPDU_MAX +#define SC_NETPORT_NPDU_MAX 1500 +#endif +#ifndef SC_NETPORT_CONNECT_TIMEOUT +#define SC_NETPORT_CONNECT_TIMEOUT 5 +#endif +#ifndef SC_NETPORT_HEARTBEAT_TIMEOUT +#define SC_NETPORT_HEARTBEAT_TIMEOUT 60 +#endif +#ifndef SC_NETPORT_DISCONNECT_TIMEOUT +#define SC_NETPORT_DISCONNECT_TIMEOUT 150 +#endif +#ifndef SC_NETPORT_RECONNECT_TIME +#define SC_NETPORT_RECONNECT_TIME 2 +#endif +#endif + /* for confirmed messages, this is the number of transactions */ /* that we hold in a queue waiting for timeout. */ /* Configure to zero if you don't want any confirmed messages */ @@ -205,6 +234,7 @@ defined(BACAPP_SHED_LEVEL) || \ defined(BACAPP_ACCESS_RULE) || \ defined(BACAPP_CHANNEL_VALUE) || \ + defined(BACAPP_SECURE_CONNECT) || \ defined(BACAPP_TYPES_EXTRA)) #define BACAPP_ALL #endif @@ -253,6 +283,7 @@ #define BACAPP_SHED_LEVEL #define BACAPP_ACCESS_RULE #define BACAPP_CHANNEL_VALUE +#define BACAPP_SECURE_CONNECT #endif /* clang-format off */ @@ -270,6 +301,7 @@ defined(BACAPP_DEVICE_OBJECT_REFERENCE) || \ defined(BACAPP_OBJECT_PROPERTY_REFERENCE) || \ defined(BACAPP_DESTINATION) || \ + defined(BACAPP_SECURE_CONNECT) || \ defined(BACAPP_BDT_ENTRY) || \ defined(BACAPP_FDT_ENTRY) || \ defined(BACAPP_ACTION_COMMAND) || \ diff --git a/src/bacnet/datalink/bsc/README.md b/src/bacnet/datalink/bsc/README.md new file mode 100644 index 00000000..e001939d --- /dev/null +++ b/src/bacnet/datalink/bsc/README.md @@ -0,0 +1,54 @@ +This readme explains how to enable support of BACNet Secure Connect datalink +(ANNEX AB – BACnet Secure Connect in standard), how to set up the building +environment and clarifies some important moments regarding implementation +of that functionality. + +By default the support of that functionality is turned off, to enable it +you need to set option BACDL_BSC=ON in CMakeLists.txt if cmake build system +is used or set BACDL_BSC=1 define in a case if using makefiles. + +BACNet/SC standard uses websockets as a transport layer, so Windows/Linux/BSD +implementations use libwebsocket library. Implementation for Zephyr uses +native websocket API for the client side and mongoose library for the +server side. Websocket layer which is built on the top of libwebsockets +uses 1 service thread per websocket server instance and 1 service thread +per 1 websocket client instance. As a result, libwebsocket must be built +with LWS_MAX_SMP > 1, otherwise rarely crashes may ocure in the application +which uses bacnet stack library. Recommended value for that define is 32. +You should note, that by default libwebsocket is built with LWS_MAX_SMP=1, +so a packet manager like vcpkg and apt can have that lib built with +LWS_MAX_SMP=1, which can lead to unstable work. It's better to build +libwebsockets from recent stable sources for your platform, corresponded +examples of how to do that you can find in .github/workflows/bsc-tests-platform.yml, +(for example bsc-tests-linux.yml). + +In order to build bacnet stack library for linux, user needs to install +libconfig-dev, libcap-dev and libssl-dev to the system. Most easiest way to do +that is to use Advanced Packaging Tool (APT), check bsc-test-linux.yml. + +For MacOSX build user must install brew packet manager (https://brew.sh), +then install openssl using brew then build libwebsocket, check +.github/workflows/bsc-tests-macosx.yml. + +Windows build may be a challenge because libwebsocket depends on libpthreads +which not so easy to find and build on windows, refer to the libwebsocket +build instruction https://libwebsockets.org/lws-api-doc-master/html/md_README_8build.html. +Using of vcpkg packet manager can save a lot of time, but you must ensure that +installed libwebsocket library is compiled with LWS_MAX_SMP > 1. +A good example how to use vcpkg packet manager with custom build of +libwebsocket can be found in .github/workflows/bsc-tests-windows.yml. + +One moment is still needed to be improved. The current implementation processes +dataflow between BACNET/SC nodes ignoring max_bvlc_size and max_npdu_size +params in connect request and connect accept packets as that is not +explicitly explained in standard. As a result if a node which has max_npdu_size +less than remote peer can drop received packet if it does not fit into it's internal +buffer which depends on BVLC_SC_NPDU_SIZE_CONF parameter. In opposite, a node +can send a PDU more than max_npdu_size which may also lead to the drop of PDU +on remote peer side. + +The current implementation does not support Certificate_Signing_Request_File +property of BACNET/SC netport and properties Operational_Certificate_File +and Issuer_Certificate_Files related to certificates are readonly and can't +be changed remotely. So, management of device certificates is out of the scope +of current BACNet/SC implementation. diff --git a/src/bacnet/datalink/bsc/bsc-conf.h b/src/bacnet/datalink/bsc/bsc-conf.h new file mode 100644 index 00000000..cf0b33ea --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-conf.h @@ -0,0 +1,169 @@ +/** + * @file + * @brief Configuration file of BACNet/SC datalink. + * @author Kirill Neznamov + * @date August 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_CONF_H +#define BACNET_DATALINK_BSC_CONF_H +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bvlc-sc.h" + +#if !defined(BACDL_BSC) +#define BSC_CONF_TX_PRE 0 +#else +#ifndef bsd +#define bsd 1 +#endif +#ifndef linux +#define linux 2 +#endif +#ifndef win32 +#define win32 3 +#endif +#if BACNET_PORT == bsd || BACNET_PORT == linux || BACNET_PORT == win32 +#include +#define BSC_CONF_TX_PRE LWS_PRE +#else +#define BSC_CONF_TX_PRE 0 +#endif +#endif + +#ifndef BSC_CONF_HUB_CONNECTORS_NUM +#define BSC_CONF_HUB_CONNECTORS_NUM 1 +#endif + +#ifndef BSC_CONF_HUB_FUNCTIONS_NUM +#define BSC_CONF_HUB_FUNCTIONS_NUM 1 +#endif + +#ifndef BSC_CONF_NODE_SWITCHES_NUM +#define BSC_CONF_NODE_SWITCHES_NUM 1 +#endif + +#ifndef BSC_CONF_NODES_NUM +#define BSC_CONF_NODES_NUM 1 +#endif + +#ifndef BVLC_SC_NPDU_SIZE_CONF +/* 16 bytes is sum of all sizes of all static (non variable) + fields of header of BVLC message */ +#define BVLC_SC_NPDU_SIZE_CONF ((MAX_PDU) + 16) +#endif + +#ifndef BSC_CONF_WEBSOCKET_RX_BUFFER_LEN +#define BSC_CONF_WEBSOCKET_RX_BUFFER_LEN BVLC_SC_NPDU_SIZE_CONF +#endif + +/* THIS should not be changed, most of BACNet/SC devices must have */ +/* hub connector, it uses 2 connections */ +#ifndef BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM +#define BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM (BSC_CONF_HUB_CONNECTORS_NUM * 2) +#endif + +#ifndef BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM +#define BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM (BSC_CONF_HUB_FUNCTIONS_NUM * 10) +#endif + +#ifndef BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM +#define BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM 10 +#endif + +/* Total amount of client(initiator) webosocket connections */ +#ifndef BSC_CONF_CLIENT_CONNECTIONS_NUM +#define BSC_CONF_CLIENT_CONNECTIONS_NUM \ + (BSC_CONF_HUB_CONNECTOR_CONNECTIONS_NUM + \ + BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM * BSC_CONF_NODE_SWITCHES_NUM) +#endif + +#ifndef BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM +#define BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM \ + (BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM) +#endif + +#ifndef BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM +#define BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM \ + (BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM * BSC_CONF_NODE_SWITCHES_NUM) +#endif + +#define BSC_CONF_SOCKET_TX_BUFFERED_PACKET_NUM 2 +#define BSC_CONF_DATALINK_BUFFERED_PACKET_NUM 10 + +#define BSC_CONF_SOCK_RX_BUFFER_SIZE BVLC_SC_NPDU_SIZE_CONF + +/* 2 bytes is a prefix containing BVLC message length. + BSC_CONF_TX_PRE - some reserved bytes before actual payload. + Some libs like libwebsocket requires some bytes to be reserved + before actual payload for sending, so BSC_CONF_TX_PRE is used for + that purpose (it allows to avoid copying of payload and + buffer reallocation) +*/ + +#define BSC_CONF_SOCK_TX_BUFFER_SIZE \ + ((BVLC_SC_NPDU_SIZE_CONF + 2 + BSC_CONF_TX_PRE) * \ + BSC_CONF_SOCKET_TX_BUFFERED_PACKET_NUM) + +/* datalink RX buffer size is always rounded to next power of two */ +/* so if final buffer size is 1628 it will be rounded to 2048 */ + +#define BSC_CONF_DATALINK_RX_BUFFER_SIZE \ + (BVLC_SC_NPDU_SIZE_CONF * BSC_CONF_DATALINK_BUFFERED_PACKET_NUM) + +#ifndef BSC_CONF_WSURL_MAX_LEN +#define BSC_CONF_WSURL_MAX_LEN 128 +#endif + +#ifndef BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN +#define BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN 128 +#endif + +#ifndef BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK +#define BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK \ + BSC_CONF_WSURL_MAX_LEN +#endif + +#ifndef BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK +#define BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK \ + (BSC_CONF_SOCK_RX_BUFFER_SIZE / \ + BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK - \ + 1) +#endif + +#ifndef BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE +#define BSC_CONF_OPERATIONAL_CERTIFICATE_FILE_INSTANCE 5 +#endif + +#ifndef BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE +#define BSC_CONF_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE 6 +#endif + +#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE +#define BSC_CONF_ISSUER_CERTIFICATE_FILE_1_INSTANCE 7 +#endif + +#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE +#define BSC_CONF_ISSUER_CERTIFICATE_FILE_2_INSTANCE 8 +#endif + +#ifndef BSC_CONF_ISSUER_CERTIFICATE_FILE_3_INSTANCE +#define BSC_CONF_ISSUER_CERTIFICATE_FILE_3_INSTANCE 9 +#endif + +#ifndef BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM +#define BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM \ + BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM +#endif + +#ifndef BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM +#define BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM \ + BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM +#endif + +#ifndef BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM +#define BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM 4 +#endif + +#endif diff --git a/src/bacnet/datalink/bsc/bsc-datalink.c b/src/bacnet/datalink/bsc/bsc-datalink.c new file mode 100644 index 00000000..30295732 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-datalink.c @@ -0,0 +1,653 @@ +/** + * @file + * @brief BACnet secure connect hub function API. + * @author Kirill Neznamov + * @date October 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#include "bacnet/basic/sys/debug.h" +#include +#include "bacnet/datalink/bsc/bsc-conf.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/bsc-datalink.h" +#include "bacnet/datalink/bsc/bsc-socket.h" +#include "bacnet/datalink/bsc/bsc-util.h" +#include "bacnet/datalink/bsc/bsc-event.h" +#include "bacnet/datalink/bsc/bsc-hub-function.h" +#include "bacnet/datalink/bsc/bsc-node.h" +#include "bacnet/bacdef.h" +#include "bacnet/npdu.h" +#include "bacnet/bacenum.h" +#include "bacnet/basic/object/netport.h" +#include "bacnet/basic/object/sc_netport.h" +#include "bacnet/basic/object/bacfile.h" + +#define PRINTF debug_perror +#define DEBUG_BSC_DATALINK 0 +#if DEBUG_BSC_DATALINK == 1 +#define DEBUG_PRINTF debug_printf +#else +#define DEBUG_PRINTF debug_printf_disabled +#endif + +#define BSC_NEXT_POWER_OF_TWO1(v) \ + ((((unsigned int)v) - 1) | ((((unsigned int)v) - 1) >> 1)) +#define BSC_NEXT_POWER_OF_TWO2(v) \ + (BSC_NEXT_POWER_OF_TWO1(v) | BSC_NEXT_POWER_OF_TWO1(v) >> 2) +#define BSC_NEXT_POWER_OF_TWO3(v) \ + (BSC_NEXT_POWER_OF_TWO2(v) | BSC_NEXT_POWER_OF_TWO2(v) >> 4) +#define BSC_NEXT_POWER_OF_TWO4(v) \ + (BSC_NEXT_POWER_OF_TWO3(v) | BSC_NEXT_POWER_OF_TWO3(v) >> 8) +#define BSC_NEXT_POWER_OF_TWO(v) \ + ((BSC_NEXT_POWER_OF_TWO4(v) | BSC_NEXT_POWER_OF_TWO4(v) >> 16)) + 1 + +typedef enum { + BSC_DATALINK_STATE_IDLE = 0, + BSC_DATALINK_STATE_STARTING = 1, + BSC_DATALINK_STATE_STARTED = 2, + BSC_DATALINK_STATE_STOPPING = 3 +} BSC_DATALINK_STATE; + +static FIFO_BUFFER bsc_fifo = { 0 }; +static uint8_t + bsc_fifo_buf[BSC_NEXT_POWER_OF_TWO(BSC_CONF_DATALINK_RX_BUFFER_SIZE)]; +static BSC_NODE *bsc_node = NULL; +static BSC_NODE_CONF bsc_conf; +static BSC_DATALINK_STATE bsc_datalink_state = BSC_DATALINK_STATE_IDLE; +static BSC_EVENT *bsc_event = NULL; +static BSC_EVENT *bsc_data_event = NULL; + +/** + * @brief bsc_node_event() is a callback function which is called by + * BACnet/SC datalink when some event occurs. + * The function is used to notify the upper layer about the events. + * @param node - pointer to the BACnet/SC node + * @param ev - event type + * @param dest - pointer to the destination address + * @param pdu - pointer to the received PDU + * @param pdu_len - length of the received PDU + */ +static void bsc_node_event( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + uint16_t pdu16_len; + DEBUG_PRINTF("bsc_node_event() >>> ev = %d\n", ev); + bws_dispatch_lock(); + (void)node; + (void)dest; + if (ev == BSC_NODE_EVENT_STARTED || ev == BSC_NODE_EVENT_STOPPED) { + if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE) { + bsc_event_signal(bsc_event); + } + } else if (ev == BSC_NODE_EVENT_RECEIVED_NPDU) { + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) { + if (pdu_len <= USHRT_MAX && + FIFO_Available( + &bsc_fifo, (unsigned)(pdu_len + sizeof(pdu16_len)))) { + pdu16_len = (uint16_t)pdu_len; + FIFO_Add(&bsc_fifo, (uint8_t *)&pdu16_len, sizeof(pdu16_len)); + FIFO_Add(&bsc_fifo, pdu, pdu16_len); + bsc_event_signal(bsc_data_event); + } +#if DEBUG_ENABLED == 1 + else { + PRINTF("pdu of size %d\n is dropped\n", pdu_len); + } +#endif + } + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_event() <<<\n"); +} + +/** + * @brief bsc_deinit_resources() is a function which is used to + * deinitialize all resources allocated by BACnet/SC datalink. + */ +static void bsc_deinit_resources(void) +{ + if (bsc_event) { + bsc_event_deinit(bsc_event); + bsc_event = NULL; + } + if (bsc_data_event) { + bsc_event_deinit(bsc_data_event); + bsc_data_event = NULL; + } +} + +/** + * @brief Initialize the BACnet/SC datalink layer + * @param ifname - name of the network interface + * @return true if the initialization was successful, otherwise false + */ +bool bsc_init(char *ifname) +{ + BSC_SC_RET r; + bool ret = false; + DEBUG_PRINTF("bsc_init() >>>\n"); + + (void)ifname; + + bws_dispatch_lock(); + + if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE) { + bws_dispatch_unlock(); + PRINTF("bsc_init() <<< ret = %d\n", ret); + return ret; + } + + bsc_event = bsc_event_init(); + bsc_data_event = bsc_event_init(); + + if (!bsc_event || !bsc_data_event) { + bsc_deinit_resources(); + bws_dispatch_unlock(); + PRINTF("bsc_init() <<< ret = %d\n", false); + return false; + } + + DEBUG_PRINTF( + "bsc_init() BACNET/SC datalink configured to use input fifo " + "of size %d\n", + sizeof(bsc_fifo_buf)); + FIFO_Init(&bsc_fifo, bsc_fifo_buf, sizeof(bsc_fifo_buf)); + + ret = bsc_node_conf_fill_from_netport(&bsc_conf, &bsc_node_event); + + if (!ret) { + bsc_deinit_resources(); + bws_dispatch_unlock(); + PRINTF("bsc_init() <<< configuration of BACNET/SC datalink " + "failed, ret = false\n"); + return ret; + } + + bsc_datalink_state = BSC_DATALINK_STATE_STARTING; + r = bsc_node_init(&bsc_conf, &bsc_node); + if (r == BSC_SC_SUCCESS) { + r = bsc_node_start(bsc_node); + if (r == BSC_SC_SUCCESS) { + bws_dispatch_unlock(); + bsc_event_wait(bsc_event); + bws_dispatch_lock(); + bsc_datalink_state = BSC_DATALINK_STATE_STARTED; + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_init() <<< ret = %d\n", true); + return true; + } + } + bsc_deinit_resources(); + bsc_node_conf_cleanup(&bsc_conf); + bsc_datalink_state = BSC_DATALINK_STATE_IDLE; + bws_dispatch_unlock(); + PRINTF("bsc_init() <<< ret = %d\n", false); + return false; +} + +/** + * @brief Blocking thread-safe bsc_cleanup() function + * de-initializes BACNet/SC datalink. + */ +void bsc_cleanup(void) +{ + DEBUG_PRINTF("bsc_cleanup() >>>\n"); + bws_dispatch_lock(); + if (bsc_datalink_state != BSC_DATALINK_STATE_IDLE && + bsc_datalink_state != BSC_DATALINK_STATE_STOPPING) { + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTING) { + bws_dispatch_unlock(); + bsc_event_wait(bsc_event); + bws_dispatch_lock(); + } + if (bsc_datalink_state != BSC_DATALINK_STATE_STOPPING) { + bsc_datalink_state = BSC_DATALINK_STATE_STOPPING; + bsc_event_signal(bsc_data_event); + bsc_node_stop(bsc_node); + bws_dispatch_unlock(); + bsc_event_wait(bsc_event); + bsc_event_wait(bsc_data_event); + bws_dispatch_lock(); + bsc_deinit_resources(); + (void)bsc_node_deinit(bsc_node); + bsc_node_conf_cleanup(&bsc_conf); + bsc_node = NULL; + bsc_datalink_state = BSC_DATALINK_STATE_IDLE; + } + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_cleanup() <<<\n"); +} + +/** + * @brief Send a BACnet/SC PDU to a remote node + * @param dest - destination address + * @param npdu_data - network layer data + * @param pdu - PDU to send + * @param pdu_len - length of the PDU + * @return number of bytes sent on success, negative number on failure + */ +int bsc_send_pdu( + BACNET_ADDRESS *dest, + BACNET_NPDU_DATA *npdu_data, + uint8_t *pdu, + unsigned pdu_len) +{ + BSC_SC_RET ret; + BACNET_SC_VMAC_ADDRESS dest_vmac; + int len = -1; + static uint8_t buf[BVLC_SC_NPDU_SIZE_CONF]; + + /* this datalink doesn't need to know the npdu data */ + (void)npdu_data; + + bws_dispatch_lock(); + + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) { + if (dest->net == BACNET_BROADCAST_NETWORK || dest->mac_len == 0) { + /* broadcast message */ + memset(&dest_vmac.address[0], 0xFF, BVLC_SC_VMAC_SIZE); + } else if (dest->mac_len == BVLC_SC_VMAC_SIZE) { + /* unicast */ + memcpy(&dest_vmac.address[0], &dest->mac[0], BVLC_SC_VMAC_SIZE); + } else { + bws_dispatch_unlock(); + PRINTF("bsc_send_pdu() <<< ret = -1, incorrect dest mac address\n"); + return len; + } + + len = (int)bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), bsc_get_next_message_id(), NULL, &dest_vmac, pdu, + pdu_len); + + ret = bsc_node_send(bsc_node, buf, len); + len = pdu_len; + + if (ret != BSC_SC_SUCCESS) { + len = -1; + } + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_send_pdu() <<< ret = %d\n", len); + return len; +} + +/** + * @brief Remove a BACnet/SC packet from the FIFO buffer + * @param packet_size - size of the packet to remove + */ +static void bsc_remove_packet(size_t packet_size) +{ + size_t i; + for (i = 0; i < packet_size; i++) { + (void)FIFO_Get(&bsc_fifo); + } +} + +/** + * @brief Blocking thread-safe bsc_receive() function + * receives NPDUs transferred over BACNet/SC + * from a node specified by it's virtual MAC address as + * defined in Clause AB.1.5.2. + * @param src - source VMAC address + * @param pdu - a buffer to hold the PDU portion of the received packet, + * after the BVLC portion has been stripped off. + * @param max_pdu - size of the pdu[] buffer + * @param timeout_ms - the number of milliseconds to wait for a packet + * @return the number of octets (remaining) in the PDU, or zero if no packet + */ +uint16_t bsc_receive( + BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout_ms) +{ + uint16_t pdu_len = 0; + uint16_t npdu16_len = 0; + BVLC_SC_DECODED_MESSAGE dm; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc = NULL; + static uint8_t buf[BVLC_SC_NPDU_SIZE_CONF]; + + DEBUG_PRINTF("bsc_receive() >>>\n"); + + bws_dispatch_lock(); + + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) { + if (FIFO_Count(&bsc_fifo) <= sizeof(npdu16_len)) { + bws_dispatch_unlock(); + bsc_event_timedwait(bsc_data_event, timeout_ms); + bws_dispatch_lock(); + } + + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED && + FIFO_Count(&bsc_fifo) > sizeof(npdu16_len)) { + DEBUG_PRINTF("bsc_receive() processing data...\n"); + FIFO_Pull(&bsc_fifo, (uint8_t *)&npdu16_len, sizeof(npdu16_len)); + + if (sizeof(buf) < npdu16_len) { + PRINTF("bsc_receive() pdu of size %d is dropped\n", npdu16_len); + bsc_remove_packet(npdu16_len); + } else { + FIFO_Pull(&bsc_fifo, buf, npdu16_len); + if (!bvlc_sc_decode_message( + buf, npdu16_len, &dm, &error, &class, &err_desc)) { + PRINTF( + "bsc_receive() pdu of size %d is dropped because " + "of err = %d, class %d, desc = %s\n", + npdu16_len, error, class, err_desc); + bsc_remove_packet(npdu16_len); + } else { + if (dm.hdr.origin && + max_pdu >= dm.payload.encapsulated_npdu.npdu_len) { + src->mac_len = BVLC_SC_VMAC_SIZE; + memcpy( + &src->mac[0], &dm.hdr.origin->address[0], + BVLC_SC_VMAC_SIZE); + memcpy( + pdu, dm.payload.encapsulated_npdu.npdu, + dm.payload.encapsulated_npdu.npdu_len); + pdu_len = + (uint16_t)dm.payload.encapsulated_npdu.npdu_len; + } +#if DEBUG_ENABLED == 1 + else { + PRINTF( + "bsc_receive() pdu of size %d is dropped " + "because origin addr is absent or output " + "buf of size %d is to small\n", + npdu16_len, max_pdu); + } +#endif + } + } + DEBUG_PRINTF("bsc_receive() pdu_len = %d\n", pdu_len); + } + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_receive() <<< ret = %d\n", pdu_len); + return pdu_len; +} + +/** + * @brief Function can be used to retrieve broadcast + * VMAC address for BACNet/SC node. + * @param dest - value of broadcast VMAC address + */ +void bsc_get_broadcast_address(BACNET_ADDRESS *dest) +{ + if (dest) { + dest->net = BACNET_BROADCAST_NETWORK; + dest->mac_len = BVLC_SC_VMAC_SIZE; + memset(&dest->mac[0], 0xFF, BVLC_SC_VMAC_SIZE); + /* no SADR */ + dest->len = 0; + memset(dest->adr, 0, sizeof(dest->adr)); + } +} + +/** + * @brief Function can be used to retrieve local + * VMAC address of initialized BACNet/SC datalink. + * @param my_address - value of local VMAC address + */ +void bsc_get_my_address(BACNET_ADDRESS *my_address) +{ + if (my_address) { + memset(my_address, 0, sizeof(*my_address)); + } + + bws_dispatch_lock(); + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) { + my_address->mac_len = BVLC_SC_VMAC_SIZE; + memcpy( + &my_address->mac[0], &bsc_conf.local_vmac.address[0], + BVLC_SC_VMAC_SIZE); + } + bws_dispatch_unlock(); +} + +/** + * @brief Determine if the BACnet/SC direct connection is established + * with a remote BACnet/SC node. + * @param dest - BACnet/SC VMAC address of the remote node to check direct + * connection status + * @param urls - array representing the possible URIs of a remote node for + * acceptance of direct connections. Can contain 1 element + * @param urls_cnt - number of elements in the urls array + * @return true if the connection is established, otherwise false + */ +bool bsc_direct_connection_established( + BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt) +{ + bool ret = false; + bws_dispatch_lock(); + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) { + ret = bsc_node_direct_connection_established( + bsc_node, dest, urls, urls_cnt); + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Start the process of establishing a direct BACnet/SC connection + * to a node identified by either urls or dest parameter. The user should + * note that if the dest parameter is used, the local node tries to resolve + * it (e.g. to get URIs related to dest VMAC from all existing BACnet/SC + * nodes in the network). As a result, the process of establishing a BACnet/SC + * connection by dest may take an unpredictable amount of time depending on + * the current network configuration. + * @param dest - BACnet/SC VMAC address of the remote node to check direct + * connection status + * @param urls - array representing the possible URIs of a remote node for + * acceptance of direct connections. Can contain 1 element + * @param urls_cnt - number of elements in the urls array + * @return BSC_SC_SUCCESS if the process of establishing a BACnet/SC + * connection was started successfully, otherwise an error code + */ +BSC_SC_RET +bsc_connect_direct(BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt) +{ + BSC_SC_RET ret = BSC_SC_INVALID_OPERATION; + DEBUG_PRINTF( + "bsc_connect_direct() >>> dest = %p, urls = %p, urls_cnt = %d\n", dest, + urls, urls_cnt); + bws_dispatch_lock(); + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) { + ret = bsc_node_connect_direct(bsc_node, dest, urls, urls_cnt); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_connect_direct() ret = %d\n", ret); + return ret; +} + +/** + * @brief Disconnect a direct BACnet/SC connection with a remote node + * identified by its VMAC address. + * @param dest - BACnet/SC VMAC address of the remote node to disconnect + * the direct connection with + */ +void bsc_disconnect_direct(BACNET_SC_VMAC_ADDRESS *dest) +{ + bws_dispatch_lock(); + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) { + bsc_node_disconnect_direct(bsc_node, dest); + } + bws_dispatch_unlock(); +} + +/** + * @brief Process the hub connector state + */ +static void bsc_update_hub_connector_state(void) +{ + BACNET_SC_HUB_CONNECTOR_STATE state; + uint32_t instance; + + instance = Network_Port_Index_To_Instance(0); + state = bsc_node_hub_connector_state(bsc_node); + Network_Port_SC_Hub_Connector_State_Set(instance, state); +} + +/** + * @brief Process the hub connector status + */ +static void bsc_update_hub_connector_status(void) +{ + BACNET_SC_HUB_CONNECTION_STATUS *status; + uint32_t instance; + + instance = Network_Port_Index_To_Instance(0); + status = bsc_node_hub_connector_status(bsc_node, true); + if (status) { + Network_Port_SC_Primary_Hub_Connection_Status_Set( + instance, status->State, &status->Connect_Timestamp, + &status->Disconnect_Timestamp, status->Error, + status->Error_Details[0] ? status->Error_Details : NULL); + } + status = bsc_node_hub_connector_status(bsc_node, false); + if (status) { + Network_Port_SC_Failover_Hub_Connection_Status_Set( + instance, status->State, &status->Connect_Timestamp, + &status->Disconnect_Timestamp, status->Error, + status->Error_Details[0] ? status->Error_Details : NULL); + } +} + +/** + * @brief Process the hub function status + */ +static void bsc_update_hub_function_status(void) +{ + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *s; + size_t cnt; + int i; + uint32_t instance = Network_Port_Index_To_Instance(0); + BACNET_SC_VMAC_ADDRESS uninitialized = { 0 }; + + s = bsc_node_hub_function_status(bsc_node, &cnt); + if (s) { + Network_Port_SC_Hub_Function_Connection_Status_Delete_All(instance); + for (i = 0; i < cnt; i++) { + if (memcmp( + &uninitialized.address[0], &s[i].Peer_VMAC[0], + BVLC_SC_VMAC_SIZE) != 0) { + Network_Port_SC_Hub_Function_Connection_Status_Add( + instance, s[i].State, &s[i].Connect_Timestamp, + &s[i].Disconnect_Timestamp, &s[i].Peer_Address, + s[i].Peer_VMAC, s[i].Peer_UUID.uuid.uuid128, s[i].Error, + s[i].Error_Details[0] == 0 ? NULL : s[i].Error_Details); + } + } + } +} + +/** + * @brief Add direct connection status to the network port + * @param s - direct connection status + * @param cnt - number of direct connection statuses + */ +static void bsc_add_direct_status_to_netport( + BACNET_SC_DIRECT_CONNECTION_STATUS *s, size_t cnt) +{ + int i; + uint32_t instance = Network_Port_Index_To_Instance(0); + BACNET_SC_VMAC_ADDRESS uninitialized = { 0 }; + + for (i = 0; i < cnt; i++) { + if (memcmp( + &uninitialized.address[0], &s[i].Peer_VMAC[0], + BVLC_SC_VMAC_SIZE) != 0) { + Network_Port_SC_Direct_Connect_Connection_Status_Add( + instance, s[i].URI[0] == 0 ? NULL : s[i].URI, s[i].State, + &s[i].Connect_Timestamp, &s[i].Disconnect_Timestamp, + &s[i].Peer_Address, s[i].Peer_VMAC, s[i].Peer_UUID.uuid.uuid128, + s[i].Error, + s[i].Error_Details[0] == 0 ? NULL : s[i].Error_Details); + } + } +} + +/** + * @brief Process the direct connection status + */ +static void bsc_update_direct_connection_status(void) +{ + BACNET_SC_DIRECT_CONNECTION_STATUS *s = NULL; + size_t cnt = 0; + uint32_t instance = Network_Port_Index_To_Instance(0); + + s = bsc_node_direct_connection_status(bsc_node, &cnt); + if (s) { + Network_Port_SC_Direct_Connect_Connection_Status_Delete_All(instance); + if (s && cnt) { + bsc_add_direct_status_to_netport(s, cnt); + } + } +} + +/** + * @brief Process the failed requests + */ +static void bsc_update_failed_requests(void) +{ + BACNET_SC_FAILED_CONNECTION_REQUEST *r; + size_t cnt; + int i; + + uint32_t instance = Network_Port_Index_To_Instance(0); + r = bsc_node_failed_requests_status(bsc_node, &cnt); + if (r) { + Network_Port_SC_Failed_Connection_Requests_Delete_All(instance); + for (i = 0; i < cnt; i++) { + if (r[i].Peer_Address.host[0] != 0) { +#if DEBUG_ENABLED == 1 + DEBUG_PRINTF( + "failed request record %d, host %s, vmac %s, uuid " + "%s, error %d, details = %s\n", + i, r[i].Peer_Address.host, + bsc_vmac_to_string( + (BACNET_SC_VMAC_ADDRESS *)r[i].Peer_VMAC), + bsc_uuid_to_string( + (BACNET_SC_UUID *)r[i].Peer_UUID.uuid.uuid128), + r[i].Error, r[i].Error_Details); +#endif + Network_Port_SC_Failed_Connection_Requests_Add( + instance, &r[i].Timestamp, &r[i].Peer_Address, + r[i].Peer_VMAC, r[i].Peer_UUID.uuid.uuid128, r[i].Error, + r[i].Error_Details[0] != 0 ? r[i].Error_Details : NULL); + } + } + } +} + +/** + * @brief Update the network port properties + */ +static void bsc_update_netport_properties(void) +{ + if (bsc_datalink_state == BSC_DATALINK_STATE_STARTED) { + bsc_update_hub_connector_state(); + bsc_update_hub_connector_status(); + bsc_update_hub_function_status(); + bsc_update_direct_connection_status(); + bsc_update_failed_requests(); + } +} + +/** + * @brief Manage the BACnet/SC datalink timer + * @param seconds - number of elapsed seconds + */ +void bsc_maintenance_timer(uint16_t seconds) +{ + bws_dispatch_lock(); + bsc_node_maintenance_timer(seconds); + bsc_update_netport_properties(); + bws_dispatch_unlock(); +} diff --git a/src/bacnet/datalink/bsc/bsc-datalink.h b/src/bacnet/datalink/bsc/bsc-datalink.h new file mode 100644 index 00000000..3fef7fe2 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-datalink.h @@ -0,0 +1,176 @@ +/** + * @file + * @brief BACNet/SC datalink public interface API. + * @author Kirill Neznamov + * @date October 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#ifndef BACNET_DATALINK_BSC_DATALINK_H +#define BACNET_DATALINK_BSC_DATALINK_H +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/npdu.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/bsc-retcodes.h" + +/** + * @brief Blocking thread-safe bsc_init() function + * initializes BACNet/SC datalink in accordence with value of properties + * from BACNet/SC Network Port Object (sc_netport.h). That means that + * user must initialize and set corresponded properties required for + * configuration of BACNet/SC datalink before calling of that function. + * According to "Addendum cc" to ANSI/ASHRAE Standard 135-2020 + * https://bacnet.org/wp-content/uploads/sites/4/2022/08/Add-135-2020cc.pdf + * most important properties are: + * SC_Primary_Hub_URI + * SC_Failover_Hub_URI + * SC_Maximum_Reconnect_Time + * SC_Connect_Wait_Timeout + * SC_Disconnect_Wait_Timeout + * SC_Heartbeat_Timeout + * Operational_Certificate_File + * Issuer_Certificate_Files + * SC_Hub_Function_Enable + * SC_Hub_Function_Binding + * SC_Direct_Connect_Initiate_Enable + * SC_Direct_Connect_Binding + * SC_Direct_Connect_Accept_Enable + * @param ifname - ignored and unused it was added for backward compatibility. + * @return true if datalink was initiated and started, otherwise returns false. + */ + +BACNET_STACK_EXPORT +bool bsc_init(char *ifname); + +/** + * @brief Blocking thread-safe bsc_cleanup() function + * de-initializes BACNet/SC datalink. + */ +BACNET_STACK_EXPORT +void bsc_cleanup(void); +/** + * @brief Function checks if all needed certificate file are present. + * @return true if all needed certificate file are present otherwise returns + * false. + */ + +BACNET_STACK_EXPORT +bool bsc_cert_files_check(void); + +/** + * @brief Blocking thread-safe bsc_send_pdu() function + * sends pdu over BACNet/SC to node specified by + * destination address param. + * + * @param dest [in] BACNet/SC node's virtual MAC address as + * defined in Clause AB.1.5.2. + * Can be broadcast. + * @param npdu_data [in] BACNet/SC datalink does not use that + * parameter. Added for backward + * compatibility. + * @param pdu [in] protocol data unit to be sent. + * @param pdu_len [in] - number of bytes to send. + * @return Number of bytes sent on success, negative number on failure. + */ + +BACNET_STACK_EXPORT +int bsc_send_pdu( + BACNET_ADDRESS *dest, + BACNET_NPDU_DATA *npdu_data, + uint8_t *pdu, + unsigned pdu_len); + +/** + * @brief Blocking thread-safe bsc_receive() function + * receives NPDUs transferred over BACNet/SC + * from a node specified by it's virtual MAC address as + * defined in Clause AB.1.5.2. + * + * @param src [out] Source VMAC address + * @param pdu [out] A buffer to hold the PDU portion of the received packet, + * after the BVLC portion has been stripped off. + * @param max_pdu [in] Size of the pdu[] buffer. + * @param timeout [in] The number of milliseconds to wait for a packet. + * @return The number of octets (remaining) in the PDU, or zero on failure. + */ + +BACNET_STACK_EXPORT +uint16_t bsc_receive( + BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout_ms); + +/** + * @brief Function can be used to retrieve broadcast + * VMAC address for BACNet/SC node. + * + * @param addr [out] Value of broadcast VMAC address. + */ + +BACNET_STACK_EXPORT +void bsc_get_broadcast_address(BACNET_ADDRESS *addr); + +/** + * @brief Function can be used to retrieve local + * VMAC address of initialized BACNet/SC datalink. + * If function called when datalink is not started, + * my_address filled by empty vmac address + * X'000000000000' as it defined in clause AB.1.5.2 + * + * @param my_address [out] Value of local VMAC address. + */ + +BACNET_STACK_EXPORT +void bsc_get_my_address(BACNET_ADDRESS *my_address); + +/** + * @brief Function checks if BACNet/SC direct connection is + * established with remote BACNet/SC node. + * User can check the status of connection using either + * destination vmac or list of destination urls. + * @param dest BACNet/SC vmac of remote node to check direct + * connection status. + * @param urls this array represents the possible URIs of a + * remote node for acceptance of direct connections. + * Can contain 1 elem. + * @param urls_cnt - size of urls array. + * @return true if connection is established otherwise returns + * false. + */ + +BACNET_STACK_EXPORT +bool bsc_direct_connection_established( + BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt); + +/** + * @brief Function starts process of establishing of a + * direct BACNet/SC connection to node identified by + * either urls or dest parameter. User should note that + * if dest parameter is used, local node tries to resolve + * it (e.g.to get URIs related to dest vmac from all existent + * BACNet/SC nodes in network). As a result the process of + * establishing of a BACNet/SC connection by dest may + * take unpredictable amount of time depending on a current + * network configuration. + * @param dest BACNet/SC vmac of remote node to check direct + * connection status. + * @param urls this array represents the possible URIs of a + * remote node for acceptance of direct connections. + * Can contain 1 elem. + * @param urls_cnt - size of urls array. + * + * @return BSC_SC_SUCCESS if process of a establishing of a BACNet/SC + * connection was started successfully, otherwise returns + * any retcode from BSC_SC_RET enum. + */ + +BACNET_STACK_EXPORT +BSC_SC_RET +bsc_connect_direct(BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt); + +BACNET_STACK_EXPORT +void bsc_disconnect_direct(BACNET_SC_VMAC_ADDRESS *dest); + +BACNET_STACK_EXPORT +void bsc_maintenance_timer(uint16_t seconds); + +#endif diff --git a/src/bacnet/datalink/bsc/bsc-event.h b/src/bacnet/datalink/bsc/bsc-event.h new file mode 100644 index 00000000..3e1bdcaf --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-event.h @@ -0,0 +1,101 @@ +/** + * @file + * @brief crossplatform event abstraction used in BACnet secure connect. + * @author Kirill Neznamov + * @date August 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_EVENT_H +#define BACNET_DATALINK_BSC_EVENT_H +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" + +struct BSC_Event; +typedef struct BSC_Event BSC_EVENT; + +/** + * @brief bsc_event_init() allocates and initializes auto-reset + * event object to non-signalled state. An event object + * can be set to signalled state by the call of + * bsc_event_signal(). When the state of the event object + * is signaled, it remains signaled until last thread is + * released which was blocked on bsc_event_wait() or + * bsc_event_timed_wait() calls. When writing the code user + * must always remember the following statements: + * 1. It is guaranteed that all currently waiting threads will be + * unblocked by the call of bsc_event_signal(). + * 2. If user has called bsc_event_wait() or bsc_event_timedwait() + * from thread N after the call of bsc_event_signal() but + * when the state of the corresponded event object is still + * signalled, it is not guaranteed that thread N will be + * unblocked. Thread N may be stay blocked or may unblock + * depending on multitasking context of the OS. + * + * @return handle of event object if function succeeded, otherwise + * returns NULL. + */ + +BSC_EVENT *bsc_event_init(void); + +/** + * @brief bsc_event_deinit() deinitialize auto-reset + * event object. If a user calls that function while + * some threads are waiting the specified event object ev + * the behaviour is undefined. + * + * @param ev - handle to previously initialized event object. + */ + +void bsc_event_deinit(BSC_EVENT *ev); + +/** + * @brief bsc_wait() suspends the execution of the current thread + * for the specified amount of seconds. + * + * @param seconds - time to sleep in seconds. + */ + +void bsc_wait(int seconds); + +/** + * @brief bsc_wait() suspends the execution of the current thread + * for the specified amount of seconds. + * + * @param mseconds - time to sleep in milliseconds. + */ + +void bsc_wait_ms(int mseconds); + +/** + * @brief bsc_event_wait() suspends the execution of the current thread + * until corresponded event object becomes signalled. + * + * @param ev - handle to previously initialized event object. + */ + +void bsc_event_wait(BSC_EVENT *ev); + +/** + * @brief bsc_event_timedwait() suspends the execution of the current thread + * until corresponded event object becomes signalled + * or ms_timeout becomes elapsed. + * + * @param ev - handle to previously initialized event object. + * @param ms_timeout - timeout in milliseconds + * @return true if the corresponded event was signalled. + * false if ms_timeout was elapsed but event ev is still in + * non signalled state. + */ + +bool bsc_event_timedwait(BSC_EVENT *ev, unsigned int ms_timeout); + +/** + * @brief bsc_event_signal() function sets state of corresponded event object + * to signalled state. + * + * @param ev - handle to previously initialized event object. + */ + +void bsc_event_signal(BSC_EVENT *ev); + +#endif diff --git a/src/bacnet/datalink/bsc/bsc-hub-connector.c b/src/bacnet/datalink/bsc/bsc-hub-connector.c new file mode 100644 index 00000000..134ee027 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-hub-connector.c @@ -0,0 +1,651 @@ +/** + * @file + * @brief BACnet hub connector API. + * @author Kirill Neznamov + * @date July 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include "bacnet/basic/sys/debug.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/bsc-socket.h" +#include "bacnet/datalink/bsc/bsc-util.h" +#include "bacnet/datalink/bsc/bsc-hub-connector.h" +#include "bacnet/bacdef.h" +#include "bacnet/npdu.h" +#include "bacnet/bacenum.h" +#include "bacnet/basic/object/sc_netport.h" +#include "bacnet/bactext.h" + +#define DEBUG_BSC_HUB_CONNECTOR 0 + +#if DEBUG_BSC_HUB_CONNECTOR == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +typedef enum { + BSC_HUB_CONN_PRIMARY = 0, + BSC_HUB_CONN_FAILOVER = 1 +} BSC_HUB_CONN_TYPE; + +static void hub_connector_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu); + +static void hub_connector_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev); + +typedef enum { + BSC_HUB_CONNECTOR_STATE_IDLE = 0, + BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY = 1, + BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER = 2, + BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY = 3, + BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER = 4, + BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT = 5, + BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT = 6, + BSC_HUB_CONNECTOR_STATE_DUPLICATED_VMAC = 7 +} BSC_HUB_CONNECTOR_STATE; + +typedef struct BSC_Hub_Connector { + bool used; + BSC_SOCKET_CTX ctx; + BSC_CONTEXT_CFG cfg; + BSC_SOCKET sock[2]; + BSC_HUB_CONNECTOR_STATE state; + unsigned int reconnect_timeout_s; + uint8_t primary_url[BSC_WSURL_MAX_LEN + 1]; + uint8_t failover_url[BSC_WSURL_MAX_LEN + 1]; + struct mstimer t; + BSC_HUB_CONNECTOR_EVENT_FUNC event_func; + void *user_arg; + BACNET_SC_HUB_CONNECTION_STATUS primary_status; + BACNET_SC_HUB_CONNECTION_STATUS failover_status; +} BSC_HUB_CONNECTOR; + +#if BSC_CONF_HUB_CONNECTORS_NUM > 0 +static BSC_HUB_CONNECTOR bsc_hub_connector[BSC_CONF_HUB_CONNECTORS_NUM] = { 0 }; +#else +static BSC_HUB_CONNECTOR *bsc_hub_connector = NULL; +#endif + +static BSC_SOCKET_CTX_FUNCS bsc_hub_connector_ctx_funcs = { + NULL, NULL, hub_connector_socket_event, hub_connector_context_event, NULL +}; + +/** + * @brief Initialize the BACnet hub connector status + * @param s - pointer to the status structure + */ +static void hub_connector_reset_status(BACNET_SC_HUB_CONNECTION_STATUS *s) +{ + /* set timestamps to unspecified values */ + memset(&s->Connect_Timestamp, 0xFF, sizeof(s->Connect_Timestamp)); + memset(&s->Disconnect_Timestamp, 0xFF, sizeof(s->Disconnect_Timestamp)); + s->Error = ERROR_CODE_DEFAULT; + s->Error_Details[0] = 0; +} + +/** + * @brief Allocate a hub connector + * @return pointer to the allocated hub connector + */ +static BSC_HUB_CONNECTOR *hub_connector_alloc(void) +{ + int i; + for (i = 0; i < BSC_CONF_HUB_CONNECTORS_NUM; i++) { + if (!bsc_hub_connector[i].used) { + memset(&bsc_hub_connector[i], 0, sizeof(bsc_hub_connector[i])); + bsc_hub_connector[i].used = true; + hub_connector_reset_status(&bsc_hub_connector[i].primary_status); + hub_connector_reset_status(&bsc_hub_connector[i].failover_status); + DEBUG_PRINTF( + "hub_connector_alloc() ret = %p\n", &bsc_hub_connector[i]); + return &bsc_hub_connector[i]; + } + } + DEBUG_PRINTF("hub_connector_alloc() ret = %p\n", &bsc_hub_connector[i]); + return NULL; +} + +/** + * @brief Free a hub connector + * @param c - pointer to the hub connector + */ +static void hub_connector_free(BSC_HUB_CONNECTOR *c) +{ + DEBUG_PRINTF("hub_connector_free() c = %p\n", c); + c->used = false; +} + +/** + * @brief Update the BACnet hub connector status + * @param s - pointer to the status structure + * @param state - new state + * @param err - error code + * @param err_desc - error description + */ +static void hub_conector_update_status( + BACNET_SC_HUB_CONNECTION_STATUS *s, + BACNET_SC_CONNECTION_STATE state, + BACNET_ERROR_CODE err, + const char *err_desc) +{ + s->State = state; + if (state == BACNET_SC_CONNECTION_STATE_NOT_CONNECTED || + state == BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS) { + bsc_set_timestamp(&s->Disconnect_Timestamp); + } else if ( + state == BACNET_SC_CONNECTION_STATE_CONNECTED || + state == BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT) { + bsc_set_timestamp(&s->Connect_Timestamp); + } + s->Error = err; + s->Error_Details[0] = 0; + if (err_desc) { + bsc_copy_str(&s->Error_Details[0], err_desc, sizeof(s->Error_Details)); + } +} + +/** + * @brief Connect to a BACnet hub + * @param p - pointer to the hub connector + * @param type - connection type + */ +static void hub_connector_connect(BSC_HUB_CONNECTOR *p, BSC_HUB_CONN_TYPE type) +{ + BSC_SC_RET ret; + char *url = (type == BSC_HUB_CONN_PRIMARY) ? (char *)p->primary_url + : (char *)p->failover_url; + + p->state = (type == BSC_HUB_CONN_PRIMARY) + ? BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY + : BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER; + + DEBUG_PRINTF( + "hub_connector_connect() hub = %p connecting to url %s\n", p, url); + + if (url[0] == 0) { + mstimer_set(&p->t, p->reconnect_timeout_s * 1000); + p->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT; + return; + } + + ret = bsc_connect(&p->ctx, &p->sock[type], url); + (void)ret; + +#if DEBUG_ENABLED == 1 + if (ret != BSC_SC_SUCCESS) { + DEBUG_PRINTF( + "hub_connector_connect() got error while " + "connecting to hub type %d, err = %d\n", + type, ret); + } +#endif +} + +/** + * @brief Process the hub connector state + * @param c - pointer to the hub connector + */ +static void hub_connector_process_state(BSC_HUB_CONNECTOR *c) +{ + if (c->state == BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT) { + if (mstimer_expired(&c->t)) { + hub_connector_connect(c, BSC_HUB_CONN_PRIMARY); + } + } +} + +/** + * @brief Hub connector maintenance timer + * @param seconds - number of elapsed seconds + */ +void bsc_hub_connector_maintenance_timer(uint16_t seconds) +{ + int i; + (void)seconds; + + bws_dispatch_lock(); + for (i = 0; i < BSC_CONF_HUB_CONNECTORS_NUM; i++) { + if (bsc_hub_connector[i].used) { + hub_connector_process_state(&bsc_hub_connector[i]); + } + } + bws_dispatch_unlock(); +} + +/** + * @brief Hub connector socket event + * @param c - pointer to the socket + * @param ev - event + * @param disconnect_reason - disconnect reason + * @param disconnect_reason_desc - disconnect reason description + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @param decoded_pdu - decoded PDU + */ +static void hub_connector_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE disconnect_reason, + const char *disconnect_reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + BSC_HUB_CONNECTOR *hc; + BACNET_SC_CONNECTION_STATE st = + BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS; + + bws_dispatch_lock(); + hc = (BSC_HUB_CONNECTOR *)c->ctx->user_arg; + DEBUG_PRINTF( + "hub_connector_socket_event() >>> hub_connector = %p, socket " + "= %p, ev = %d, reason = %d, reason_desc = %p," + "pdu = %p, pdu_len = %d\n", + hc, c, ev, disconnect_reason, disconnect_reason_desc, pdu, pdu_len); + DEBUG_PRINTF("hub_connector_socket_event() state = %d\n", hc->state); + if (ev == BSC_SOCKET_EVENT_CONNECTED) { + if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) { + DEBUG_PRINTF( + "hub_connector_socket_event() hub_connector = %p " + "connected primary\n", + hc); + hc->state = BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY; + hub_conector_update_status( + &hc->primary_status, BACNET_SC_CONNECTION_STATE_CONNECTED, + ERROR_CODE_DEFAULT, NULL); + hc->event_func( + BSC_HUBC_EVENT_CONNECTED_PRIMARY, hc, hc->user_arg, NULL, 0, + NULL); + } else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) { + DEBUG_PRINTF( + "hub_connector_socket_event() hub_connector = %p " + "connected failover\n", + hc); + hc->state = BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER; + hub_conector_update_status( + &hc->failover_status, BACNET_SC_CONNECTION_STATE_CONNECTED, + ERROR_CODE_DEFAULT, NULL); + hc->event_func( + BSC_HUBC_EVENT_CONNECTED_FAILOVER, hc, hc->user_arg, NULL, 0, + NULL); + } + } else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) { + if (disconnect_reason == ERROR_CODE_NODE_DUPLICATE_VMAC && + hc->state != BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT) { + DEBUG_PRINTF("hub_connector_socket_event() " + "got ERROR_CODE_NODE_DUPLICATE_VMAC error\n"); + if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) { + hub_conector_update_status( + &hc->primary_status, + BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, + disconnect_reason, disconnect_reason_desc); + } else if ( + hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) { + hub_conector_update_status( + &hc->failover_status, + BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, + disconnect_reason, disconnect_reason_desc); + } + hc->state = BSC_HUB_CONNECTOR_STATE_DUPLICATED_VMAC; + hc->event_func( + BSC_HUBC_EVENT_ERROR_DUPLICATED_VMAC, hc, hc->user_arg, NULL, 0, + NULL); + } else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY) { + DEBUG_PRINTF("hub_connector_socket_event() try to connect to " + "failover hub\n"); + hub_conector_update_status( + &hc->primary_status, + BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, disconnect_reason, + disconnect_reason_desc); + hub_connector_connect(hc, BSC_HUB_CONN_FAILOVER); + } else if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER) { + DEBUG_PRINTF( + "hub_connector_socket_event() wait for %d seconds\n", + hc->reconnect_timeout_s); + hub_conector_update_status( + &hc->failover_status, + BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, disconnect_reason, + disconnect_reason_desc); + hc->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_RECONNECT; + mstimer_set(&hc->t, hc->reconnect_timeout_s * 1000); + } else if ( + hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY || + hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER) { + if (disconnect_reason == ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER || + disconnect_reason == ERROR_CODE_SUCCESS) { + st = BACNET_SC_CONNECTION_STATE_NOT_CONNECTED; + } + if (hc->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) { + hub_conector_update_status( + &hc->primary_status, st, ERROR_CODE_DEFAULT, NULL); + } else { + hub_conector_update_status( + &hc->failover_status, st, ERROR_CODE_DEFAULT, NULL); + } + DEBUG_PRINTF( + "hub_connector_socket_event() try to connect to primary hub\n"); + hub_connector_connect(hc, BSC_HUB_CONN_PRIMARY); + } + } else if (ev == BSC_SOCKET_EVENT_RECEIVED) { + DEBUG_PRINTF( + "hub_connector_socket_event() hub_connector = %p pdu of " + "%d len is received\n", + hc, pdu_len); + hc->event_func( + BSC_HUBC_EVENT_RECEIVED, hc, hc->user_arg, pdu, pdu_len, + decoded_pdu); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("hub_connector_socket_event() <<<\n"); +} + +/** + * @brief Hub connector context event + * @param ctx - pointer to the context + * @param ev - event + */ +static void hub_connector_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev) +{ + BSC_HUB_CONNECTOR *c; + + DEBUG_PRINTF( + "hub_connector_context_event() >>> ctx = %p, ev = %d\n", ctx, ev); + + if (ev == BSC_CTX_DEINITIALIZED) { + bws_dispatch_lock(); + c = (BSC_HUB_CONNECTOR *)ctx->user_arg; + if (c->state != BSC_HUB_CONNECTOR_STATE_IDLE) { + c->state = BSC_HUB_CONNECTOR_STATE_IDLE; + hub_connector_free(c); + c->event_func( + BSC_HUBC_EVENT_STOPPED, c, c->user_arg, NULL, 0, NULL); + } + bws_dispatch_unlock(); + } + + DEBUG_PRINTF("hub_connector_context_event() <<<\n"); +} + +/** + * @brief Start a BACnet hub connector + * @param ca_cert_chain - pointer to the CA certificate chain + * @param ca_cert_chain_size - size of the CA certificate chain + * @param cert_chain - pointer to the certificate chain + * @param cert_chain_size - size of the certificate chain + * @param key - pointer to the private key + * @param key_size - size of the private key + * @param local_uuid - pointer to the local UUID + * @param local_vmac - pointer to the local VMAC address + * @param max_local_bvlc_len - maximum BVLC message size + * @param max_local_npdu_len - maximum NPDU message size + * @param connect_timeout_s - connection timeout in seconds + * @param heartbeat_timeout_s - heartbeat timeout in seconds + * @param disconnect_timeout_s - disconnect timeout in seconds + * @param primaryURL - primary hub URL + * @param failoverURL - failover hub URL + * @param reconnect_timeout_s - reconnect timeout in seconds + * @param event_func - event function + * @param user_arg - user argument + * @param h - pointer to the hub connector handle + * @return status + */ +BSC_SC_RET bsc_hub_connector_start( + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + uint16_t max_local_bvlc_len, + uint16_t max_local_npdu_len, + unsigned int connect_timeout_s, + unsigned int heartbeat_timeout_s, + unsigned int disconnect_timeout_s, + char *primaryURL, + char *failoverURL, + unsigned int reconnect_timeout_s, + BSC_HUB_CONNECTOR_EVENT_FUNC event_func, + void *user_arg, + BSC_HUB_CONNECTOR_HANDLE *h) +{ + BSC_SC_RET ret = BSC_SC_SUCCESS; + BSC_HUB_CONNECTOR *c; + + DEBUG_PRINTF("bsc_hub_connector_start() >>>\n"); + + if (!ca_cert_chain || !ca_cert_chain_size || !cert_chain || + !cert_chain_size || !key || !key_size || !local_uuid || !local_vmac || + !max_local_npdu_len || !max_local_bvlc_len || !connect_timeout_s || + !heartbeat_timeout_s || !disconnect_timeout_s || !primaryURL || + !reconnect_timeout_s || !event_func || !h) { + DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + if (strlen(primaryURL) > BSC_WSURL_MAX_LEN || + (failoverURL && (strlen(failoverURL) > BSC_WSURL_MAX_LEN))) { + DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + bws_dispatch_lock(); + c = hub_connector_alloc(); + if (!c) { + bws_dispatch_unlock(); + DEBUG_PRINTF( + "bsc_hub_connector_start() <<< ret = BSC_SC_NO_RESOURCES\n"); + return BSC_SC_NO_RESOURCES; + } + + c->reconnect_timeout_s = reconnect_timeout_s; + c->primary_url[0] = 0; + c->failover_url[0] = 0; + c->user_arg = user_arg; + strcpy((char *)c->primary_url, primaryURL); + + if (failoverURL) { + strcpy((char *)c->failover_url, failoverURL); + } + + c->event_func = event_func; + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &c->cfg, BSC_WEBSOCKET_HUB_PROTOCOL, 0, NULL, + ca_cert_chain, ca_cert_chain_size, cert_chain, cert_chain_size, key, + key_size, local_uuid, local_vmac, max_local_bvlc_len, + max_local_npdu_len, connect_timeout_s, heartbeat_timeout_s, + disconnect_timeout_s); + + DEBUG_PRINTF( + "bsc_hub_connector_start() uuid = %s, vmac = %s\n", + bsc_uuid_to_string(&c->cfg.local_uuid), + bsc_vmac_to_string(&c->cfg.local_vmac)); + + ret = bsc_init_ctx( + &c->ctx, &c->cfg, &bsc_hub_connector_ctx_funcs, c->sock, + sizeof(c->sock) / sizeof(BSC_SOCKET), (void *)c); + + if (ret == BSC_SC_SUCCESS) { + *h = (BSC_HUB_CONNECTOR_HANDLE)c; + DEBUG_PRINTF( + "bsc_hub_connector_start() hub = %p connecting to url %s\n", c, + c->primary_url); + ret = bsc_connect( + &c->ctx, &c->sock[BSC_HUB_CONN_PRIMARY], (char *)c->primary_url); + if (ret == BSC_SC_SUCCESS) { + c->state = BSC_HUB_CONNECTOR_STATE_CONNECTING_PRIMARY; + } else { + if (c->failover_url[0] == 0) { + c->state = BSC_HUB_CONNECTOR_STATE_IDLE; + bsc_deinit_ctx(&c->ctx); + hub_connector_free(c); + *h = NULL; + } else { + c->state = BSC_HUB_CONNECTOR_STATE_CONNECTING_FAILOVER; + DEBUG_PRINTF( + "bsc_hub_connector_start() hub = %p connecting to url %s\n", + c, c->primary_url); + ret = bsc_connect( + &c->ctx, &c->sock[BSC_HUB_CONN_FAILOVER], + (char *)c->failover_url); + if (ret != BSC_SC_SUCCESS) { + c->state = BSC_HUB_CONNECTOR_STATE_IDLE; + bsc_deinit_ctx(&c->ctx); + hub_connector_free(c); + *h = NULL; + } + } + } + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_hub_connector_start() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Stop a BACnet hub connector + * @param h - pointer to the hub connector + */ +void bsc_hub_connector_stop(BSC_HUB_CONNECTOR_HANDLE h) +{ + BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h; + DEBUG_PRINTF("bsc_hub_connector_stop() >>> h = %p\n", h); + + bws_dispatch_lock(); + +#if DEBUG_ENABLED == 1 + if (c) { + DEBUG_PRINTF("bsc_hub_connector_stop() state = %d\n", c->state); + } +#endif + + if (c && c->state != BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT && + c->state != BSC_HUB_CONNECTOR_STATE_IDLE) { + c->state = BSC_HUB_CONNECTOR_STATE_WAIT_FOR_CTX_DEINIT; + bsc_deinit_ctx(&c->ctx); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_hub_connector_stop() <<<\n"); +} + +/** + * @brief Send a BACnet hub connector PDU + * @param h - pointer to the hub connector + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @return status + */ +BSC_SC_RET +bsc_hub_connector_send(BSC_HUB_CONNECTOR_HANDLE h, uint8_t *pdu, size_t pdu_len) +{ + BSC_SC_RET ret; + BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h; + + DEBUG_PRINTF( + "bsc_hub_connector_send() >>> h = %p, pdu = %p, pdu_len = %d\n", h, + pdu, pdu_len); + + bws_dispatch_lock(); + if (!c) { + DEBUG_PRINTF("bsc_hub_connector_send() <<< ret = BSC_SC_BAD_PARAM\n"); + bws_dispatch_unlock(); + return BSC_SC_BAD_PARAM; + } + if (c->state == BSC_HUB_CONNECTOR_STATE_IDLE || + (c->state != BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY && + c->state != BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER)) { + DEBUG_PRINTF( + "bsc_hub_connector_send() pdu is dropped, state of " + "hub_connector %p is %d\n", + c, c->state); + DEBUG_PRINTF( + "bsc_hub_connector_send() <<< ret = BSC_SC_INVALID_OPERATION\n"); + bws_dispatch_unlock(); + return BSC_SC_INVALID_OPERATION; + } + if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) { + ret = bsc_send(&c->sock[BSC_HUB_CONN_PRIMARY], pdu, pdu_len); + } else { + ret = bsc_send(&c->sock[BSC_HUB_CONN_FAILOVER], pdu, pdu_len); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_hub_connector_send() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Check if the hub connector is stopped + * @param h - pointer to the hub connector + * @return status + */ +bool bsc_hub_connector_stopped(BSC_HUB_CONNECTOR_HANDLE h) +{ + BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h; + bool ret = false; + + DEBUG_PRINTF("bsc_hub_connector_stopped() >>> h = %p\n", h); + bws_dispatch_lock(); + if (c && c->state == BSC_HUB_CONNECTOR_STATE_IDLE) { + ret = true; + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_hub_connector_stopped() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Get the hub connector status + * @param h - pointer to the hub connector + * @param primary - primary or failover status + * @return pointer to the status + */ +BACNET_SC_HUB_CONNECTION_STATUS * +bsc_hub_connector_status(BSC_HUB_CONNECTOR_HANDLE h, bool primary) +{ + BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h; + BACNET_SC_HUB_CONNECTION_STATUS *ret = NULL; + bws_dispatch_lock(); + if (c) { + if (primary) { + ret = &c->primary_status; + } else { + ret = &c->failover_status; + } + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Get the hub connector state + * @param h - pointer to the hub connector + * @return state + */ +BACNET_SC_HUB_CONNECTOR_STATE +bsc_hub_connector_state(BSC_HUB_CONNECTOR_HANDLE h) +{ + BSC_HUB_CONNECTOR *c = (BSC_HUB_CONNECTOR *)h; + BACNET_SC_HUB_CONNECTOR_STATE ret = + BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION; + bws_dispatch_lock(); + if (c) { + if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_PRIMARY) { + ret = BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY; + } else if (c->state == BSC_HUB_CONNECTOR_STATE_CONNECTED_FAILOVER) { + ret = BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER; + } + } + bws_dispatch_unlock(); + return ret; +} diff --git a/src/bacnet/datalink/bsc/bsc-hub-connector.h b/src/bacnet/datalink/bsc/bsc-hub-connector.h new file mode 100644 index 00000000..3e5d74f9 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-hub-connector.h @@ -0,0 +1,81 @@ +/** + * @file + * @brief BACNet secure connect hub connector API. + * In general, user should not use that API directly, + * BACNet/SC datalink API should be used. + * @author Kirill Neznamov + * @date July 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_HUB_CONNECTOR_H +#define BACNET_DATALINK_BSC_HUB_CONNECTOR_H +#include +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/datalink/bsc/bsc-retcodes.h" + +typedef void *BSC_HUB_CONNECTOR_HANDLE; + +typedef enum { + BSC_HUBC_EVENT_CONNECTED_PRIMARY = 1, + BSC_HUBC_EVENT_CONNECTED_FAILOVER = 2, + BSC_HUBC_EVENT_ERROR_DUPLICATED_VMAC = 3, + BSC_HUBC_EVENT_RECEIVED = 4, + BSC_HUBC_EVENT_STOPPED = 5 +} BSC_HUB_CONNECTOR_EVENT; + +typedef void (*BSC_HUB_CONNECTOR_EVENT_FUNC)( + BSC_HUB_CONNECTOR_EVENT ev, + BSC_HUB_CONNECTOR_HANDLE h, + void *user_arg, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_hub_connector_start( + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + uint16_t max_local_bvlc_len, + uint16_t max_local_npdu_len, + unsigned int connect_timeout_s, + unsigned int heartbeat_timeout_s, + unsigned int disconnect_timeout_s, + char *primaryURL, + char *failoverURL, + unsigned int reconnnect_timeout_s, + BSC_HUB_CONNECTOR_EVENT_FUNC event_func, + void *user_arg, + BSC_HUB_CONNECTOR_HANDLE *h); + +BACNET_STACK_EXPORT +void bsc_hub_connector_stop(BSC_HUB_CONNECTOR_HANDLE h); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_hub_connector_send( + BSC_HUB_CONNECTOR_HANDLE h, uint8_t *pdu, size_t pdu_len); + +BACNET_STACK_EXPORT +bool bsc_hub_connector_stopped(BSC_HUB_CONNECTOR_HANDLE h); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_CONNECTOR_STATE +bsc_hub_connector_state(BSC_HUB_CONNECTOR_HANDLE h); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_CONNECTION_STATUS * +bsc_hub_connector_status(BSC_HUB_CONNECTOR_HANDLE h, bool primary); + +BACNET_STACK_EXPORT +void bsc_hub_connector_maintenance_timer(uint16_t seconds); + +#endif diff --git a/src/bacnet/datalink/bsc/bsc-hub-function.c b/src/bacnet/datalink/bsc/bsc-hub-function.c new file mode 100644 index 00000000..d23fc7f8 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-hub-function.c @@ -0,0 +1,535 @@ +/** + * @file + * @brief BACNet hub function API. + * @author Kirill Neznamov + * @date July 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include "bacnet/basic/sys/debug.h" +#include "bacnet/datalink/bsc/bsc-conf.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/bsc-socket.h" +#include "bacnet/datalink/bsc/bsc-util.h" +#include "bacnet/datalink/bsc/bsc-hub-function.h" +#include "bacnet/bacdef.h" +#include "bacnet/npdu.h" +#include "bacnet/bacenum.h" + +#define DEBUG_BSC_HUB_FUNCTION 0 + +#if DEBUG_BSC_HUB_FUNCTION == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +static BSC_SOCKET *hub_function_find_connection_for_vmac( + BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg); + +static BSC_SOCKET * +hub_function_find_connection_for_uuid(BACNET_SC_UUID *uuid, void *user_arg); + +static void hub_function_failed_request( + BSC_SOCKET_CTX *ctx, + BSC_SOCKET *c, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE error, + const char *error_desc); + +static void hub_function_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu); + +static void hub_function_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev); + +typedef enum { + BSC_HUB_FUNCTION_STATE_IDLE = 0, + BSC_HUB_FUNCTION_STATE_STARTING = 1, + BSC_HUB_FUNCTION_STATE_STARTED = 2, + BSC_HUB_FUNCTION_STATE_STOPPING = 3 +} BSC_HUB_FUNCTION_STATE; + +typedef struct BSC_Hub_Connector { + bool used; + BSC_SOCKET_CTX ctx; + BSC_CONTEXT_CFG cfg; + BSC_SOCKET sock[BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM]; + BSC_HUB_FUNCTION_STATE state; + BSC_HUB_EVENT_FUNC event_func; + void *user_arg; +} BSC_HUB_FUNCTION; + +#if BSC_CONF_HUB_FUNCTIONS_NUM > 0 +static BSC_HUB_FUNCTION bsc_hub_function[BSC_CONF_HUB_FUNCTIONS_NUM] = { 0 }; +#else +static BSC_HUB_FUNCTION *bsc_hub_function = NULL; +#endif + +static BSC_SOCKET_CTX_FUNCS bsc_hub_function_ctx_funcs = { + hub_function_find_connection_for_vmac, + hub_function_find_connection_for_uuid, hub_function_socket_event, + hub_function_context_event, hub_function_failed_request +}; + +/** + * @brief Allocate a hub function + * @return pointer to the hub function + */ +static BSC_HUB_FUNCTION *hub_function_alloc(void) +{ + int i; + for (i = 0; i < BSC_CONF_HUB_FUNCTIONS_NUM; i++) { + if (!bsc_hub_function[i].used) { + bsc_hub_function[i].used = true; + return &bsc_hub_function[i]; + } + } + return NULL; +} + +/** + * @brief Free a hub function + * @param p - pointer to the hub function + */ +static void hub_function_free(BSC_HUB_FUNCTION *p) +{ + p->used = false; +} + +/** + * @brief find a hub function connection for a specific VMAC address + * @param vmac - pointer to the VMAC address + * @param user_arg - pointer to the user argument + * @return pointer to the socket, or NULL if not found + */ +static BSC_SOCKET *hub_function_find_connection_for_vmac( + BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg) +{ + int i; + BSC_HUB_FUNCTION *f; + + bws_dispatch_lock(); + f = (BSC_HUB_FUNCTION *)user_arg; + DEBUG_PRINTF( + "hubf = %p local_vmac = %s\n", f, + bsc_vmac_to_string(&f->cfg.local_vmac)); + for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) { + DEBUG_PRINTF( + "hubf = %p, sock %p, state = %d, vmac = %s\n", f, &f->sock[i], + f->sock[i].state, bsc_vmac_to_string(&f->sock[i].vmac)); + if (f->sock[i].state != BSC_SOCK_STATE_IDLE && + !memcmp( + &vmac->address[0], &f->sock[i].vmac.address[0], + sizeof(vmac->address))) { + bws_dispatch_unlock(); + return &f->sock[i]; + } + } + bws_dispatch_unlock(); + return NULL; +} + +/** + * @brief find a hub function connection for a specific UUID + * @param uuid - pointer to the UUID + * @param user_arg - pointer to the user argument + * @return pointer to the socket, or NULL if not found + */ +static BSC_SOCKET * +hub_function_find_connection_for_uuid(BACNET_SC_UUID *uuid, void *user_arg) +{ + int i; + BSC_HUB_FUNCTION *f; + + bws_dispatch_lock(); + f = (BSC_HUB_FUNCTION *)user_arg; + for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) { + DEBUG_PRINTF( + "hubf = %p, sock %p, state = %d, uuid = %s\n", f, &f->sock[i], + f->sock[i].state, bsc_uuid_to_string(&f->sock[i].uuid)); + if (f->sock[i].state != BSC_SOCK_STATE_IDLE && + !memcmp( + &uuid->uuid[0], &f->sock[i].uuid.uuid[0], sizeof(uuid->uuid))) { + bws_dispatch_unlock(); + DEBUG_PRINTF("found socket\n"); + return &f->sock[i]; + } + } + bws_dispatch_unlock(); + return NULL; +} + +/** + * @brief update the status of the hub function + * @param f - pointer to the hub function + * @param c - pointer to the socket + * @param ev - event + * @param disconnect_reason - disconnect reason + * @param disconnect_reason_desc - disconnect reason description + */ +static void hub_function_update_status( + BSC_HUB_FUNCTION *f, + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE disconnect_reason, + const char *disconnect_reason_desc) +{ + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *s; + + if (f->user_arg) { + s = bsc_node_find_hub_status_for_vmac(f->user_arg, &c->vmac); + + if (s) { + memcpy(s->Peer_VMAC, &c->vmac.address[0], BVLC_SC_VMAC_SIZE); + memcpy( + &s->Peer_UUID.uuid.uuid128[0], &c->uuid.uuid[0], + BVLC_SC_UUID_SIZE); + if (!bsc_socket_get_peer_addr(c, &s->Peer_Address)) { + memset(&s->Peer_Address, 0, sizeof(s->Peer_Address)); + } + if (disconnect_reason_desc) { + bsc_copy_str( + s->Error_Details, disconnect_reason_desc, + sizeof(s->Error_Details)); + } else { + s->Error_Details[0] = 0; + } + s->Error = ERROR_CODE_DEFAULT; + if (ev == BSC_SOCKET_EVENT_CONNECTED) { + s->State = BACNET_SC_CONNECTION_STATE_CONNECTED; + bsc_set_timestamp(&s->Connect_Timestamp); + memset( + &s->Disconnect_Timestamp, 0xff, + sizeof(s->Disconnect_Timestamp)); + } else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) { + bsc_set_timestamp(&s->Disconnect_Timestamp); + if (disconnect_reason == ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER || + disconnect_reason == ERROR_CODE_SUCCESS) { + s->State = BACNET_SC_CONNECTION_STATE_NOT_CONNECTED; + } else { + s->State = + BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS; + s->Error = disconnect_reason; + } + } + } + } +} + +/** + * @brief Handle a hub function failed request + * @param ctx - pointer to the socket context + * @param c - pointer to the socket + * @param vmac - pointer to the VMAC address + * @param uuid - pointer to the UUID + * @param error - error code + * @param error_desc - pointer to the error description + */ +static void hub_function_failed_request( + BSC_SOCKET_CTX *ctx, + BSC_SOCKET *c, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE error, + const char *error_desc) +{ + BSC_HUB_FUNCTION *f; + BACNET_HOST_N_PORT_DATA peer; + (void)ctx; + + bws_dispatch_lock(); + f = (BSC_HUB_FUNCTION *)c->ctx->user_arg; + if (f->user_arg) { + if (bsc_socket_get_peer_addr(c, &peer)) { + bsc_node_store_failed_request_info( + (BSC_NODE *)f->user_arg, &peer, vmac, uuid, error, error_desc); + } + } + bws_dispatch_unlock(); +} + +/** + * @brief Handle a hub function socket event + * @param c - pointer to the socket + * @param ev - event + * @param reason - disconnect reason + * @param reason_desc - disconnect reason description + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @param decoded_pdu - decoded PDU + */ +static void hub_function_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + BSC_SOCKET *dst; + BSC_SC_RET ret; + int i; + uint8_t *p_pdu; + BSC_HUB_FUNCTION *f; + size_t len; + + DEBUG_PRINTF( + "hub_function_socket_event() >>> c = %p, ev = %d, reason = " + "%d, desc = %p, pdu = %p, pdu_len = %d, decoded_pdu = %p\n", + c, ev, reason, reason_desc, pdu, pdu_len, decoded_pdu); + + bws_dispatch_lock(); + f = (BSC_HUB_FUNCTION *)c->ctx->user_arg; + if (ev == BSC_SOCKET_EVENT_RECEIVED) { + /* double check that received message does not contain */ + /* originating virtual address and contains dest vaddr */ + /* although such kind of check is already in bsc-socket.c */ + if (!decoded_pdu->hdr.origin && decoded_pdu->hdr.dest) { + if (bvlc_sc_is_vmac_broadcast(decoded_pdu->hdr.dest)) { + if (bsc_socket_get_global_buf_size() >= pdu_len) { + p_pdu = bsc_socket_get_global_buf(); + len = pdu_len; + memcpy(p_pdu, pdu, len); + + for (i = 0; i < sizeof(f->sock) / sizeof(BSC_SOCKET); i++) { + if (&f->sock[i] != c && + f->sock[i].state == BSC_SOCK_STATE_CONNECTED) { + /* change origin address if presented or add origin + */ + /* address into pdu by extending of it's header */ + len = (uint16_t)bvlc_sc_set_orig( + &p_pdu, len, &c->vmac); + ret = bsc_send(&f->sock[i], p_pdu, len); + (void)ret; +#if DEBUG_ENABLED == 1 + if (ret != BSC_SC_SUCCESS) { + DEBUG_PRINTF( + "sending of reconstructed pdu failed, " + "err = %d\n", + ret); + } +#endif + } + } + } +#if DEBUG_ENABLED == 1 + else { + DEBUG_PRINTF("pdu with len = %d is dropped\n", pdu_len); + } +#endif + } else { + dst = hub_function_find_connection_for_vmac( + decoded_pdu->hdr.dest, (void *)f); + if (!dst) { + DEBUG_PRINTF( + "can not find socket, hub dropped pdu of size " + "%d for dest vmac %s\n", + pdu_len, bsc_vmac_to_string(decoded_pdu->hdr.dest)); + } else { + bvlc_sc_remove_dest_set_orig(pdu, pdu_len, &c->vmac); + ret = bsc_send(dst, pdu, pdu_len); + (void)ret; +#if DEBUG_ENABLED == 1 + if (ret != BSC_SC_SUCCESS) { + DEBUG_PRINTF( + "sending of pdu of %d bytes failed, err = %d\n", + pdu_len, ret); + } +#endif + } + } + } + } else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) { + hub_function_update_status(f, c, ev, reason, reason_desc); + if (reason == ERROR_CODE_NODE_DUPLICATE_VMAC) { + f->event_func( + BSC_HUBF_EVENT_ERROR_DUPLICATED_VMAC, + (BSC_HUB_FUNCTION_HANDLE)f, f->user_arg); + } + } else if (ev == BSC_SOCKET_EVENT_CONNECTED) { + hub_function_update_status(f, c, ev, reason, reason_desc); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("hub_function_socket_event() <<<\n"); +} + +/** + * @brief Handle a hub function context event + * @param ctx - pointer to the socket context + * @param ev - event + */ +static void hub_function_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev) +{ + BSC_HUB_FUNCTION *f; + bws_dispatch_lock(); + f = (BSC_HUB_FUNCTION *)ctx->user_arg; + if (ev == BSC_CTX_INITIALIZED) { + f->state = BSC_HUB_FUNCTION_STATE_STARTED; + f->event_func( + BSC_HUBF_EVENT_STARTED, (BSC_HUB_FUNCTION_HANDLE)f, f->user_arg); + } else if (ev == BSC_CTX_DEINITIALIZED) { + f->state = BSC_HUB_FUNCTION_STATE_IDLE; + hub_function_free(f); + f->event_func( + BSC_HUBF_EVENT_STOPPED, (BSC_HUB_FUNCTION_HANDLE)f, f->user_arg); + } + bws_dispatch_unlock(); +} + +/** + * @brief Start a BACnet hub function + * @param ca_cert_chain - pointer to the CA certificate chain + * @param ca_cert_chain_size - size of the CA certificate chain + * @param cert_chain - pointer to the certificate chain + * @param cert_chain_size - size of the certificate chain + * @param key - pointer to the private key + * @param key_size - size of the private key + * @param port - port number + * @param iface - pointer to the interface + * @param local_uuid - pointer to the local UUID + * @param local_vmac - pointer to the local VMAC address + * @param max_local_bvlc_len - maximum local BVLC length + * @param max_local_npdu_len - maximum local NPDU length + * @param connect_timeout_s - connection timeout in seconds + * @param heartbeat_timeout_s - heartbeat timeout in seconds + * @param disconnect_timeout_s - disconnect timeout in seconds + * @param event_func - pointer to the event function + * @param user_arg - pointer to the user argument + * @param h - pointer to the hub function handle + * @return BACnet/SC status + */ +BSC_SC_RET bsc_hub_function_start( + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + int port, + char *iface, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + uint16_t max_local_bvlc_len, + uint16_t max_local_npdu_len, + unsigned int connect_timeout_s, + unsigned int heartbeat_timeout_s, + unsigned int disconnect_timeout_s, + BSC_HUB_EVENT_FUNC event_func, + void *user_arg, + BSC_HUB_FUNCTION_HANDLE *h) +{ + BSC_SC_RET ret; + BSC_HUB_FUNCTION *f; + + DEBUG_PRINTF("bsc_hub_function_start() >>>\n"); + + if (!ca_cert_chain || !ca_cert_chain_size || !cert_chain || + !cert_chain_size || !key || !key_size || !local_uuid || !local_vmac || + !max_local_npdu_len || !max_local_bvlc_len || !connect_timeout_s || + !heartbeat_timeout_s || !disconnect_timeout_s || !event_func || !h) { + DEBUG_PRINTF("bsc_hub_function_start() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + *h = NULL; + + bws_dispatch_lock(); + f = hub_function_alloc(); + + if (!f) { + bws_dispatch_unlock(); + DEBUG_PRINTF( + "bsc_hub_function_start() <<< ret = BSC_SC_NO_RESOURCES\n"); + return BSC_SC_NO_RESOURCES; + } + + f->user_arg = user_arg; + f->event_func = event_func; + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_ACCEPTOR, &f->cfg, BSC_WEBSOCKET_HUB_PROTOCOL, port, + iface, ca_cert_chain, ca_cert_chain_size, cert_chain, cert_chain_size, + key, key_size, local_uuid, local_vmac, max_local_bvlc_len, + max_local_npdu_len, connect_timeout_s, heartbeat_timeout_s, + disconnect_timeout_s); + + ret = bsc_init_ctx( + &f->ctx, &f->cfg, &bsc_hub_function_ctx_funcs, f->sock, + sizeof(f->sock) / sizeof(BSC_SOCKET), f); + + if (ret == BSC_SC_SUCCESS) { + f->state = BSC_HUB_FUNCTION_STATE_STARTING; + *h = (BSC_HUB_FUNCTION_HANDLE)f; + } else { + hub_function_free(f); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_hub_function_start() << ret = %d\n", ret); + return ret; +} + +/** + * @brief Stop a BACnet hub function + * @param h - pointer to the hub function handle + */ +void bsc_hub_function_stop(BSC_HUB_FUNCTION_HANDLE h) +{ + BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h; + DEBUG_PRINTF("bsc_hub_function_stop() >>> h = %p\n", h); + bws_dispatch_lock(); + if (f && f->state != BSC_HUB_FUNCTION_STATE_IDLE && + f->state != BSC_HUB_FUNCTION_STATE_STOPPING) { + f->state = BSC_HUB_FUNCTION_STATE_STOPPING; + bsc_deinit_ctx(&f->ctx); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_hub_function_stop() <<<\n"); +} + +/** + * @brief Check if the hub function is stopped + * @param h - pointer to the hub function handle + * @return true if the hub function is stopped, false otherwise + */ +bool bsc_hub_function_stopped(BSC_HUB_FUNCTION_HANDLE h) +{ + BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h; + bool ret = false; + + DEBUG_PRINTF("bsc_hub_function_stopped() >>> h = %p\n", h); + bws_dispatch_lock(); + if (f && f->state == BSC_HUB_FUNCTION_STATE_IDLE) { + ret = true; + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_hub_function_stopped() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Check if the hub function is started + * @param h - pointer to the hub function handle + * @return true if the hub function is started, false otherwise + */ +bool bsc_hub_function_started(BSC_HUB_FUNCTION_HANDLE h) +{ + BSC_HUB_FUNCTION *f = (BSC_HUB_FUNCTION *)h; + bool ret = false; + + DEBUG_PRINTF("bsc_hub_function_started() >>> h = %p\n", h); + bws_dispatch_lock(); + if (f && f->state == BSC_HUB_FUNCTION_STATE_STARTED) { + ret = true; + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_hub_function_started() <<< ret = %d\n", ret); + return ret; +} diff --git a/src/bacnet/datalink/bsc/bsc-hub-function.h b/src/bacnet/datalink/bsc/bsc-hub-function.h new file mode 100644 index 00000000..ac74a266 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-hub-function.h @@ -0,0 +1,62 @@ +/** + * @file + * @brief BACNet secure connect hub function API. + * In general, user should not use that API directly, + * BACNet/SC datalink API should be used. + * @author Kirill Neznamov + * @date July 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_HUB_FUNCTION_H +#define BACNET_DATALINK_BSC_HUB_FUNCTION_H +#include +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/datalink/bsc/bsc-retcodes.h" +#include "bacnet/basic/object/sc_netport.h" + +typedef void *BSC_HUB_FUNCTION_HANDLE; + +typedef enum { + BSC_HUBF_EVENT_STARTED = 1, + BSC_HUBF_EVENT_STOPPED = 2, + BSC_HUBF_EVENT_ERROR_DUPLICATED_VMAC = 3 +} BSC_HUB_FUNCTION_EVENT; + +typedef void (*BSC_HUB_EVENT_FUNC)( + BSC_HUB_FUNCTION_EVENT ev, BSC_HUB_FUNCTION_HANDLE h, void *user_arg); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_hub_function_start( + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + int port, + char *iface, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + uint16_t max_local_bvlc_len, + uint16_t max_local_npdu_len, + unsigned int connect_timeout_s, + unsigned int heartbeat_timeout_s, + unsigned int disconnect_timeout_s, + BSC_HUB_EVENT_FUNC event_func, + void *user_arg, + BSC_HUB_FUNCTION_HANDLE *h); + +BACNET_STACK_EXPORT +void bsc_hub_function_stop(BSC_HUB_FUNCTION_HANDLE h); + +BACNET_STACK_EXPORT +bool bsc_hub_function_stopped(BSC_HUB_FUNCTION_HANDLE h); + +BACNET_STACK_EXPORT +bool bsc_hub_function_started(BSC_HUB_FUNCTION_HANDLE h); + +#endif diff --git a/src/bacnet/datalink/bsc/bsc-node-switch.c b/src/bacnet/datalink/bsc/bsc-node-switch.c new file mode 100644 index 00000000..27c2901c --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-node-switch.c @@ -0,0 +1,1359 @@ +/** + * @file + * @brief BACNet secure connect node switch function API. + * @author Kirill Neznamov + * @date October 2022 + * @copyright SPDX-License-Identifier: GPL-2\.0-or-later WITH GCC-exception-2.0 + */ +#include "bacnet/basic/sys/debug.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/bsc-socket.h" +#include "bacnet/datalink/bsc/bsc-util.h" +#include "bacnet/datalink/bsc/bsc-node-switch.h" +#include "bacnet/datalink/bsc/bsc-hub-connector.h" +#include "bacnet/datalink/bsc/bsc-node.h" +#include "bacnet/bacdef.h" +#include "bacnet/npdu.h" +#include "bacnet/bacenum.h" + +#define DEBUG_BSC_NODE_SWITCH 0 + +#if DEBUG_BSC_NODE_SWITCH == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +typedef enum { + BSC_NODE_SWITCH_STATE_IDLE = 0, + BSC_NODE_SWITCH_STATE_STARTING = 1, + BSC_NODE_SWITCH_STATE_STARTED = 2, + BSC_NODE_SWITCH_STATE_STOPPING = 3 +} BSC_NODE_SWITCH_STATE; + +typedef enum { + BSC_NODE_SWITCH_CONNECTION_STATE_IDLE = 0, + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_CONNECTION = 1, + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_RESOLUTION = 2, + BSC_NODE_SWITCH_CONNECTION_STATE_CONNECTED = 3, + BSC_NODE_SWITCH_CONNECTION_STATE_DELAYING = 4, + BSC_NODE_SWITCH_CONNECTION_STATE_LOCAL_DISCONNECT = 5 +} BSC_NODE_SWITCH_CONNECTION_STATE; + +static BSC_SOCKET *node_switch_acceptor_find_connection_for_vmac( + BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg); + +static BSC_SOCKET *node_switch_acceptor_find_connection_for_uuid( + BACNET_SC_UUID *uuid, void *user_arg); + +static void node_switch_acceptor_failed_request( + BSC_SOCKET_CTX *ctx, + BSC_SOCKET *c, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE error, + const char *error_desc); + +static void node_switch_acceptor_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu); + +static void +node_switch_acceptor_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev); + +static void node_switch_initiator_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu); + +static void +node_switch_initiator_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev); + +typedef struct BSC_Node_Switch_Acceptor { + BSC_SOCKET_CTX ctx; + BSC_CONTEXT_CFG cfg; + BSC_SOCKET sock[BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM]; + BSC_NODE_SWITCH_STATE state; + BACNET_SC_DIRECT_CONNECTION_STATUS *status; +} BSC_NODE_SWITCH_ACCEPTOR; + +typedef struct { + uint8_t utf8_urls[BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK] + [BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK + 1]; + size_t urls_cnt; + int url_elem; +} BSC_NODE_SWITCH_URLS; + +typedef struct BSC_Node_Switch_Initiator { + BSC_SOCKET_CTX ctx; + BSC_CONTEXT_CFG cfg; + BSC_SOCKET sock[BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM]; + BSC_NODE_SWITCH_CONNECTION_STATE + sock_state[BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM]; + BACNET_SC_VMAC_ADDRESS dest_vmac[BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM]; + struct mstimer t[BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM]; + BSC_NODE_SWITCH_URLS urls[BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM]; + BSC_NODE_SWITCH_STATE state; +} BSC_NODE_SWITCH_INITIATOR; + +typedef struct { + bool used; + BSC_NODE_SWITCH_ACCEPTOR acceptor; + BSC_NODE_SWITCH_INITIATOR initiator; + BSC_NODE_SWITCH_EVENT_FUNC event_func; + unsigned int reconnect_timeout_s; + unsigned int address_resolution_timeout_s; + bool direct_connect_accept_enable; + bool direct_connect_initiate_enable; + void *user_arg; +} BSC_NODE_SWITCH_CTX; + +#if BSC_CONF_NODE_SWITCHES_NUM > 0 +static BSC_NODE_SWITCH_CTX bsc_node_switch[BSC_CONF_NODE_SWITCHES_NUM] = { 0 }; +#else +static BSC_NODE_SWITCH_CTX *bsc_node_switch = NULL; +#endif + +static BSC_SOCKET_CTX_FUNCS bsc_node_switch_acceptor_ctx_funcs = { + node_switch_acceptor_find_connection_for_vmac, + node_switch_acceptor_find_connection_for_uuid, + node_switch_acceptor_socket_event, node_switch_acceptor_context_event, + node_switch_acceptor_failed_request +}; + +static BSC_SOCKET_CTX_FUNCS bsc_node_switch_initiator_ctx_funcs = { + NULL, NULL, node_switch_initiator_socket_event, + node_switch_initiator_context_event, NULL +}; + +static int node_switch_acceptor_find_connection_index_for_vmac( + BACNET_SC_VMAC_ADDRESS *vmac, BSC_NODE_SWITCH_CTX *ctx); + +/** + * @brief Allocate a node switch context + * @return pointer to the allocated node switch context + */ +static BSC_NODE_SWITCH_CTX *node_switch_alloc(void) +{ + int i; + + for (i = 0; i < BSC_CONF_NODE_SWITCHES_NUM; i++) { + if (!bsc_node_switch[i].used) { + bsc_node_switch[i].used = true; + memset( + &bsc_node_switch[i].initiator, 0, + sizeof(bsc_node_switch[i].initiator)); + memset( + &bsc_node_switch[i].acceptor, 0, + sizeof(bsc_node_switch[i].acceptor)); + return &bsc_node_switch[i]; + } + } + return NULL; +} + +/** + * @brief Free the node switch context + */ +static void node_switch_free(BSC_NODE_SWITCH_CTX *ctx) +{ + ctx->used = false; +} + +/** + * @brief Update the status of the node switch + * @param ctx - pointer to the node switch context + * @param initiator - true if the initiator, otherwise false + * @param failed_to_connect - true if failed to connect, otherwise false + * @param URI - pointer to the URI + * @param c - pointer to the socket + * @param ev - socket event + * @param reason - error code + * @param reason_desc - pointer to the reason description + */ +static void node_switch_update_status( + BSC_NODE_SWITCH_CTX *ctx, + bool initiator, + bool failed_to_connect, + const char *URI, + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc) +{ + BACNET_SC_DIRECT_CONNECTION_STATUS *s; + + DEBUG_PRINTF( + "node_switch_update_status() >>> ctx = %p, initiator = %d, " + "failed_to_connect = %d, URI = %p, c = %p, ev = %d, reason = " + "%d, reason_desc = %p\n", + ctx, initiator, failed_to_connect, URI, c, ev, reason, reason_desc); + + if (ctx->user_arg) { + s = bsc_node_find_direct_status_for_vmac(ctx->user_arg, &c->vmac); + + if (s) { + DEBUG_PRINTF( + "found existent status entry %p for vmac = %s\n", s, + bsc_vmac_to_string(&c->vmac)); + if (!initiator) { + s->URI[0] = 0; + } else if (URI) { + bsc_copy_str(s->URI, URI, sizeof(s->URI)); + } + memcpy(s->Peer_VMAC, &c->vmac.address[0], BVLC_SC_VMAC_SIZE); + memcpy( + &s->Peer_UUID.uuid.uuid128[0], &c->uuid.uuid[0], + BVLC_SC_UUID_SIZE); + if (reason_desc) { + bsc_copy_str( + s->Error_Details, reason_desc, sizeof(s->Error_Details)); + } else { + s->Error_Details[0] = 0; + } + s->Error = reason; + if (ev == BSC_SOCKET_EVENT_CONNECTED) { + DEBUG_PRINTF("set status state to CONNECTED\n"); + if (!bsc_socket_get_peer_addr(c, &s->Peer_Address)) { + memset(&s->Peer_Address, 0, sizeof(s->Peer_Address)); + } + s->State = BACNET_SC_CONNECTION_STATE_CONNECTED; + bsc_set_timestamp(&s->Connect_Timestamp); + memset( + &s->Disconnect_Timestamp, 0xFF, + sizeof(s->Disconnect_Timestamp)); + } else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) { + bsc_set_timestamp(&s->Disconnect_Timestamp); + if (reason == ERROR_CODE_WEBSOCKET_CLOSED_BY_PEER || + reason == ERROR_CODE_SUCCESS) { + s->State = BACNET_SC_CONNECTION_STATE_NOT_CONNECTED; + DEBUG_PRINTF("set status state to NOT-CONNECTED\n"); + } else { + s->State = + BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS; + DEBUG_PRINTF( + "set status state to DISCONNECTED-WITH-ERRORS\n"); + } + if (failed_to_connect) { + s->State = BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT; + DEBUG_PRINTF("set status state to FAILED-TO-CONNECT\n"); + } + } + } + } + + DEBUG_PRINTF("node_switch_update_status() <<<\n"); +} + +/** + * @brief Copy URLs from the address resolution to the node switch context + * @param ctx - pointer to the node switch context + * @param index - index of the URL + * @param r - pointer to the address resolution + */ +static void +copy_urls(BSC_NODE_SWITCH_CTX *ctx, int index, BSC_ADDRESS_RESOLUTION *r) +{ + int i; + for (i = 0; i < r->urls_num; i++) { + ctx->initiator.urls[index].utf8_urls[i][0] = 0; + strcpy( + (char *)&ctx->initiator.urls[index].utf8_urls[i][0], + (char *)&r->utf8_urls[i][0]); + } + ctx->initiator.urls[index].urls_cnt = r->urls_num; +} + +/** + * @brief Copy URLs from the array to the node switch context + * @param ctx - pointer to the node switch context + * @param index - index of the URL + * @param urls - pointer to the array of URLs + * @param urls_cnt - number of URLs + */ +static void +copy_urls2(BSC_NODE_SWITCH_CTX *ctx, int index, char **urls, size_t urls_cnt) +{ + size_t i; + for (i = 0; i < urls_cnt; i++) { + ctx->initiator.urls[index].utf8_urls[i][0] = 0; + strcpy((char *)&ctx->initiator.urls[index].utf8_urls[i][0], urls[i]); + } + ctx->initiator.urls[index].urls_cnt = urls_cnt; +} + +/** + * @brief Find the connection socket for a specific VMAC address + * @param vmac - pointer to the VMAC address + * @param user_arg - pointer to the user argument + * @return pointer to the socket, or NULL if not found + */ +static BSC_SOCKET *node_switch_acceptor_find_connection_for_vmac( + BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg) +{ + int i; + BSC_NODE_SWITCH_CTX *c; + bws_dispatch_lock(); + c = (BSC_NODE_SWITCH_CTX *)user_arg; + for (i = 0; i < sizeof(c->acceptor.sock) / sizeof(BSC_SOCKET); i++) { + DEBUG_PRINTF( + "ns = %p, sock %p, state = %d, vmac = %s\n", c, + &c->acceptor.sock[i], c->acceptor.sock[i].state, + bsc_vmac_to_string(&c->acceptor.sock[i].vmac)); + if (c->acceptor.sock[i].state != BSC_SOCK_STATE_IDLE && + !memcmp( + &vmac->address[0], &c->acceptor.sock[i].vmac.address[0], + sizeof(vmac->address))) { + bws_dispatch_unlock(); + return &c->acceptor.sock[i]; + } + } + bws_dispatch_unlock(); + return NULL; +} + +/** + * @brief Find the connection socket for a specific UUID + * @param uuid - pointer to the UUID + * @param user_arg - pointer to the user argument + * @return pointer to the socket, or NULL if not found + */ +static BSC_SOCKET *node_switch_acceptor_find_connection_for_uuid( + BACNET_SC_UUID *uuid, void *user_arg) +{ + int i; + BSC_NODE_SWITCH_CTX *c; + bws_dispatch_lock(); + c = (BSC_NODE_SWITCH_CTX *)user_arg; + for (i = 0; i < sizeof(c->acceptor.sock) / sizeof(BSC_SOCKET); i++) { + DEBUG_PRINTF( + "ns = %p, sock %p, state = %d, uuid = %s\n", c, + &c->acceptor.sock[i], c->acceptor.sock[i].state, + bsc_uuid_to_string(&c->acceptor.sock[i].uuid)); + if (c->acceptor.sock[i].state != BSC_SOCK_STATE_IDLE && + !memcmp( + &uuid->uuid[0], &c->acceptor.sock[i].uuid.uuid[0], + sizeof(uuid->uuid))) { + DEBUG_PRINTF("found\n"); + bws_dispatch_unlock(); + return &c->acceptor.sock[i]; + } + } + bws_dispatch_unlock(); + return NULL; +} + +/** + * @brief Handle a node switch acceptor failed request + * @param ctx - pointer to the socket context + * @param c - pointer to the socket + * @param vmac - pointer to the VMAC address + * @param uuid - pointer to the UUID + * @param error - error code + * @param error_desc - pointer to the error description + */ +static void node_switch_acceptor_failed_request( + BSC_SOCKET_CTX *ctx, + BSC_SOCKET *c, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE error, + const char *error_desc) +{ + BSC_NODE_SWITCH_CTX *ns; + BACNET_HOST_N_PORT_DATA peer; + (void)ctx; + + bws_dispatch_lock(); + ns = (BSC_NODE_SWITCH_CTX *)c->ctx->user_arg; + if (ns->user_arg) { + if (bsc_socket_get_peer_addr(c, &peer)) { + bsc_node_store_failed_request_info( + (BSC_NODE *)ns->user_arg, &peer, vmac, uuid, error, error_desc); + } + } + bws_dispatch_unlock(); +} + +/** + * @brief Handle a node switch acceptor socket event + * @param c - pointer to the socket + * @param ev - event + * @param disconnect_reason - disconnect reason + * @param disconnect_reason_desc - disconnect reason description + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @param decoded_pdu - decoded PDU + */ +static void node_switch_acceptor_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE disconnect_reason, + const char *disconnect_reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + uint8_t *p_pdu; + BSC_NODE_SWITCH_CTX *ctx; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc; + bool res = true; + + DEBUG_PRINTF( + "node_switch_acceptor_socket_event() >>> c %p, ev = %d\n", c, ev); + + bws_dispatch_lock(); + ctx = (BSC_NODE_SWITCH_CTX *)c->ctx->user_arg; + + /* Node switch does not take care about incoming connection status, */ + /* so just route PDUs to upper layer */ + + if (ctx->acceptor.state == BSC_NODE_SWITCH_STATE_STARTED) { + if (ev == BSC_SOCKET_EVENT_RECEIVED) { + if (bsc_socket_get_global_buf_size() >= pdu_len) { + p_pdu = bsc_socket_get_global_buf(); + memcpy(p_pdu, pdu, pdu_len); + pdu_len = bvlc_sc_set_orig(&p_pdu, pdu_len, &c->vmac); + res = bvlc_sc_decode_message( + p_pdu, pdu_len, decoded_pdu, &error, &class, &err_desc); + if (res) { + ctx->event_func( + BSC_NODE_SWITCH_EVENT_RECEIVED, ctx, ctx->user_arg, + NULL, p_pdu, pdu_len, decoded_pdu); + } + } + } else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) { + node_switch_update_status( + ctx, false, false, NULL, c, ev, disconnect_reason, + disconnect_reason_desc); + if (disconnect_reason == ERROR_CODE_NODE_DUPLICATE_VMAC) { + ctx->event_func( + BSC_NODE_SWITCH_EVENT_DUPLICATED_VMAC, ctx, ctx->user_arg, + NULL, NULL, 0, NULL); + } + } else if (ev == BSC_SOCKET_EVENT_CONNECTED) { + node_switch_update_status( + ctx, false, false, NULL, c, ev, ERROR_CODE_DEFAULT, NULL); + } + } + bws_dispatch_unlock(); + DEBUG_PRINTF("node_switch_acceptor_socket_event() <<<\n"); +} + +/** + * @brief Deinialize the node switch context + * @param ns - pointer to the node switch context + */ +static void node_switch_context_deinitialized(BSC_NODE_SWITCH_CTX *ns) +{ + if (ns->initiator.state == BSC_NODE_SWITCH_STATE_IDLE && + ns->acceptor.state == BSC_NODE_SWITCH_STATE_IDLE) { + node_switch_free(ns); + ns->event_func( + BSC_NODE_SWITCH_EVENT_STOPPED, ns, ns->user_arg, NULL, NULL, 0, + NULL); + } +} + +/** + * @brief Handle a node switch acceptor context event + * @param ctx - pointer to the socket context + * @param ev - event + */ +static void +node_switch_acceptor_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev) +{ + BSC_NODE_SWITCH_CTX *ns; + + bws_dispatch_lock(); + DEBUG_PRINTF( + "node_switch_acceptor_context_event() >>> ctx = %p, ev = %d, " + "user_arg = %p\n", + ctx, ev, ctx->user_arg); + ns = (BSC_NODE_SWITCH_CTX *)ctx->user_arg; + if (ev == BSC_CTX_INITIALIZED) { + if (ns->acceptor.state == BSC_NODE_SWITCH_STATE_STARTING) { + ns->acceptor.state = BSC_NODE_SWITCH_STATE_STARTED; + ns->event_func( + BSC_NODE_SWITCH_EVENT_STARTED, ns, ns->user_arg, NULL, NULL, 0, + NULL); + } + } else if (ev == BSC_CTX_DEINITIALIZED) { + ns->acceptor.state = BSC_NODE_SWITCH_STATE_IDLE; + node_switch_context_deinitialized(ns); + } + DEBUG_PRINTF("node_switch_acceptor_context_event() <<<\n"); + bws_dispatch_unlock(); +} + +/** + * @brief find a node switch initiator connection index for a specific + * VMAC address + * @param vmac - pointer to the VMAC address + * @param ctx - pointer to the node switch context + * @return connection index, or -1 if not found + */ +static int node_switch_initiator_find_connection_index_for_vmac( + BACNET_SC_VMAC_ADDRESS *vmac, BSC_NODE_SWITCH_CTX *ctx) +{ + int i; + + for (i = 0; i < sizeof(ctx->initiator.sock) / sizeof(BSC_SOCKET); i++) { + if (ctx->initiator.sock_state[i] != + BSC_NODE_SWITCH_CONNECTION_STATE_IDLE && + !memcmp( + &ctx->initiator.dest_vmac[i].address[0], &vmac->address[0], + sizeof(vmac->address))) { + return i; + } + } + return -1; +} + +/** + * @brief find a node switch acceptor connection index for a specific VMAC + * address + * @param vmac - pointer to the VMAC address + * @param ctx - pointer to the node switch context + * @return connection index, or -1 if not found + */ +static int node_switch_acceptor_find_connection_index_for_vmac( + BACNET_SC_VMAC_ADDRESS *vmac, BSC_NODE_SWITCH_CTX *ctx) +{ + int i; + + for (i = 0; i < sizeof(ctx->acceptor.sock) / sizeof(BSC_SOCKET); i++) { + if (ctx->acceptor.sock[i].state == BSC_SOCK_STATE_CONNECTED && + !memcmp( + &ctx->acceptor.sock[i].vmac.address[0], &vmac->address[0], + sizeof(vmac->address))) { + return i; + } + } + return -1; +} + +/** + * @brief find a node switch initiator connection index + * @param ctx - pointer to the node switch context + * @param c - pointer to the socket + * @return node switch initiator index, or -1 if not found + */ +static int +node_switch_initiator_get_index(BSC_NODE_SWITCH_CTX *ctx, BSC_SOCKET *c) +{ + return ((c - &ctx->initiator.sock[0]) >= 0) + ? (int)(c - &ctx->initiator.sock[0]) + : -1; +} + +/** + * @brief allocate a socket for the node switch initiator + * @param ctx - pointer to the node switch context + * @return socket index, or -1 if not found + */ +static int node_switch_initiator_alloc_sock(BSC_NODE_SWITCH_CTX *ctx) +{ + int i; + for (i = 0; i < sizeof(ctx->initiator.sock) / sizeof(BSC_SOCKET); i++) { + if (ctx->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_IDLE) { + return i; + } + } + return -1; +} + +/** + * @brief Connect to the next URL + * @param ctx - pointer to the node switch context + * @param index - index of the URL + */ +static void connect_next_url(BSC_NODE_SWITCH_CTX *ctx, int index) +{ + BSC_SC_RET ret = BSC_SC_BAD_PARAM; + + while (ret != BSC_SC_SUCCESS) { + if (ctx->initiator.urls[index].url_elem >= + ctx->initiator.urls[index].urls_cnt) { + ctx->initiator.sock_state[index] = + BSC_NODE_SWITCH_CONNECTION_STATE_DELAYING; + mstimer_set( + &ctx->initiator.t[index], ctx->reconnect_timeout_s * 1000); + ctx->initiator.urls[index].url_elem = 0; + break; + } else { + ctx->initiator.sock_state[index] = + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_CONNECTION; + ret = bsc_connect( + &ctx->initiator.ctx, &ctx->initiator.sock[index], + (char *)&ctx->initiator.urls[index] + .utf8_urls[ctx->initiator.urls[index].url_elem][0]); + ctx->initiator.urls[index].url_elem++; + } + } +} + +/** + * @brief Connect to the next URL or delay + * @param ns - pointer to the node switch context + * @param dest - pointer to the VMAC address + * @param sock_index - socket index + */ +static void node_switch_connect_or_delay( + BSC_NODE_SWITCH_CTX *ns, BACNET_SC_VMAC_ADDRESS *dest, int sock_index) +{ + BSC_ADDRESS_RESOLUTION *r; + + if (ns->initiator.urls[sock_index].urls_cnt > 0) { + connect_next_url(ns, sock_index); + } else if (dest) { + r = bsc_node_get_address_resolution(ns->user_arg, dest); + if (r && r->urls_num) { + copy_urls(ns, sock_index, r); + ns->initiator.urls[sock_index].url_elem = 0; + connect_next_url(ns, sock_index); + } else { + ns->initiator.sock_state[sock_index] = + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_RESOLUTION; + ns->initiator.urls[sock_index].urls_cnt = 0; + memcpy( + &ns->initiator.dest_vmac[sock_index].address[0], + &dest->address[0], BVLC_SC_VMAC_SIZE); + mstimer_set( + &ns->initiator.t[sock_index], + ns->address_resolution_timeout_s * 1000); + (void)bsc_node_send_address_resolution( + ns->user_arg, &ns->initiator.dest_vmac[sock_index]); + } + } +} + +/** + * @brief Process a node switch initiator runloop + * @param ns - pointer to the node switch context + */ +static void node_switch_initiator_runloop(BSC_NODE_SWITCH_CTX *ns) +{ + int i; + + /* DEBUG_PRINTF("node_switch_initiator_runloop() >>> ctx = %p\n", ns); */ + + for (i = 0; i < sizeof(ns->initiator.sock) / sizeof(BSC_SOCKET); i++) { + /* DEBUG_PRINTF("node_switch_initiator_runloop() socket %d state %d\n", + */ + /* i, ns->initiator.sock_state[i]); */ + if (ns->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_DELAYING) { + if (mstimer_expired(&ns->initiator.t[i])) { + ns->initiator.urls[i].url_elem = 0; + ns->initiator.sock_state[i] = + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_CONNECTION; + node_switch_connect_or_delay( + ns, &ns->initiator.dest_vmac[i], i); + } + } else if ( + ns->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_RESOLUTION) { + if (mstimer_expired(&ns->initiator.t[i])) { + ns->initiator.sock_state[i] = + BSC_NODE_SWITCH_CONNECTION_STATE_DELAYING; + mstimer_set( + &ns->initiator.t[i], ns->reconnect_timeout_s * 1000); + } + } + } +} + +/** + * @brief Run the node switch maintenance timer + * @param seconds - number of seconds elapsed from the previous call + */ +void bsc_node_switch_maintenance_timer(uint16_t seconds) +{ + int i; + (void)seconds; + + bws_dispatch_lock(); + for (i = 0; i < BSC_CONF_NODE_SWITCHES_NUM; i++) { + if (bsc_node_switch[i].used) { + node_switch_initiator_runloop(&bsc_node_switch[i]); + } + } + bws_dispatch_unlock(); +} + +/** + * @brief Handle a node switch initiator socket event + * @param c - pointer to the socket + * @param ev - event + * @param disconnect_reason - disconnect reason + * @param disconnect_reason_desc - disconnect reason description + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @param decoded_pdu - decoded PDU + */ +static void node_switch_initiator_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE disconnect_reason, + const char *disconnect_reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + int index; + uint8_t *p_pdu = NULL; + BSC_NODE_SWITCH_CTX *ns; + int elem; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc; + bool res; + + DEBUG_PRINTF( + "node_switch_initiator_socket_event() >>> c %p, ev = %d\n", c, ev); + + bws_dispatch_lock(); + + ns = (BSC_NODE_SWITCH_CTX *)c->ctx->user_arg; + + if (ns->initiator.state == BSC_NODE_SWITCH_STATE_STARTED) { + if (ev == BSC_SOCKET_EVENT_DISCONNECTED && + disconnect_reason == ERROR_CODE_NODE_DUPLICATE_VMAC) { + ns->event_func( + BSC_NODE_SWITCH_EVENT_DUPLICATED_VMAC, ns, ns->user_arg, NULL, + NULL, 0, NULL); + } else if (ev == BSC_SOCKET_EVENT_RECEIVED) { + if (bsc_socket_get_global_buf_size() >= pdu_len) { + p_pdu = bsc_socket_get_global_buf(); + memcpy(p_pdu, pdu, pdu_len); + pdu_len = bvlc_sc_set_orig(&p_pdu, pdu_len, &c->vmac); + res = bvlc_sc_decode_message( + p_pdu, pdu_len, decoded_pdu, &error, &class, &err_desc); + if (res) { + ns->event_func( + BSC_NODE_SWITCH_EVENT_RECEIVED, ns, ns->user_arg, NULL, + p_pdu, pdu_len, decoded_pdu); + } + } + } + + index = node_switch_initiator_get_index(ns, c); + + if (index > -1) { + elem = ns->initiator.urls[index].url_elem - 1; + if (elem < 0 || elem >= ns->initiator.urls[index].urls_cnt) { + elem = -1; + } + if (ns->initiator.sock_state[index] == + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_CONNECTION) { + if (ev == BSC_SOCKET_EVENT_CONNECTED) { + ns->initiator.sock_state[index] = + BSC_NODE_SWITCH_CONNECTION_STATE_CONNECTED; + /* if user provides url instead of dest in */ + /* bsc_node_switch_connect() ns->initiator.dest_vmac[index] + */ + /* is unset. that's why we always set it from socket */ + /* descriptor */ + memcpy( + &ns->initiator.dest_vmac[index].address[0], + &c->vmac.address[0], sizeof(c->vmac.address)); + node_switch_update_status( + ns, true, false, + elem == -1 ? NULL + : (const char *)&ns->initiator.urls[index] + .utf8_urls[elem][0], + c, ev, ERROR_CODE_DEFAULT, NULL); + ns->event_func( + BSC_NODE_SWITCH_EVENT_CONNECTED, ns, ns->user_arg, + &ns->initiator.dest_vmac[index], NULL, 0, NULL); + } else if (ev == BSC_SOCKET_EVENT_DISCONNECTED) { + node_switch_update_status( + ns, true, true, + elem == -1 ? NULL + : (const char *)&ns->initiator.urls[index] + .utf8_urls[elem][0], + c, ev, disconnect_reason, disconnect_reason_desc); + connect_next_url(ns, index); + } + } else if ( + ns->initiator.sock_state[index] == + BSC_NODE_SWITCH_CONNECTION_STATE_CONNECTED) { + if (ev == BSC_SOCKET_EVENT_DISCONNECTED) { + node_switch_update_status( + ns, true, false, + elem == -1 ? NULL + : (const char *)&ns->initiator.urls[index] + .utf8_urls[elem][0], + c, ev, disconnect_reason, disconnect_reason_desc); + ns->event_func( + BSC_NODE_SWITCH_EVENT_DISCONNECTED, ns, ns->user_arg, + &ns->initiator.dest_vmac[index], NULL, 0, NULL); + ns->initiator.urls[index].url_elem = 0; + connect_next_url(ns, index); + } + } else if ( + ns->initiator.sock_state[index] == + BSC_NODE_SWITCH_CONNECTION_STATE_LOCAL_DISCONNECT) { + if (ev == BSC_SOCKET_EVENT_DISCONNECTED) { + node_switch_update_status( + ns, true, false, + elem == -1 ? NULL + : (const char *)&ns->initiator.urls[index] + .utf8_urls[elem][0], + c, ev, ERROR_CODE_SUCCESS, NULL); + ns->initiator.sock_state[index] = + BSC_NODE_SWITCH_CONNECTION_STATE_IDLE; + ns->event_func( + BSC_NODE_SWITCH_EVENT_DISCONNECTED, ns, ns->user_arg, + &ns->initiator.dest_vmac[index], NULL, 0, NULL); + } + } + } + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("node_switch_initiator_socket_event() <<<\n"); +} + +/** + * @brief Handle a node switch initiator context event + * @param ctx - pointer to the socket context + * @param ev - event + */ +static void +node_switch_initiator_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev) +{ + BSC_NODE_SWITCH_CTX *ns; + int i; + + bws_dispatch_lock(); + DEBUG_PRINTF( + "node_switch_initiator_context_event () >>> ctx = %p, ev = " + "%d, user_arg = %p\n", + ctx, ev, ctx->user_arg); + ns = (BSC_NODE_SWITCH_CTX *)ctx->user_arg; + if (ev == BSC_CTX_DEINITIALIZED) { + for (i = 0; i < sizeof(ns->initiator.sock) / sizeof(BSC_SOCKET); i++) { + if (ns->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_CONNECTED) { + ns->initiator.sock_state[i] = + BSC_NODE_SWITCH_CONNECTION_STATE_IDLE; + ns->event_func( + BSC_NODE_SWITCH_EVENT_DISCONNECTED, ns, ns->user_arg, + &ns->initiator.dest_vmac[i], NULL, 0, NULL); + } + } + ns->initiator.state = BSC_NODE_SWITCH_STATE_IDLE; + node_switch_context_deinitialized(ns); + } + DEBUG_PRINTF("node_switch_initiator_context_event() <<<\n"); + bws_dispatch_unlock(); +} + +/** + * @brief Start the node switch + * @param ca_cert_chain - pointer to the CA certificate chain + * @param ca_cert_chain_size - size of the CA certificate chain + * @param cert_chain - pointer to the certificate chain + * @param cert_chain_size - size of the certificate chain + * @param key - pointer to the key + * @param key_size - size of the key + * @param port - port number + * @param iface - pointer to the interface + * @param local_uuid - pointer to the local UUID + * @param local_vmac - pointer to the local VMAC address + * @param max_local_bvlc_len - maximum local BVLC length + * @param max_local_npdu_len - maximum local NPDU length + * @param connect_timeout_s - connection timeout in seconds + * @param heartbeat_timeout_s - heartbeat timeout in seconds + * @param disconnect_timeout_s - disconnect timeout in seconds + * @param reconnnect_timeout_s - reconnect timeout in seconds + * @param address_resolution_timeout_s - address resolution timeout in seconds + * @param direct_connect_accept_enable - true if direct connect accept is + * enabled + * @param direct_connect_initiate_enable - true if direct connect initiate is + * enabled + * @param event_func - pointer to the event function + * @param user_arg - pointer to the user argument + * @param h - pointer to the node switch handle + * @return BACnet/SC status + */ +BSC_SC_RET bsc_node_switch_start( + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + int port, + char *iface, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + uint16_t max_local_bvlc_len, + uint16_t max_local_npdu_len, + uint16_t connect_timeout_s, + uint16_t heartbeat_timeout_s, + uint16_t disconnect_timeout_s, + uint16_t reconnnect_timeout_s, + uint16_t address_resolution_timeout_s, + bool direct_connect_accept_enable, + bool direct_connect_initiate_enable, + BSC_NODE_SWITCH_EVENT_FUNC event_func, + void *user_arg, + BSC_NODE_SWITCH_HANDLE *h) +{ + BSC_SC_RET ret = BSC_SC_SUCCESS; + BSC_NODE_SWITCH_CTX *ns; + + DEBUG_PRINTF("bsc_node_switch_start() >>>\n"); + + if (!address_resolution_timeout_s || !event_func || + (direct_connect_accept_enable && !port) || !h) { + DEBUG_PRINTF("bsc_node_switch_start() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + if (!direct_connect_accept_enable && !direct_connect_initiate_enable) { + DEBUG_PRINTF("bsc_node_switch_start() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + *h = NULL; + bws_dispatch_lock(); + ns = node_switch_alloc(); + + if (!ns) { + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_start() <<< ret = BSC_SC_NO_RESOURCES\n"); + return BSC_SC_NO_RESOURCES; + } + + ns->event_func = event_func; + ns->user_arg = user_arg; + ns->reconnect_timeout_s = reconnnect_timeout_s; + ns->address_resolution_timeout_s = address_resolution_timeout_s; + ns->direct_connect_initiate_enable = direct_connect_initiate_enable; + ns->direct_connect_accept_enable = direct_connect_accept_enable; + ns->initiator.state = BSC_NODE_SWITCH_STATE_IDLE; + ns->acceptor.state = BSC_NODE_SWITCH_STATE_IDLE; + + if (direct_connect_initiate_enable) { + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &ns->initiator.cfg, + BSC_WEBSOCKET_DIRECT_PROTOCOL, 0, NULL, ca_cert_chain, + ca_cert_chain_size, cert_chain, cert_chain_size, key, key_size, + local_uuid, local_vmac, max_local_bvlc_len, max_local_npdu_len, + connect_timeout_s, heartbeat_timeout_s, disconnect_timeout_s); + ret = bsc_init_ctx( + &ns->initiator.ctx, &ns->initiator.cfg, + &bsc_node_switch_initiator_ctx_funcs, ns->initiator.sock, + sizeof(ns->initiator.sock) / sizeof(BSC_SOCKET), ns); + if (ret == BSC_SC_SUCCESS) { + ns->initiator.state = BSC_NODE_SWITCH_STATE_STARTED; + } + } + + if (ret == BSC_SC_SUCCESS && direct_connect_accept_enable) { + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_ACCEPTOR, &ns->acceptor.cfg, + BSC_WEBSOCKET_DIRECT_PROTOCOL, port, iface, ca_cert_chain, + ca_cert_chain_size, cert_chain, cert_chain_size, key, key_size, + local_uuid, local_vmac, max_local_bvlc_len, max_local_npdu_len, + connect_timeout_s, heartbeat_timeout_s, disconnect_timeout_s); + ret = bsc_init_ctx( + &ns->acceptor.ctx, &ns->acceptor.cfg, + &bsc_node_switch_acceptor_ctx_funcs, ns->acceptor.sock, + sizeof(ns->acceptor.sock) / sizeof(BSC_SOCKET), ns); + if (ret == BSC_SC_SUCCESS) { + ns->acceptor.state = BSC_NODE_SWITCH_STATE_STARTING; + } + } + + if (ret != BSC_SC_SUCCESS) { + if (ns->initiator.state == BSC_NODE_SWITCH_STATE_STARTED) { + bsc_deinit_ctx(&ns->initiator.ctx); + ns->initiator.state = BSC_NODE_SWITCH_STATE_IDLE; + } + node_switch_free(ns); + } else { + *h = ns; + if (direct_connect_initiate_enable && !direct_connect_accept_enable) { + ns->event_func( + BSC_NODE_SWITCH_EVENT_STARTED, ns, ns->user_arg, NULL, NULL, 0, + NULL); + } + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_start() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Stop the node switch + * @param h - pointer to the node switch handle + */ +void bsc_node_switch_stop(BSC_NODE_SWITCH_HANDLE h) +{ + BSC_NODE_SWITCH_CTX *ns; + DEBUG_PRINTF("bsc_node_switch_stop() >>> h = %p \n", h); + bws_dispatch_lock(); + ns = (BSC_NODE_SWITCH_CTX *)h; + if (ns) { + DEBUG_PRINTF("bsc_node_switch_stop() unreg loop\n"); + if (ns->direct_connect_accept_enable && + ns->acceptor.state != BSC_NODE_SWITCH_STATE_IDLE) { + ns->acceptor.state = BSC_NODE_SWITCH_STATE_STOPPING; + bsc_deinit_ctx(&ns->acceptor.ctx); + } + if (ns->direct_connect_initiate_enable && + ns->initiator.state != BSC_NODE_SWITCH_STATE_IDLE) { + ns->initiator.state = BSC_NODE_SWITCH_STATE_STOPPING; + bsc_deinit_ctx(&ns->initiator.ctx); + } + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_stop() <<<\n"); +} + +/** + * @brief Check if the node switch is stopped + * @param h - pointer to the node switch handle + * @return true if the node switch is stopped, false otherwise + */ +bool bsc_node_switch_stopped(BSC_NODE_SWITCH_HANDLE h) +{ + BSC_NODE_SWITCH_CTX *ns = (BSC_NODE_SWITCH_CTX *)h; + bool ret = false; + + DEBUG_PRINTF("bsc_node_switch_stopped() h = %p >>>\n", h); + bws_dispatch_lock(); + if (ns && ns->acceptor.state == BSC_NODE_SWITCH_STATE_IDLE && + ns->initiator.state == BSC_NODE_SWITCH_STATE_IDLE) { + ret = true; + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_stoped() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Check if the node switch is started + * @param h - pointer to the node switch handle + * @return true if the node switch is started, false otherwise + */ +bool bsc_node_switch_started(BSC_NODE_SWITCH_HANDLE h) +{ + BSC_NODE_SWITCH_CTX *ns = (BSC_NODE_SWITCH_CTX *)h; + bool ret = false; + + DEBUG_PRINTF("bsc_node_switch_started() h = %p >>>\n", h); + bws_dispatch_lock(); + if (ns) { + ret = true; + if (ns->direct_connect_initiate_enable && + ns->initiator.state != BSC_NODE_SWITCH_STATE_STARTED) { + ret = false; + } + if (ns->direct_connect_accept_enable && + ns->acceptor.state != BSC_NODE_SWITCH_STATE_STARTED) { + ret = false; + } + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_started() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Connect to a node switch + * @param h - pointer to the node switch handle + * @param dest - pointer to the VMAC address + * @param urls - pointer to the URLs + * @param urls_cnt - number of URLs + * @return BACnet/SC status + */ +BSC_SC_RET bsc_node_switch_connect( + BSC_NODE_SWITCH_HANDLE h, + BACNET_SC_VMAC_ADDRESS *dest, + char **urls, + size_t urls_cnt) +{ + BSC_NODE_SWITCH_CTX *ns; + BSC_SC_RET ret = BSC_SC_INVALID_OPERATION; + int i; + + DEBUG_PRINTF( + "bsc_node_switch_connect() >>> h = %p, dest = %p, urls = %p, " + "urls_cnt = %u\n", + h, dest, urls, urls_cnt); + + for (i = 0; i < urls_cnt; i++) { + if (strlen(urls[i]) > + BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK) { + DEBUG_PRINTF( + "bsc_node_switch_connect() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + } + + if (!dest && (!urls || !urls_cnt)) { + DEBUG_PRINTF("bsc_node_switch_connect() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + if (dest && (urls || urls_cnt)) { + DEBUG_PRINTF("bsc_node_switch_connect() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + bws_dispatch_lock(); + ns = (BSC_NODE_SWITCH_CTX *)h; + if (ns->direct_connect_initiate_enable) { + if (urls && urls_cnt) { + i = node_switch_initiator_alloc_sock(ns); + if (i == -1) { + ret = BSC_SC_NO_RESOURCES; + } else { + copy_urls2(ns, i, urls, urls_cnt); + ns->initiator.urls[i].url_elem = 0; + node_switch_connect_or_delay(ns, NULL, i); + ret = BSC_SC_SUCCESS; + } + } else { + i = node_switch_initiator_find_connection_index_for_vmac(dest, ns); + if (i != -1) { + ret = BSC_SC_SUCCESS; + } else { + i = node_switch_initiator_alloc_sock(ns); + if (i == -1) { + ret = BSC_SC_NO_RESOURCES; + } else { + ns->initiator.urls[i].urls_cnt = 0; + node_switch_connect_or_delay(ns, dest, i); + ret = BSC_SC_SUCCESS; + } + } + } + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_connect() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Process an address resolution + * @param h - pointer to the node switch handle + * @param r - pointer to the address resolution + */ +void bsc_node_switch_process_address_resolution( + BSC_NODE_SWITCH_HANDLE h, BSC_ADDRESS_RESOLUTION *r) +{ + BSC_NODE_SWITCH_CTX *ns; + int i; + + DEBUG_PRINTF( + "bsc_node_switch_process_address_resolution() >>> h = %p, r = " + "%p (%s)\n", + h, r, bsc_vmac_to_string(&r->vmac)); + bws_dispatch_lock(); + + ns = (BSC_NODE_SWITCH_CTX *)h; + if (r && r->urls_num) { + i = node_switch_initiator_find_connection_index_for_vmac(&r->vmac, ns); + if (i != -1 && + ns->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_RESOLUTION) { + copy_urls(ns, i, r); + ns->initiator.urls[i].url_elem = 0; + node_switch_connect_or_delay(ns, NULL, i); + } + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_process_address_resolution() <<<\n"); +} + +/** + * @brief Disconnect from a node switch + * @param h - pointer to the node switch handle + * @param dest - pointer to the VMAC address + */ +void bsc_node_switch_disconnect( + BSC_NODE_SWITCH_HANDLE h, BACNET_SC_VMAC_ADDRESS *dest) +{ + BSC_NODE_SWITCH_CTX *ns; + BSC_SOCKET *c; + int i; + + DEBUG_PRINTF( + "bsc_node_switch_disconnect() >>> h = %p, dest = %p (%s)\n", h, dest, + bsc_vmac_to_string(dest)); + bws_dispatch_lock(); + ns = (BSC_NODE_SWITCH_CTX *)h; + if (ns->direct_connect_initiate_enable) { + i = node_switch_initiator_find_connection_index_for_vmac(dest, ns); + + if (i != -1) { + if (ns->initiator.sock_state[i] != + BSC_NODE_SWITCH_CONNECTION_STATE_LOCAL_DISCONNECT) { + if (ns->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_CONNECTED || + ns->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_WAIT_CONNECTION) { + c = &ns->initiator.sock[i]; + bsc_disconnect(c); + ns->initiator.sock_state[i] = + BSC_NODE_SWITCH_CONNECTION_STATE_LOCAL_DISCONNECT; + } else { + ns->initiator.sock_state[i] = + BSC_NODE_SWITCH_CONNECTION_STATE_IDLE; + ns->event_func( + BSC_NODE_SWITCH_EVENT_DISCONNECTED, ns, ns->user_arg, + &ns->initiator.dest_vmac[i], NULL, 0, NULL); + } + } + } + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_disconnect() <<<\n"); +} + +/** + * @brief Send a PDU to a node switch + * @param h - pointer to the node switch handle + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @return BACnet/SC status + */ +BSC_SC_RET +bsc_node_switch_send(BSC_NODE_SWITCH_HANDLE h, uint8_t *pdu, size_t pdu_len) +{ + BSC_NODE_SWITCH_CTX *ns; + BSC_SC_RET ret = BSC_SC_SUCCESS; + BSC_SOCKET *c = NULL; + BACNET_SC_VMAC_ADDRESS dest; + uint8_t **ppdu = &pdu; + int i; + + DEBUG_PRINTF( + "bsc_node_switch_send() >>> pdu = %p, pdu_len = %u\n", pdu, pdu_len); + bws_dispatch_lock(); + ns = (BSC_NODE_SWITCH_CTX *)h; + if (bvlc_sc_pdu_has_no_dest(pdu, pdu_len) || + bvlc_sc_pdu_has_dest_broadcast(pdu, pdu_len)) { + ret = bsc_node_hub_connector_send(ns->user_arg, pdu, pdu_len); + } else { + if (bvlc_sc_pdu_get_dest(pdu, pdu_len, &dest)) { + i = node_switch_initiator_find_connection_index_for_vmac(&dest, ns); + if (i != -1 && + ns->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_CONNECTED) { + c = &ns->initiator.sock[i]; + } + if (!c) { + c = node_switch_acceptor_find_connection_for_vmac(&dest, ns); + } + if (c && c->state == BSC_SOCK_STATE_CONNECTED) { + pdu_len = bvlc_sc_remove_orig_and_dest(ppdu, pdu_len); + if (pdu_len > 0) { + ret = bsc_send(c, *ppdu, pdu_len); + } + } else { + ret = bsc_node_hub_connector_send(ns->user_arg, pdu, pdu_len); + } + } + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_switch_send() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Check if a node switch is connected + * @param h - pointer to the node switch handle + * @param dest - pointer to the VMAC address + * @param urls - pointer to the URLs + * @param urls_cnt - number of URLs + */ +BACNET_STACK_EXPORT +bool bsc_node_switch_connected( + BSC_NODE_SWITCH_HANDLE h, + BACNET_SC_VMAC_ADDRESS *dest, + char **urls, + size_t urls_cnt) +{ + BSC_NODE_SWITCH_CTX *ns; + bool ret = false; + int i, j, k; + + if (!dest && (!urls || urls_cnt == 0)) { + return false; + } + + bws_dispatch_lock(); + ns = (BSC_NODE_SWITCH_CTX *)h; + if (ns->direct_connect_initiate_enable) { + if (dest) { + i = node_switch_initiator_find_connection_index_for_vmac(dest, ns); + if (i != -1) { + if (ns->initiator.sock_state[i] == + BSC_NODE_SWITCH_CONNECTION_STATE_CONNECTED) { + ret = true; + bws_dispatch_unlock(); + return ret; + } + } + } else { + for (i = 0; i < urls_cnt; i++) { + for (j = 0; j < sizeof(ns->initiator.sock) / sizeof(BSC_SOCKET); + j++) { + if (ns->initiator.sock_state[j] == + BSC_NODE_SWITCH_CONNECTION_STATE_CONNECTED) { + if (ns->initiator.urls[j].urls_cnt > 0) { + for (k = 0; k < ns->initiator.urls[j].urls_cnt; + k++) { + if (strlen((char *)&ns->initiator.urls[j] + .utf8_urls[k][0]) == + strlen(urls[i])) { + if (!memcmp( + (char *)&ns->initiator.urls[j] + .utf8_urls[k][0], + urls[i], strlen(urls[i]))) { + ret = true; + bws_dispatch_unlock(); + return ret; + } + } + } + } + } + } + } + } + } + if (ns->direct_connect_accept_enable) { + if (dest) { + i = node_switch_acceptor_find_connection_index_for_vmac(dest, ns); + if (i != -1) { + ret = true; + } + } + } + bws_dispatch_unlock(); + return ret; +} diff --git a/src/bacnet/datalink/bsc/bsc-node-switch.h b/src/bacnet/datalink/bsc/bsc-node-switch.h new file mode 100644 index 00000000..9ea7cc2a --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-node-switch.h @@ -0,0 +1,126 @@ +/** + * @file + * @brief BACNet secure connect node switch function API. + * In general, user should not use that API directly, + * BACNet/SC datalink API should be used. + * @author Kirill Neznamov + * @date October 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_NODE_SWITCH_H +#define BACNET_DATALINK_BSC_NODE_SWITCH_H +#include +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/datalink/bsc/bsc-retcodes.h" +#include "bacnet/datalink/bsc/bsc-node.h" + +typedef void *BSC_NODE_SWITCH_HANDLE; + +typedef enum { + BSC_NODE_SWITCH_EVENT_STARTED = 1, + BSC_NODE_SWITCH_EVENT_STOPPED = 2, + BSC_NODE_SWITCH_EVENT_RECEIVED = 3, + BSC_NODE_SWITCH_EVENT_DUPLICATED_VMAC = 4, + + /* BSC_NODE_SWITCH_EVENT_CONNECTED event is emitted every */ + /* time remote peer connects only if bsc_node_switch_connect() */ + /* was called after start for corresponded mac or url/urls. */ + /* Events indication are stopped only */ + /* if node switch was stopped or bsc_node_switch_disconnect() */ + /* was called. For a connection initiated from a remote peer that */ + /* event won't be ever emitted. */ + + BSC_NODE_SWITCH_EVENT_CONNECTED = 5, + + /* The BSC_NODE_SWITCH_EVENT_DISCONNECTED event is emitted */ + /* every time remote peer disconnects only if */ + /* bsc_node_switch_connect() was called after start. */ + /* If user called bsc_node_switch_disconnect() or stopped */ + /* node switch, after last event indication next event indications */ + /* are stopped for corresponded peer with corresponded vmac. */ + BSC_NODE_SWITCH_EVENT_DISCONNECTED = 6 + +} BSC_NODE_SWITCH_EVENT; + +/* dest parameter is actual only for */ +/* BSC_NODE_SWITCH_EVENT_CONNECTED and BSC_NODE_SWITCH_EVENT_DISCONNECTED */ +/* events. It is the parameter which was specified in */ +/* bsc_node_switch_connect() or bsc_node_switch_disconnect() calls. */ + +typedef void (*BSC_NODE_SWITCH_EVENT_FUNC)( + BSC_NODE_SWITCH_EVENT ev, + BSC_NODE_SWITCH_HANDLE h, + void *user_arg, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_node_switch_start( + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + int port, + char *iface, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + uint16_t max_local_bvlc_len, + uint16_t max_local_npdu_len, + uint16_t connect_timeout_s, + uint16_t heartbeat_timeout_s, + uint16_t disconnect_timeout_s, + uint16_t reconnnect_timeout_s, + uint16_t address_resolution_timeout_s, + bool direct_connect_accept_enable, + bool direct_connect_initiate_enable, + BSC_NODE_SWITCH_EVENT_FUNC event_func, + void *user_arg, + BSC_NODE_SWITCH_HANDLE *h); + +BACNET_STACK_EXPORT +void bsc_node_switch_stop(BSC_NODE_SWITCH_HANDLE h); + +BACNET_STACK_EXPORT +bool bsc_node_switch_stopped(BSC_NODE_SWITCH_HANDLE h); + +BACNET_STACK_EXPORT +bool bsc_node_switch_started(BSC_NODE_SWITCH_HANDLE h); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_node_switch_connect( + BSC_NODE_SWITCH_HANDLE h, + BACNET_SC_VMAC_ADDRESS *dest, + char **urls, + size_t urls_cnt); + +BACNET_STACK_EXPORT +bool bsc_node_switch_connected( + BSC_NODE_SWITCH_HANDLE h, + BACNET_SC_VMAC_ADDRESS *dest, + char **urls, + size_t urls_cnt); + +BACNET_STACK_EXPORT +void bsc_node_switch_disconnect( + BSC_NODE_SWITCH_HANDLE h, BACNET_SC_VMAC_ADDRESS *dest); + +BACNET_STACK_EXPORT +void bsc_node_switch_process_address_resolution( + BSC_NODE_SWITCH_HANDLE h, BSC_ADDRESS_RESOLUTION *r); + +BACNET_STACK_EXPORT +BSC_SC_RET +bsc_node_switch_send(BSC_NODE_SWITCH_HANDLE h, uint8_t *pdu, size_t pdu_len); + +BACNET_STACK_EXPORT +void bsc_node_switch_maintenance_timer(uint16_t seconds); + +#endif diff --git a/src/bacnet/datalink/bsc/bsc-node.c b/src/bacnet/datalink/bsc/bsc-node.c new file mode 100644 index 00000000..488d0ef8 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-node.c @@ -0,0 +1,1525 @@ +/** + * @file + * @brief BACNet secure connect node API. + * @author Kirill Neznamov + * @date October 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#include "bacnet/basic/sys/debug.h" +#include "bacnet/datalink/bsc/bsc-node.h" +#include "bacnet/datalink/bsc/bsc-event.h" +#include "bacnet/datalink/bsc/bsc-util.h" +#include "bacnet/datalink/bsc/bsc-hub-function.h" +#include "bacnet/datalink/bsc/bsc-hub-connector.h" +#include "bacnet/datalink/bsc/bsc-node-switch.h" +#include "bacnet/datalink/bsc/bsc-socket.h" +#include "bacnet/basic/object/sc_netport.h" +#include "bacnet/bacdef.h" +#include "bacnet/npdu.h" +#include "bacnet/bacenum.h" + +#define DEBUG_BSC_NODE 0 + +#if DEBUG_BSC_NODE == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +#define ERROR_STR_OPTION_NOT_UNDERSTOOD \ + "'must understand' option not understood " + +#define ERROR_STR_DIRECT_CONNECTIONS_NOT_SUPPORTED \ + "direct connections are not supported" + +typedef enum { + BSC_NODE_STATE_IDLE = 0, + BSC_NODE_STATE_STARTING = 1, + BSC_NODE_STATE_STARTED = 2, + BSC_NODE_STATE_RESTARTING = 3, + BSC_NODE_STATE_STOPPING = 4 +} BSC_NODE_STATE; + +struct BSC_Node { + bool used; + BSC_NODE_STATE state; + BSC_NODE_CONF *conf; + BSC_ADDRESS_RESOLUTION *resolution; + BSC_HUB_CONNECTOR_HANDLE hub_connector; + BSC_HUB_FUNCTION_HANDLE hub_function; + BSC_NODE_SWITCH_HANDLE node_switch; + BACNET_SC_FAILED_CONNECTION_REQUEST *failed; + BACNET_SC_DIRECT_CONNECTION_STATUS *direct_status; + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *hub_status; +}; + +#if BSC_CONF_NODES_NUM < 1 +#error "BSC_CONF_NODES_NUM must be >= 1" +#endif + +static struct BSC_Node bsc_node[BSC_CONF_NODES_NUM] = { 0 }; + +static BACNET_SC_FAILED_CONNECTION_REQUEST + bsc_failed_request[BSC_CONF_NODES_NUM] + [BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM]; +static bool bsc_failed_request_initialized[BSC_CONF_NODES_NUM] = { 0 }; + +static BACNET_SC_DIRECT_CONNECTION_STATUS + bsc_direct_status[BSC_CONF_NODES_NUM] + [BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM]; +static bool bsc_direct_status_initialized[BSC_CONF_NODES_NUM] = { 0 }; + +static BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS + bsc_hub_status[BSC_CONF_NODES_NUM] + [BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM]; +static bool bsc_hub_status_initialized[BSC_CONF_NODES_NUM] = { 0 }; + +static BSC_ADDRESS_RESOLUTION + bsc_address_resolution[BSC_CONF_NODES_NUM] + [BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM]; + +static BSC_NODE_CONF bsc_conf[BSC_CONF_NODES_NUM]; + +static BSC_SC_RET bsc_node_start_state(BSC_NODE *node, BSC_NODE_STATE state); + +/** + * @brief Initialize direct connection status + * @param s - pointer to the direct connection status + */ +static void bsc_node_init_direct_status(BACNET_SC_DIRECT_CONNECTION_STATUS *s) +{ + int j; + + for (j = 0; j < BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM; j++) { + memset(&s[j], 0, sizeof(*s)); + memset(&s[j].Connect_Timestamp, 0xFF, sizeof(s[j].Connect_Timestamp)); + memset( + &s[j].Disconnect_Timestamp, 0xFF, + sizeof(s[j].Disconnect_Timestamp)); + } +} + +/** + * @brief Initialize hub connection status + * @param s - pointer to the hub connection status + */ +static void +bsc_node_init_hub_status(BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *s) +{ + int j; + + for (j = 0; j < BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM; j++) { + memset(&s[j], 0, sizeof(*s)); + memset(&s[j].Connect_Timestamp, 0xFF, sizeof(s[j].Connect_Timestamp)); + memset( + &s[j].Disconnect_Timestamp, 0xFF, + sizeof(s[j].Disconnect_Timestamp)); + } +} + +/** + * @brief Allocate a node + * @return pointer to the allocated node + */ +static BSC_NODE *bsc_alloc_node(void) +{ + int i, j; + DEBUG_PRINTF("bsc_alloc_node() >>> \n"); + + for (i = 0; i < BSC_CONF_NODES_NUM; i++) { + if (bsc_node[i].used == false) { + memset(&bsc_node[i], 0, sizeof(bsc_node[i])); + bsc_node[i].used = true; + bsc_node[i].hub_status = (BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS + *)&bsc_hub_status[i][0]; + bsc_node[i].direct_status = + (BACNET_SC_DIRECT_CONNECTION_STATUS *)&bsc_direct_status[i][0]; + + /* Start/stop cycles of a node must not make an influence to history + That's why hub and direct status arrays are initialized + only once */ + + if (!bsc_hub_status_initialized[i]) { + bsc_node_init_hub_status(bsc_node[i].hub_status); + } + + if (!bsc_direct_status_initialized[i]) { + bsc_node_init_direct_status(bsc_node[i].direct_status); + } + + bsc_node[i].conf = &bsc_conf[i]; + bsc_node[i].resolution = &bsc_address_resolution[i][0]; + bsc_node[i].failed = &bsc_failed_request[i][0]; + memset( + bsc_node[i].resolution, 0, + sizeof(BSC_ADDRESS_RESOLUTION) * + BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM); + + /* Start/stop cycles of a node must not make an influence to history + * about failed requests */ + /* That's why bsc_failed_request[] array is initialized only once */ + + if (!bsc_failed_request_initialized[i]) { + for (j = 0; j < BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM; + j++) { + memset( + &bsc_failed_request[i][j], 0, + sizeof(bsc_failed_request[i][j])); + memset( + &bsc_failed_request[i][j].Timestamp, 0xff, + sizeof(bsc_failed_request[i][j].Timestamp)); + } + bsc_failed_request_initialized[i] = true; + } + DEBUG_PRINTF( + "bsc_alloc_node() <<< i = %d, node = %p, conf = %p\n", i, + &bsc_node[i], bsc_node[i].conf); + return &bsc_node[i]; + } + } + DEBUG_PRINTF("bsc_alloc_node() <<< ret = NULL\n"); + return NULL; +} + +/** + * @brief Check if the node is enabled or not + * @param conf - pointer to the node configuration + * @return true if the node is enabled, otherwise false + */ +static bool node_switch_enabled(BSC_NODE_CONF *conf) +{ + if (conf->direct_connect_initiate_enable || + conf->direct_connect_accept_enable) { + return true; + } + return false; +} + +/** + * @brief Get the address resolution for the BACnet/SC node + * @param node - pointer to the BACnet/SC node + * @param vmac - pointer to the VMAC address + * @return pointer to the address resolution + */ +static BSC_ADDRESS_RESOLUTION * +node_get_address_resolution(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac) +{ + int i; + + for (i = 0; i < BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM; i++) { + if (node->resolution[i].used && + !memcmp( + &vmac->address[0], &node->resolution[i].vmac.address[0], + BVLC_SC_VMAC_SIZE)) { + return &node->resolution[i]; + } + } + return NULL; +} + +/** + * @brief Free the address resolution + * @param r - pointer to the address resolution + */ +static void node_free_address_resolution(BSC_ADDRESS_RESOLUTION *r) +{ + r->used = false; + r->urls_num = false; +} + +/** + * @brief Allocate the address resolution + * @param node - pointer to the BACnet/SC node + * @param vmac - pointer to the VMAC address + * @return pointer to the address resolution + */ +static BSC_ADDRESS_RESOLUTION * +node_alloc_address_resolution(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac) +{ + int i; + unsigned long max = 0; + int max_index = 0; + + for (i = 0; i < BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM; i++) { + if (!node->resolution[i].used) { + node->resolution[i].used = true; + mstimer_set( + &node->resolution[i].fresh_timer, + node->conf->address_resolution_freshness_timeout_s * 1000); + memcpy( + &node->resolution[i].vmac.address[0], &vmac->address[0], + BVLC_SC_VMAC_SIZE); + return &node->resolution[i]; + } + } + + /* find and remove oldest resolution */ + + for (i = 0; i < BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM; i++) { + if (mstimer_elapsed(&node->resolution[i].fresh_timer) > max) { + max = mstimer_elapsed(&node->resolution[i].fresh_timer); + max_index = i; + } + } + + node->resolution[max_index].urls_num = 0; + return &node->resolution[max_index]; +} + +/** + * @brief Free the BACnet/SC node + * @param node - pointer to the BACnet/SC node + */ +static void bsc_free_node(BSC_NODE *node) +{ + DEBUG_PRINTF( + "bsc_free_node() >>> node = %p, state = %d\n", node, node->state); + node->used = false; + DEBUG_PRINTF("bsc_free_node() <<<\n"); +} + +/** + * @brief Process a BACnet/SC node stop event + * @param node - pointer to the BACnet/SC node + */ +static void bsc_node_process_stop_event(BSC_NODE *node) +{ + bool stopped = true; + + DEBUG_PRINTF( + "bsc_node_process_stop_event() >>> node = %p, state = %d\n", node, + node->state); + + if (node->conf->hub_function_enabled) { + if (node->hub_function && + !bsc_hub_function_stopped(node->hub_function)) { + DEBUG_PRINTF( + "bsc_node_process_stop_event() hub_function %p is not " + "stopped\n", + node->hub_function); + stopped = false; + } + } + if (node->node_switch && node_switch_enabled(node->conf)) { + if (!bsc_node_switch_stopped(node->node_switch)) { + DEBUG_PRINTF( + "bsc_node_process_stop_event() node_switch %p is not stopped\n", + node->node_switch); + stopped = false; + } + } + if (node->hub_connector && + !bsc_hub_connector_stopped(node->hub_connector)) { + DEBUG_PRINTF( + "bsc_node_process_stop_event() hub_connector %p is not stopped\n", + node->hub_connector); + stopped = false; + } + + DEBUG_PRINTF("bsc_node_process_stop_event() stopped = %d\n", stopped); + + if (node->state == BSC_NODE_STATE_STOPPING) { + if (stopped) { + node->state = BSC_NODE_STATE_IDLE; + DEBUG_PRINTF("bsc_node_process_stop_event() emit stop event\n"); + node->conf->event_func(node, BSC_NODE_EVENT_STOPPED, NULL, NULL, 0); + } + } else if (node->state == BSC_NODE_STATE_RESTARTING) { + if (stopped) { + DEBUG_PRINTF("bsc_node_process_stop_event() emit restart event\n"); + (void)bsc_node_start_state(node, BSC_NODE_STATE_RESTARTING); + } + } + DEBUG_PRINTF("bsc_node_process_stop_event() <<<\n"); +} + +/** + * @brief Process a BACnet/SC node start event + * @param node - pointer to the BACnet/SC node + */ +static void bsc_node_process_start_event(BSC_NODE *node) +{ + bool started = true; + + DEBUG_PRINTF( + "bsc_node_process_start_event() >>> node = %p, state = %d\n", node, + node->state); + if (node->hub_function && node->conf->hub_function_enabled) { + if (!bsc_hub_function_started(node->hub_function)) { + started = false; + } + } + if (node->node_switch && node_switch_enabled(node->conf)) { + if (!bsc_node_switch_started(node->node_switch)) { + started = false; + } + } + DEBUG_PRINTF("bsc_node_process_start_event() started = %d\n", started); + if (started) { + if (node->state == BSC_NODE_STATE_STARTING) { + node->state = BSC_NODE_STATE_STARTED; + node->conf->event_func(node, BSC_NODE_EVENT_STARTED, NULL, NULL, 0); + } else if (node->state == BSC_NODE_STATE_RESTARTING) { + node->state = BSC_NODE_STATE_STARTED; + node->conf->event_func( + node, BSC_NODE_EVENT_RESTARTED, NULL, NULL, 0); + } + } + DEBUG_PRINTF("bsc_node_process_start_event() <<<\n"); +} + +/** + * @brief Process a BACnet/SC node restart event + * @param node - pointer to the BACnet/SC node + */ +static void bsc_node_restart(BSC_NODE *node) +{ + DEBUG_PRINTF( + "bsc_node_restart() >>> node = %p hub_function %p " + "hub_connector %p node_switch %p\n", + node, node->hub_function, node->hub_connector, node->node_switch); + node->state = BSC_NODE_STATE_RESTARTING; + if (node->conf->primaryURL) { + bsc_hub_connector_stop(node->hub_connector); + } + if (node->hub_function && node->conf->hub_function_enabled) { + bsc_hub_function_stop(node->hub_function); + } + if (node_switch_enabled(node->conf)) { + bsc_node_switch_stop(node->node_switch); + } + DEBUG_PRINTF("bsc_node_restart() <<<\n"); +} + +/** + * @brief Parse URLs for the BACnet/SC node + * @param r - pointer to the address resolution + * @param decoded_pdu - pointer to the decoded PDU + */ +static void bsc_node_parse_urls( + BSC_ADDRESS_RESOLUTION *r, BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + int i, j; + int start; + uint8_t *url = NULL; + + url = decoded_pdu->payload.address_resolution_ack.utf8_websocket_uri_string; + r->urls_num = 0; + for (i = 0, j = 0, start = 0; + i < decoded_pdu->payload.address_resolution_ack + .utf8_websocket_uri_string_len; + i++) { + if (url[i] == 0x20) { + if (i > BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK || + (i - start) == 0) { + start = i + 1; + continue; + } else { + memcpy(&r->utf8_urls[j][0], &url[start], i - start); + r->utf8_urls[j][i] = 0; + j++; + start = i + 1; + } + } + } + if (i - start > 0 && + i <= BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK) { + memcpy(&r->utf8_urls[j][0], &url[start], i - start); + r->utf8_urls[j][i] = 0; + j++; + } + r->urls_num = j; +} + +/** + * @brief Process a BACnet/SC node received event + * @param node - pointer to the BACnet/SC node + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @param decoded_pdu - pointer to the decoded PDU + */ +static void bsc_node_process_received( + BSC_NODE *node, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + int i; + static uint8_t buf[BVLC_SC_NPDU_SIZE_CONF]; + size_t bufsize; + BSC_SC_RET ret; + uint16_t error_class; + uint16_t error_code; + BSC_ADDRESS_RESOLUTION *r; + + (void)ret; + DEBUG_PRINTF( + "bsc_node_process_received() >>> node = %p, pdu = %p, pdu_len " + "= %d, decoded_pdu = %p\n", + node, pdu, pdu_len, decoded_pdu); + + for (i = 0; i < decoded_pdu->hdr.dest_options_num; i++) { + if (decoded_pdu->dest_options[i].must_understand) { + DEBUG_PRINTF("bsc_node_process_received() pdu with " + "'must-understand' is dropped\n"); + if (bvlc_sc_need_send_bvlc_result(decoded_pdu)) { + error_code = ERROR_CODE_HEADER_NOT_UNDERSTOOD; + error_class = ERROR_CLASS_COMMUNICATION; + bufsize = bvlc_sc_encode_result( + buf, sizeof(buf), decoded_pdu->hdr.message_id, NULL, + decoded_pdu->hdr.origin, decoded_pdu->hdr.bvlc_function, 1, + &decoded_pdu->dest_options[i].packed_header_marker, + &error_class, &error_code, + (uint8_t *)ERROR_STR_OPTION_NOT_UNDERSTOOD); + if (bufsize) { + ret = bsc_node_send(node, buf, bufsize); +#if DEBUG_ENABLED == 1 + if (ret != BSC_SC_SUCCESS) { + DEBUG_PRINTF( + "bsc_node_process_received() warning " + "bvlc-result pdu is not sent, error %d\n", + ret); + } +#endif + } + DEBUG_PRINTF("bsc_node_process_received() <<<\n"); + return; + } else { + DEBUG_PRINTF("bsc_node_process_received() <<<\n"); + return; + } + } + } + + switch (decoded_pdu->hdr.bvlc_function) { + case BVLC_SC_RESULT: { + if (decoded_pdu->payload.result.bvlc_function == + BVLC_SC_ADDRESS_RESOLUTION) { + DEBUG_PRINTF( + "received a NAK for address resolution from %s\n", + bsc_vmac_to_string(decoded_pdu->hdr.origin)); + r = node_get_address_resolution(node, decoded_pdu->hdr.origin); + if (r) { + r->urls_num = 0; + mstimer_restart(&r->fresh_timer); + } else { + r = node_alloc_address_resolution( + node, decoded_pdu->hdr.origin); + if (r) { + r->urls_num = 0; + mstimer_restart(&r->fresh_timer); + } else { + DEBUG_PRINTF( + "can't allocate address resolution for " + "node with address %s\n", + bsc_vmac_to_string(decoded_pdu->hdr.origin)); + } + } + } + DEBUG_PRINTF( + "node %p get pdu with bvlc " + "function %d error_class %d error_code %d from node %s\n", + node, decoded_pdu->payload.result.bvlc_function, + decoded_pdu->payload.result.error_class, + decoded_pdu->payload.result.error_code, + bsc_vmac_to_string(decoded_pdu->hdr.origin)); + node->conf->event_func( + node, BSC_NODE_EVENT_RECEIVED_RESULT, NULL, pdu, pdu_len); + break; + } + case BVLC_SC_ADVERTISIMENT: { + node->conf->event_func( + node, BSC_NODE_EVENT_RECEIVED_ADVERTISIMENT, NULL, pdu, + pdu_len); + break; + } + case BVLC_SC_ADVERTISIMENT_SOLICITATION: { + bufsize = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), bsc_get_next_message_id(), NULL, + decoded_pdu->hdr.origin, + bsc_hub_connector_state(node->hub_connector), + node_switch_enabled(node->conf) + ? BVLC_SC_DIRECT_CONNECTION_ACCEPT_SUPPORTED + : BVLC_SC_DIRECT_CONNECTION_ACCEPT_UNSUPPORTED, + node->conf->max_local_bvlc_len, node->conf->max_local_npdu_len); + if (bufsize) { + ret = bsc_node_send(node, buf, bufsize); +#if DEBUG_ENABLED == 1 + if (ret != BSC_SC_SUCCESS) { + DEBUG_PRINTF( + "bsc_node_process_received() warning " + "advertisement pdu is not sent to node %s, error %d\n", + ret, bsc_vmac_to_string(decoded_pdu->hdr.origin)); + } +#endif + } + break; + } + case BVLC_SC_ADDRESS_RESOLUTION: { + DEBUG_PRINTF( + "bsc_node_process_received() got BVLC_SC_ADDRESS_RESOLUTION\n"); + if (node_switch_enabled(node->conf)) { + bufsize = bvlc_sc_encode_address_resolution_ack( + buf, sizeof(buf), decoded_pdu->hdr.message_id, NULL, + decoded_pdu->hdr.origin, + (uint8_t *)node->conf->direct_connection_accept_uris, + node->conf->direct_connection_accept_uris_len); + if (bufsize) { + ret = bsc_node_send(node, buf, bufsize); +#if DEBUG_ENABLED == 1 + if (ret != BSC_SC_SUCCESS) { + DEBUG_PRINTF( + "bsc_node_process_received() warning " + "address resolution ack is not sent, error %d\n", + ret); + } +#endif + } + } else { + DEBUG_PRINTF( + "bsc_node_process_received() node switch is " + "disabled, send error to node %s\n", + bsc_vmac_to_string(decoded_pdu->hdr.origin)); + error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + error_class = ERROR_CLASS_COMMUNICATION; + bufsize = bvlc_sc_encode_result( + buf, sizeof(buf), decoded_pdu->hdr.message_id, NULL, + decoded_pdu->hdr.origin, decoded_pdu->hdr.bvlc_function, 1, + NULL, &error_class, &error_code, + (uint8_t *)ERROR_STR_DIRECT_CONNECTIONS_NOT_SUPPORTED); + if (bufsize) { + ret = bsc_node_send(node, buf, bufsize); +#if DEBUG_ENABLED == 1 + if (ret != BSC_SC_SUCCESS) { + DEBUG_PRINTF( + "bsc_node_process_received() warning " + "bvlc-result pdu is not sent, error %d\n", + ret); + } +#endif + } + } + break; + } + case BVLC_SC_ADDRESS_RESOLUTION_ACK: { + DEBUG_PRINTF("bsc_node_process_received() got " + "BVLC_SC_ADDRESS_RESOLUTION_ACK\n"); + r = node_get_address_resolution(node, decoded_pdu->hdr.origin); + if (!r) { + r = node_alloc_address_resolution( + node, decoded_pdu->hdr.origin); +#if DEBUG_ENABLED == 1 + if (!r) { + DEBUG_PRINTF( + "can't allocate address resolution for node " + "with address %s\n", + bsc_vmac_to_string(decoded_pdu->hdr.origin)); + } +#endif + } + if (r) { + bsc_node_parse_urls(r, decoded_pdu); + mstimer_restart(&r->fresh_timer); + bsc_node_switch_process_address_resolution( + node->node_switch, r); + } + break; + } + case BVLC_SC_ENCAPSULATED_NPDU: { + node->conf->event_func( + node, BSC_NODE_EVENT_RECEIVED_NPDU, NULL, pdu, pdu_len); + break; + } + default: + break; + } + DEBUG_PRINTF("bsc_node_process_received() <<<\n"); +} + +/** + * @brief handle a BACnet/SC node connection event + * @param ev - connection event + * @param h - connection handle + * @param user_arg - user argument + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @param decoded_pdu - pointer to the decoded PDU + */ +static void bsc_hub_connector_event( + BSC_HUB_CONNECTOR_EVENT ev, + BSC_HUB_CONNECTOR_HANDLE h, + void *user_arg, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + BSC_NODE *node = (BSC_NODE *)user_arg; + + (void)h; + bws_dispatch_lock(); + DEBUG_PRINTF( + "bsc_hub_connector_event() >>> ev = %d, h = %p, node = %p\n", ev, h, + user_arg); + if (ev == BSC_HUBC_EVENT_STOPPED) { + node->hub_connector = NULL; + bsc_node_process_stop_event(node); + } else if (ev == BSC_HUBC_EVENT_ERROR_DUPLICATED_VMAC) { + if (node->state != BSC_NODE_STATE_STOPPING && + node->state != BSC_NODE_STATE_RESTARTING) { + bsc_node_restart(node); + } + } else if (ev == BSC_HUBC_EVENT_RECEIVED) { + bsc_node_process_received(node, pdu, pdu_len, decoded_pdu); + } + DEBUG_PRINTF("bsc_hub_connector_event() <<<\n"); + bws_dispatch_unlock(); +} + +/** + * @brief handle a BACnet/SC hub function event + * @param ev - hub function event + * @param h - hub function handle + * @param user_arg - user argument + */ +static void bsc_hub_function_event( + BSC_HUB_FUNCTION_EVENT ev, BSC_HUB_FUNCTION_HANDLE h, void *user_arg) +{ + BSC_NODE *node = (BSC_NODE *)user_arg; + + (void)h; + bws_dispatch_lock(); + DEBUG_PRINTF( + "bsc_hub_function_event() >>> ev = %d, h = %p, node = %p\n", ev, h, + user_arg); + if (ev == BSC_HUBF_EVENT_STARTED) { + bsc_node_process_start_event(node); + } else if (ev == BSC_HUBF_EVENT_STOPPED) { + node->hub_function = NULL; + bsc_node_process_stop_event(node); + } else if (ev == BSC_HUBF_EVENT_ERROR_DUPLICATED_VMAC) { + if (node->state != BSC_NODE_STATE_STOPPING && + node->state != BSC_NODE_STATE_RESTARTING && + node->state != BSC_NODE_STATE_IDLE) { + bsc_node_restart(node); + } + } + DEBUG_PRINTF("bsc_hub_function_event() <<<\n"); + bws_dispatch_unlock(); +} + +/** + * @brief handle a BACnet/SC node switch event + * @param ev - node switch event + * @param h - node switch handle + * @param user_arg - user argument + * @param dest - pointer to the destination VMAC address + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @param decoded_pdu - pointer to the decoded PDU + */ +static void bsc_node_switch_event( + BSC_NODE_SWITCH_EVENT ev, + BSC_NODE_SWITCH_HANDLE h, + void *user_arg, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + BSC_NODE *node = (BSC_NODE *)user_arg; + + (void)h; + bws_dispatch_lock(); + DEBUG_PRINTF( + "bsc_node_switch_event() >>> ev = %d, h = %p, node = %p\n", ev, h, + user_arg); + if (ev == BSC_NODE_SWITCH_EVENT_STARTED) { + bsc_node_process_start_event(node); + } else if (ev == BSC_NODE_SWITCH_EVENT_STOPPED) { + node->node_switch = NULL; + bsc_node_process_stop_event(node); + } else if (ev == BSC_NODE_SWITCH_EVENT_DUPLICATED_VMAC) { + if (node->state != BSC_NODE_STATE_STOPPING && + node->state != BSC_NODE_STATE_RESTARTING && + node->state != BSC_NODE_STATE_IDLE) { + bsc_node_restart(node); + } + } else if (ev == BSC_NODE_SWITCH_EVENT_RECEIVED) { + bsc_node_process_received(node, pdu, pdu_len, decoded_pdu); + } else if (ev == BSC_NODE_SWITCH_EVENT_CONNECTED) { + node->conf->event_func( + node, BSC_NODE_EVENT_DIRECT_CONNECTED, dest, NULL, 0); + } else if (ev == BSC_NODE_SWITCH_EVENT_DISCONNECTED) { + node->conf->event_func( + node, BSC_NODE_EVENT_DIRECT_DISCONNECTED, dest, NULL, 0); + } + DEBUG_PRINTF("bsc_node_switch_event() <<<\n"); + bws_dispatch_unlock(); +} + +/** + * @brief Initialize the BACnet/SC node + * @param conf - pointer to the node configuration + * @param node - pointer to the BACnet/SC node + * @return BACnet/SC status + */ +BSC_SC_RET bsc_node_init(BSC_NODE_CONF *conf, BSC_NODE **node) +{ + DEBUG_PRINTF("bsc_node_init() >>> conf = %p, node = %p\n", conf, node); + + if (!conf || !node) { + DEBUG_PRINTF("bsc_node_init() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + if (!conf->ca_cert_chain || !conf->ca_cert_chain_size || + !conf->cert_chain || !conf->cert_chain_size || !conf->key || + !conf->key_size || !conf->local_uuid || conf->connect_timeout_s <= 0 || + conf->heartbeat_timeout_s <= 0 || conf->disconnect_timeout_s <= 0 || + conf->reconnnect_timeout_s <= 0 || + conf->address_resolution_timeout_s <= 0 || + conf->address_resolution_freshness_timeout_s <= 0 || + !conf->event_func) { + DEBUG_PRINTF("bsc_node_init() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + bws_dispatch_lock(); + *node = bsc_alloc_node(); + + if (!(*node)) { + DEBUG_PRINTF("bsc_node_init() <<< ret = BSC_SC_NO_RESOURCE\n"); + bws_dispatch_unlock(); + return BSC_SC_NO_RESOURCES; + } + + memcpy((*node)->conf, conf, sizeof(*conf)); + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_init() <<< ret = BSC_SC_SUCCESS\n"); + return BSC_SC_SUCCESS; +} + +/** + * @brief Deinitialize the BACnet/SC node + * @param node - pointer to the BACnet/SC node + * @return BACnet/SC status + */ +BSC_SC_RET bsc_node_deinit(BSC_NODE *node) +{ + DEBUG_PRINTF("bsc_node_deinit() >>> node = %p\n", node); + bws_dispatch_lock(); + if (node->state != BSC_NODE_STATE_IDLE) { + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_deinit() <<< ret = BSC_SC_INVALID_OPERATION\n"); + return BSC_SC_INVALID_OPERATION; + } + bsc_free_node(node); + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_deinit() <<< ret = BSC_SC_SUCCESS\n"); + return BSC_SC_SUCCESS; +} + +/** + * @brief Handle the BACnet/SC node start state + * @param node - pointer to the BACnet/SC node + * @param state - node state + * @return BACnet/SC status + */ +static BSC_SC_RET bsc_node_start_state(BSC_NODE *node, BSC_NODE_STATE state) +{ + BSC_SC_RET ret = BSC_SC_BAD_PARAM; + bws_dispatch_lock(); + DEBUG_PRINTF( + "bsc_node_start_state() >>> node = %p state = %d\n", node, state); + + node->state = state; + node->hub_connector = NULL; + node->hub_function = NULL; + node->node_switch = NULL; + + if (node->state != BSC_NODE_STATE_RESTARTING) { + memset( + node->resolution, 0, + sizeof(BSC_ADDRESS_RESOLUTION) * + BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM); + } else { + bsc_generate_random_vmac(&node->conf->local_vmac); + DEBUG_PRINTF( + "bsc_node_start_state() generated random vmac %s for node %p\n", + bsc_vmac_to_string(&node->conf->local_vmac), node); + } + + if (node->conf->primaryURL) { + ret = bsc_hub_connector_start( + node->conf->ca_cert_chain, node->conf->ca_cert_chain_size, + node->conf->cert_chain, node->conf->cert_chain_size, + node->conf->key, node->conf->key_size, node->conf->local_uuid, + &node->conf->local_vmac, node->conf->max_local_bvlc_len, + node->conf->max_local_npdu_len, node->conf->connect_timeout_s, + node->conf->heartbeat_timeout_s, node->conf->disconnect_timeout_s, + node->conf->primaryURL, node->conf->failoverURL, + node->conf->reconnnect_timeout_s, bsc_hub_connector_event, node, + &node->hub_connector); + + if (ret != BSC_SC_SUCCESS) { + node->state = BSC_NODE_STATE_IDLE; + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_start_state() <<< ret = %d\n", ret); + return ret; + } + } + + if (node->conf->hub_function_enabled) { + ret = bsc_hub_function_start( + node->conf->ca_cert_chain, node->conf->ca_cert_chain_size, + node->conf->cert_chain, node->conf->cert_chain_size, + node->conf->key, node->conf->key_size, node->conf->hub_server_port, + node->conf->hub_iface, node->conf->local_uuid, + &node->conf->local_vmac, node->conf->max_local_bvlc_len, + node->conf->max_local_npdu_len, node->conf->connect_timeout_s, + node->conf->heartbeat_timeout_s, node->conf->disconnect_timeout_s, + bsc_hub_function_event, node, &node->hub_function); + if (ret != BSC_SC_SUCCESS) { + node->state = BSC_NODE_STATE_IDLE; + bsc_hub_connector_stop(node->hub_connector); + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_start_state() <<< ret = %d\n", ret); + return ret; + } + } + + if (node_switch_enabled(node->conf)) { + ret = bsc_node_switch_start( + node->conf->ca_cert_chain, node->conf->ca_cert_chain_size, + node->conf->cert_chain, node->conf->cert_chain_size, + node->conf->key, node->conf->key_size, + node->conf->direct_server_port, node->conf->direct_iface, + node->conf->local_uuid, &node->conf->local_vmac, + node->conf->max_local_bvlc_len, node->conf->max_local_npdu_len, + node->conf->connect_timeout_s, node->conf->heartbeat_timeout_s, + node->conf->disconnect_timeout_s, node->conf->reconnnect_timeout_s, + node->conf->address_resolution_timeout_s, + node->conf->direct_connect_accept_enable, + node->conf->direct_connect_initiate_enable, bsc_node_switch_event, + node, &node->node_switch); + if (ret != BSC_SC_SUCCESS) { + node->state = BSC_NODE_STATE_IDLE; + bsc_hub_connector_stop(node->hub_connector); + bsc_hub_function_stop(node->hub_function); + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_start_state() <<< ret = %d\n", ret); + return ret; + } + } + if (!node->conf->hub_function_enabled && !node_switch_enabled(node->conf)) { + if (!node->conf->primaryURL) { + node->state = BSC_NODE_STATE_IDLE; + } else { + node->state = BSC_NODE_STATE_STARTED; + node->conf->event_func(node, BSC_NODE_EVENT_STARTED, NULL, NULL, 0); + } + } + DEBUG_PRINTF( + "bsc_node_start_state() hub_function %p hub_connector %p node_switch " + "%p\n", + node->hub_function, node->hub_connector, node->node_switch); + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_start_state() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Start the BACnet/SC node + * @param node - pointer to the BACnet/SC node + * @return BACnet/SC status + */ +BSC_SC_RET bsc_node_start(BSC_NODE *node) +{ + BSC_SC_RET ret; + + DEBUG_PRINTF("bsc_node_start() >>> node = %p\n", node); + + if (!node) { + DEBUG_PRINTF("bsc_node_start() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + bws_dispatch_lock(); + + if (node->state != BSC_NODE_STATE_IDLE) { + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_start() <<< ret = BSC_SC_INVALID_OPERATION\n"); + return BSC_SC_INVALID_OPERATION; + } + ret = bsc_node_start_state(node, BSC_NODE_STATE_STARTING); + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_start() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Stop the BACnet/SC node + * @param node - pointer to the BACnet/SC node + */ +void bsc_node_stop(BSC_NODE *node) +{ + DEBUG_PRINTF("bsc_node_stop() >>> node = %p\n", node); + + if (node) { + bws_dispatch_lock(); + + if (node->state != BSC_NODE_STATE_IDLE && + node->state != BSC_NODE_STATE_STOPPING) { + node->state = BSC_NODE_STATE_STOPPING; + bsc_hub_connector_stop(node->hub_connector); + if (node->conf->hub_function_enabled) { + bsc_hub_function_stop(node->hub_function); + } + if (node_switch_enabled(node->conf)) { + bsc_node_switch_stop(node->node_switch); + } + } + + bws_dispatch_unlock(); + } + + DEBUG_PRINTF("bsc_node_stop() <<<\n"); +} + +/** + * @brief Send a PDU to the BACnet/SC connected node + * @param p_node - pointer to the BACnet/SC node + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @return BACnet/SC status + */ +BSC_SC_RET +bsc_node_hub_connector_send(void *p_node, uint8_t *pdu, size_t pdu_len) +{ + BSC_NODE *node = (BSC_NODE *)p_node; + BSC_SC_RET ret; + + DEBUG_PRINTF( + "bsc_node_hub_connector_send() >>> p_node = %p, pdu = %p, " + "pdu_len = %d\n", + p_node, pdu, pdu_len); + + if (!node) { + DEBUG_PRINTF( + "bsc_node_hub_connector_send() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + bws_dispatch_lock(); + + if (node->state != BSC_NODE_STATE_STARTED) { + DEBUG_PRINTF("bsc_node_hub_connector_send() <<< ret = " + "BSC_SC_INVALID_OPERATION\n"); + bws_dispatch_unlock(); + return BSC_SC_INVALID_OPERATION; + } + + ret = bsc_hub_connector_send(node->hub_connector, pdu, pdu_len); + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_hub_connector_send() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Send a PDU to the BACnet/SC connected node + * @param p_node - pointer to the BACnet/SC node + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @return BACnet/SC status + */ +BSC_SC_RET bsc_node_send(BSC_NODE *p_node, uint8_t *pdu, size_t pdu_len) +{ + BSC_NODE *node = (BSC_NODE *)p_node; + BSC_SC_RET ret; + + DEBUG_PRINTF( + "bsc_node_send() >>> p_node = %p(%s), pdu = %p, " + "pdu_len = %d\n", + p_node, p_node ? bsc_vmac_to_string(&p_node->conf->local_vmac) : NULL, + pdu, pdu_len); + + if (!node) { + DEBUG_PRINTF("bsc_node_send() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + bws_dispatch_lock(); + + if (node->state != BSC_NODE_STATE_STARTED) { + DEBUG_PRINTF("bsc_node_send() <<< ret = " + "BSC_SC_INVALID_OPERATION\n"); + bws_dispatch_unlock(); + return BSC_SC_INVALID_OPERATION; + } + + if (node_switch_enabled(node->conf)) { + ret = bsc_node_switch_send(node->node_switch, pdu, pdu_len); + } else { + ret = bsc_hub_connector_send(node->hub_connector, pdu, pdu_len); + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_send() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Get the BACnet/SC node address resolution + * @param p_node - pointer to the BACnet/SC node + * @param vmac - pointer to the VMAC address + * @return pointer to the address resolution, or NULL if not found + */ +BSC_ADDRESS_RESOLUTION * +bsc_node_get_address_resolution(void *p_node, BACNET_SC_VMAC_ADDRESS *vmac) +{ + int i; + BSC_NODE *node = (BSC_NODE *)p_node; + bws_dispatch_lock(); + + if (!node || node->state != BSC_NODE_STATE_STARTED || !vmac) { + bws_dispatch_unlock(); + return NULL; + } + for (i = 0; i < BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM; i++) { + if (node->resolution[i].used && + !memcmp( + &vmac->address[0], &node->resolution[i].vmac.address[0], + BVLC_SC_VMAC_SIZE)) { + if (!mstimer_expired(&node->resolution[i].fresh_timer)) { + bws_dispatch_unlock(); + return &node->resolution[i]; + } else { + node_free_address_resolution(&node->resolution[i]); + bws_dispatch_unlock(); + return NULL; + } + } + } + bws_dispatch_unlock(); + return NULL; +} + +/** + * @brief Send an address resolution to the BACnet/SC node + * @param p_node - pointer to the BACnet/SC node + * @param dest - pointer to the destination VMAC address + * @return BACnet/SC status + */ +BSC_SC_RET +bsc_node_send_address_resolution(void *p_node, BACNET_SC_VMAC_ADDRESS *dest) +{ + BSC_NODE *node = (BSC_NODE *)p_node; + uint8_t pdu[32]; + size_t pdu_len; + BSC_SC_RET ret; + DEBUG_PRINTF( + "bsc_node_send_address_resolution() >>> node = %p, dest = %p\n", node, + dest); + pdu_len = bvlc_sc_encode_address_resolution( + pdu, sizeof(pdu), bsc_get_next_message_id(), NULL, dest); + ret = bsc_node_send(node, pdu, pdu_len); + DEBUG_PRINTF("bsc_node_send_address_resolution() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Connect to the BACnet/SC node directly + * @param node - pointer to the BACnet/SC node + * @param dest - pointer to the destination VMAC address + * @param urls - pointer to the URLs + * @param urls_cnt - URLs count + * @return BACnet/SC status + */ +BSC_SC_RET bsc_node_connect_direct( + BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt) +{ + BSC_SC_RET ret = BSC_SC_INVALID_OPERATION; + DEBUG_PRINTF( + "bsc_node_connect_direct() >>> node = %p, dest = %p, urls = " + "%p, urls_cnt = %d\n", + node, dest, urls, urls_cnt); + bws_dispatch_lock(); + if (node->state == BSC_NODE_STATE_STARTED && + node->conf->direct_connect_initiate_enable) { + ret = bsc_node_switch_connect(node->node_switch, dest, urls, urls_cnt); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_connect_direct() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Disconnect from the BACnet/SC node direct connection + * @param node - pointer to the BACnet/SC node + * @param dest - pointer to the destination VMAC address + */ +void bsc_node_disconnect_direct(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest) +{ + DEBUG_PRINTF( + "bsc_node_disconnect_direct() >>> node = %p, dest = %p\n", node, dest); + bws_dispatch_lock(); + if (node->state == BSC_NODE_STATE_STARTED && + node->conf->direct_connect_initiate_enable) { + bsc_node_switch_disconnect(node->node_switch, dest); + } + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_node_disconnect_direct() <<< \n"); +} + +/** + * @brief Determine if the direct connection to the BACnet/SC node is + * established + * @param node - pointer to the BACnet/SC node + * @param dest - pointer to the destination VMAC address + * @param urls - pointer to the URLs + * @param urls_cnt - URLs count + * @return true if the direct connection is established, false otherwise + */ +bool bsc_node_direct_connection_established( + BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt) +{ + bool ret = false; + bws_dispatch_lock(); + if (node->state == BSC_NODE_STATE_STARTED && + (node->conf->direct_connect_initiate_enable || + node->conf->direct_connect_accept_enable)) { + ret = + bsc_node_switch_connected(node->node_switch, dest, urls, urls_cnt); + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Get the BACnet/SC node state + * @param node - pointer to the BACnet/SC node + * @return BACnet/SC hub connector state + */ +BACNET_SC_HUB_CONNECTOR_STATE +bsc_node_hub_connector_state(BSC_NODE *node) +{ + BACNET_SC_HUB_CONNECTOR_STATE ret = + BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION; + bws_dispatch_lock(); + if (node->state == BSC_NODE_STATE_STARTED) { + ret = bsc_hub_connector_state(node->hub_connector); + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Get the BACnet/SC node hub connector status + * @param node - pointer to the BACnet/SC node + * @param primary - true if the primary hub connector status is requested + * @return pointer to the hub connector status + */ +BACNET_SC_HUB_CONNECTION_STATUS * +bsc_node_hub_connector_status(BSC_NODE *node, bool primary) +{ + BACNET_SC_HUB_CONNECTION_STATUS *ret = NULL; + bws_dispatch_lock(); + if (node->state == BSC_NODE_STATE_STARTED) { + ret = bsc_hub_connector_status(node->hub_connector, primary); + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Get the BACnet/SC node hub function status + * @param node - pointer to the BACnet/SC node + * @param cnt - pointer to the status count + * @return pointer to the hub function status + */ +BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS * +bsc_node_hub_function_status(BSC_NODE *node, size_t *cnt) +{ + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *ret = NULL; + bws_dispatch_lock(); + if (node->state == BSC_NODE_STATE_STARTED && + node->conf->hub_function_enabled) { + *cnt = BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM; + ret = node->hub_status; + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Get the BACnet/SC node direct connection status + * @param node - pointer to the BACnet/SC node + * @param cnt - pointer to the status count + * @return pointer to the direct connection status + */ +BACNET_SC_DIRECT_CONNECTION_STATUS * +bsc_node_direct_connection_status(BSC_NODE *node, size_t *cnt) +{ + BACNET_SC_DIRECT_CONNECTION_STATUS *ret = NULL; + bws_dispatch_lock(); + if (node->state == BSC_NODE_STATE_STARTED && + (node->conf->direct_connect_accept_enable || + node->conf->direct_connect_initiate_enable)) { + *cnt = BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM; + ret = node->direct_status; + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Maintenance timer for the BACnet/SC node + * @param seconds - number of seconds since the last call + */ +void bsc_node_maintenance_timer(uint16_t seconds) +{ + (void)seconds; + + bsc_socket_maintenance_timer(seconds); + bsc_hub_connector_maintenance_timer(seconds); + bsc_node_switch_maintenance_timer(seconds); +} + +/** + * @brief Add failed request information to the BACnet/SC node + * @param r - pointer to the failed request information + * @param peer - pointer to the peer address + * @param vmac - pointer to the VMAC address + * @param uuid - pointer to the UUID + * @param error - error code + * @param error_desc - error description + */ +static void bsc_node_add_failed_request_info( + BACNET_SC_FAILED_CONNECTION_REQUEST *r, + BACNET_HOST_N_PORT_DATA *peer, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE error, + const char *error_desc) +{ + bsc_set_timestamp(&r->Timestamp); + memcpy(&r->Peer_Address, peer, sizeof(*peer)); + memcpy(r->Peer_VMAC, &vmac->address[0], BVLC_SC_VMAC_SIZE); + memcpy(&r->Peer_UUID.uuid.uuid128[0], &uuid->uuid[0], BVLC_SC_UUID_SIZE); + r->Error = error; + if (!error_desc) { + r->Error_Details[0] = 0; + } else { + bsc_copy_str(r->Error_Details, error_desc, sizeof(r->Error_Details)); + } +} + +/** + * @brief Store failed request information to the BACnet/SC node + * @param node - pointer to the BACnet/SC node + * @param peer - pointer to the peer address + * @param vmac - pointer to the VMAC address + * @param uuid - pointer to the UUID + * @param error - error code + * @param error_desc - error description + */ +void bsc_node_store_failed_request_info( + BSC_NODE *node, + BACNET_HOST_N_PORT_DATA *peer, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE error, + const char *error_desc) +{ + size_t i, j = 0; + bool found = false; + BACNET_DATE_TIME t; + + bws_dispatch_lock(); + + for (i = 0; i < BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM; i++) { + if (node->failed[i].Peer_Address.host[0] == 0) { + found = true; + break; + } + } + if (found) { + bsc_node_add_failed_request_info( + &node->failed[i], peer, vmac, uuid, error, error_desc); + } else { + bsc_set_timestamp(&t); + for (i = 0; i < BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM; i++) { + if (datetime_compare(&node->failed[i].Timestamp, &t) < 0) { + j = i; + memcpy(&t, &node->failed[i].Timestamp, sizeof(t)); + } + } + bsc_node_add_failed_request_info( + &node->failed[j], peer, vmac, uuid, error, error_desc); + } + + bws_dispatch_unlock(); +} + +/** + * @brief Get the failed request status from the BACnet/SC node + * @param node - pointer to the BACnet/SC node + * @param cnt - pointer to the status count + * @return pointer to the failed request status + */ +BACNET_SC_FAILED_CONNECTION_REQUEST * +bsc_node_failed_requests_status(BSC_NODE *node, size_t *cnt) +{ + BACNET_SC_FAILED_CONNECTION_REQUEST *ret = NULL; + bws_dispatch_lock(); + if (node->state == BSC_NODE_STATE_STARTED && + (node->conf->direct_connect_accept_enable || + node->conf->hub_function_enabled)) { + ret = node->failed; + *cnt = BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM; + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Find the BACnet/SC node direct connection status for the VMAC address + * @param node - pointer to the BACnet/SC node + * @param vmac - pointer to the VMAC address + * @return pointer to the direct connection status + */ +BACNET_SC_DIRECT_CONNECTION_STATUS *bsc_node_find_direct_status_for_vmac( + BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac) +{ + int i; + int index = -1; + BACNET_DATE_TIME timestamp; + + for (i = 0; i < BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM; i++) { + if (!datetime_is_valid( + &node->direct_status[i].Connect_Timestamp.date, + &node->direct_status[i].Connect_Timestamp.time)) { + return &node->direct_status[i]; + } + if (!memcmp( + &node->direct_status[i].Peer_VMAC[0], &vmac->address[0], + BVLC_SC_VMAC_SIZE)) { + return &node->direct_status[i]; + } + } + + /* ok, all entries are already filled, try to found oldest entry with + non connected status */ + + for (i = 0; i < BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM; i++) { + if (node->direct_status[i].State != + BACNET_SC_CONNECTION_STATE_CONNECTED && + datetime_is_valid( + &node->direct_status[i].Disconnect_Timestamp.date, + &node->direct_status[i].Disconnect_Timestamp.time)) { + if (index == -1 || + (datetime_compare( + &node->direct_status[i].Disconnect_Timestamp, ×tamp) < + 0)) { + index = i; + memcpy( + ×tamp, &node->direct_status[i].Disconnect_Timestamp, + sizeof(timestamp)); + } + } + } + + if (index != -1) { + return &node->direct_status[index]; + } + + /* ok, all entries are already filled and all are in connected state, + so reuse oldest entry which is in connected state */ + + memcpy( + ×tamp, &node->direct_status[0].Connect_Timestamp, + sizeof(timestamp)); + index = 0; + + for (i = 0; i < BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM; i++) { + if (datetime_compare( + &node->direct_status[i].Connect_Timestamp, ×tamp) < 0) { + index = i; + memcpy( + ×tamp, &node->direct_status[i].Connect_Timestamp, + sizeof(timestamp)); + } + } + + return &node->direct_status[index]; +} + +/** + * @brief Find the BACnet/SC node hub function status for the VMAC address + * @param node - pointer to the BACnet/SC node + * @param vmac - pointer to the VMAC address + * @return pointer to the hub function status + */ +BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS * +bsc_node_find_hub_status_for_vmac(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac) +{ + int i; + int index = -1; + BACNET_DATE_TIME timestamp; + + for (i = 0; i < BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM; i++) { + if (!datetime_is_valid( + &node->hub_status[i].Connect_Timestamp.date, + &node->hub_status[i].Connect_Timestamp.time)) { + return &node->hub_status[i]; + } + if (!memcmp( + &node->hub_status[i].Peer_VMAC[0], &vmac->address[0], + BVLC_SC_VMAC_SIZE)) { + return &node->hub_status[i]; + } + } + + /* ok, all entries are already filled, try to found oldest entry with + non connected status */ + + for (i = 0; i < BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM; i++) { + if (node->hub_status[i].State != BACNET_SC_CONNECTION_STATE_CONNECTED && + datetime_is_valid( + &node->hub_status[i].Disconnect_Timestamp.date, + &node->hub_status[i].Disconnect_Timestamp.time)) { + if (index == -1 || + (datetime_compare( + &node->hub_status[i].Disconnect_Timestamp, ×tamp) < + 0)) { + index = i; + memcpy( + ×tamp, &node->hub_status[i].Disconnect_Timestamp, + sizeof(timestamp)); + } + } + } + + if (index != -1) { + return &node->hub_status[index]; + } + + /* ok, all entries are already filled and all are in connected state, + so reuse oldest entry which is in connected state */ + + memcpy( + ×tamp, &node->hub_status[0].Connect_Timestamp, sizeof(timestamp)); + index = 0; + + for (i = 0; i < BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM; i++) { + if (datetime_compare( + &node->hub_status[i].Connect_Timestamp, ×tamp) < 0) { + index = i; + memcpy( + ×tamp, &node->hub_status[i].Connect_Timestamp, + sizeof(timestamp)); + } + } + + return &node->hub_status[index]; +} diff --git a/src/bacnet/datalink/bsc/bsc-node.h b/src/bacnet/datalink/bsc/bsc-node.h new file mode 100644 index 00000000..ed3b1690 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-node.h @@ -0,0 +1,162 @@ +/** + * @file + * @brief BACNet secure connect node API. + * In general, user should not use that API directly, + * BACNet/SC datalink API should be used. + * @author Kirill Neznamov + * @date October 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#ifndef BACNET_DATALINK_BSC_NODE_H +#define BACNET_DATALINK_BSC_NODE_H +#include +#include +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/datalink/bsc/bsc-conf.h" +#include "bacnet/datalink/bsc/bsc-retcodes.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/basic/sys/mstimer.h" +#include "bacnet/basic/object/sc_netport.h" + +typedef struct BSC_Node BSC_NODE; + +typedef enum { + BSC_NODE_EVENT_STARTED = 1, + BSC_NODE_EVENT_STOPPED = 2, + BSC_NODE_EVENT_RESTARTED = 3, + BSC_NODE_EVENT_RECEIVED_NPDU = 4, + BSC_NODE_EVENT_RECEIVED_RESULT = 5, + BSC_NODE_EVENT_RECEIVED_ADVERTISIMENT = 6, + BSC_NODE_EVENT_DIRECT_CONNECTED = 7, + BSC_NODE_EVENT_DIRECT_DISCONNECTED = 8 +} BSC_NODE_EVENT; + +typedef void (*BSC_NODE_EVENT_FUNC)( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len); +typedef struct { + bool used; + BACNET_SC_VMAC_ADDRESS vmac; + uint8_t utf8_urls[BSC_CONF_NODE_MAX_URIS_NUM_IN_ADDRESS_RESOLUTION_ACK] + [BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK + 1]; + size_t urls_num; + struct mstimer fresh_timer; +} BSC_ADDRESS_RESOLUTION; + +typedef struct { + uint8_t *ca_cert_chain; + size_t ca_cert_chain_size; + uint8_t *cert_chain; + size_t cert_chain_size; + uint8_t *key; + size_t key_size; + BACNET_SC_UUID *local_uuid; + BACNET_SC_VMAC_ADDRESS local_vmac; + uint16_t max_local_bvlc_len; + uint16_t max_local_npdu_len; + uint16_t connect_timeout_s; + uint16_t heartbeat_timeout_s; + uint16_t disconnect_timeout_s; + uint16_t reconnnect_timeout_s; + uint16_t address_resolution_timeout_s; + uint16_t address_resolution_freshness_timeout_s; + char *primaryURL; + char *failoverURL; + uint16_t hub_server_port; + uint16_t direct_server_port; + char *hub_iface; + char *direct_iface; + bool direct_connect_accept_enable; + bool direct_connect_initiate_enable; + bool hub_function_enabled; + char *direct_connection_accept_uris; /* URIs joined ' 'space */ + size_t direct_connection_accept_uris_len; + BSC_NODE_EVENT_FUNC event_func; +} BSC_NODE_CONF; + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_node_init(BSC_NODE_CONF *conf, BSC_NODE **node); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_node_deinit(BSC_NODE *node); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_node_start(BSC_NODE *node); + +BACNET_STACK_EXPORT +void bsc_node_stop(BSC_NODE *node); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_node_send(BSC_NODE *node, uint8_t *pdu, size_t pdu_len); + +BACNET_STACK_EXPORT +BSC_ADDRESS_RESOLUTION * +bsc_node_get_address_resolution(void *node, BACNET_SC_VMAC_ADDRESS *vmac); + +BACNET_STACK_EXPORT +BSC_SC_RET +bsc_node_send_address_resolution(void *node, BACNET_SC_VMAC_ADDRESS *dest); + +BACNET_STACK_EXPORT +BSC_SC_RET +bsc_node_hub_connector_send(void *user_arg, uint8_t *pdu, size_t pdu_len); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_node_connect_direct( + BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt); + +BACNET_STACK_EXPORT +void bsc_node_disconnect_direct(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_CONNECTOR_STATE +bsc_node_hub_connector_state(BSC_NODE *node); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_CONNECTION_STATUS * +bsc_node_hub_connector_status(BSC_NODE *node, bool primary); + +BACNET_STACK_EXPORT +bool bsc_node_direct_connection_established( + BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *dest, char **urls, size_t urls_cnt); + +BACNET_STACK_EXPORT +void bsc_node_maintenance_timer(uint16_t seconds); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS * +bsc_node_hub_function_status(BSC_NODE *node, size_t *cnt); + +BACNET_STACK_EXPORT +BACNET_SC_DIRECT_CONNECTION_STATUS * +bsc_node_direct_connection_status(BSC_NODE *node, size_t *cnt); + +BACNET_STACK_EXPORT +void bsc_node_store_failed_request_info( + BSC_NODE *node, + BACNET_HOST_N_PORT_DATA *peer, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE error, + const char *error_desc); + +BACNET_STACK_EXPORT +BACNET_SC_FAILED_CONNECTION_REQUEST * +bsc_node_failed_requests_status(BSC_NODE *node, size_t *cnt); + +BACNET_STACK_EXPORT +BACNET_SC_DIRECT_CONNECTION_STATUS *bsc_node_find_direct_status_for_vmac( + BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac); + +BACNET_STACK_EXPORT +BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS * +bsc_node_find_hub_status_for_vmac(BSC_NODE *node, BACNET_SC_VMAC_ADDRESS *vmac); + +#endif diff --git a/src/bacnet/datalink/bsc/bsc-retcodes.h b/src/bacnet/datalink/bsc/bsc-retcodes.h new file mode 100644 index 00000000..6c615ada --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-retcodes.h @@ -0,0 +1,20 @@ +/** + * @file + * @brief BACNet secure connect main include header. + * @author Kirill Neznamov + * @date August 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_RETCODES_H +#define BACNET_DATALINK_BSC_RETCODES_H +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" + +typedef enum { + BSC_SC_SUCCESS = 0, + BSC_SC_NO_RESOURCES = 1, + BSC_SC_BAD_PARAM = 2, + BSC_SC_INVALID_OPERATION = 3 +} BSC_SC_RET; + +#endif diff --git a/src/bacnet/datalink/bsc/bsc-socket.c b/src/bacnet/datalink/bsc/bsc-socket.c new file mode 100644 index 00000000..40076b41 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-socket.c @@ -0,0 +1,1839 @@ + +/** + * @file + * @brief BACNet secure connect API. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/bsc-socket.h" +#include "bacnet/datalink/bsc/bsc-util.h" +#include "bacnet/basic/sys/mstimer.h" +#include "bacnet/basic/sys/debug.h" + +#define DEBUG_BSC_SOCKET 0 + +#if DEBUG_BSC_SOCKET == 1 +#define DEBUG_PRINTF debug_printf +#else +#undef DEBUG_ENABLED +#define DEBUG_PRINTF debug_printf_disabled +#endif + +static const char *s_error_no_origin = + "'Originating Virtual Address' field must be present"; +static const char *s_error_dest_presented = + "'Destination Virtual Address' field must be absent"; +static const char *s_error_origin_presented = + "'Originating Virtual Address' field must be absent"; +static const char *s_error_no_dest = + "'Destination Virtual Address' field must be present"; + +static BSC_SOCKET_CTX *bsc_socket_ctx[BSC_SOCKET_CTX_NUM] = { 0 }; +static BVLC_SC_DECODED_MESSAGE bsc_dm = { 0 }; + +#define TX_BUF_PTR(c) \ + &c->tx_buf[c->tx_buf_size + sizeof(uint16_t) + BSC_CONF_TX_PRE] + +#define TX_BUF_UPDATE(c, len) \ + memcpy(&c->tx_buf[c->tx_buf_size], &len, sizeof(uint16_t)); \ + c->tx_buf_size += sizeof(uint16_t) + BSC_CONF_TX_PRE + len + +#define TX_BUF_BYTES_AVAIL(c) \ + (((sizeof(c->tx_buf) - c->tx_buf_size) >= \ + (sizeof(uint16_t) + BSC_CONF_TX_PRE)) \ + ? (sizeof(c->tx_buf) - c->tx_buf_size - sizeof(uint16_t) - \ + BSC_CONF_TX_PRE) \ + : 0) + +/** + * @brief Add the socket context to the list + * @param ctx - pointer to the socket context + * @return true if the context was added, otherwise false + */ +static bool bsc_ctx_add(BSC_SOCKET_CTX *ctx) +{ + int i; + for (i = 0; i < BSC_SOCKET_CTX_NUM; i++) { + if (bsc_socket_ctx[i] == NULL) { + bsc_socket_ctx[i] = ctx; + return true; + } + } + return false; +} + +/** + * @brief Remove the socket context from the list + * @param ctx - pointer to the socket context + */ +static void bsc_ctx_remove(BSC_SOCKET_CTX *ctx) +{ + int i; + for (i = 0; i < BSC_SOCKET_CTX_NUM; i++) { + if (bsc_socket_ctx[i] == ctx) { + bsc_socket_ctx[i] = NULL; + break; + } + } +} + +/** + * @brief Reset the BACnet Secure Connect + * @param c - pointer to the socket + */ +static void bsc_reset_socket(BSC_SOCKET *c) +{ + memset(&c->vmac, 0, sizeof(c->vmac)); + memset(&c->uuid, 0, sizeof(c->uuid)); + c->tx_buf_size = 0; +} + +/** + * @brief Initialize the BACnet Secure Connect context + * @param type - type of the context + * @param cfg - pointer to the configuration + * @param proto - WebSocket protocol + * @param port - port number + * @param iface - interface name + * @param ca_cert_chain - pointer to the CA certificate chain + * @param ca_cert_chain_size - size of the CA certificate chain + * @param cert_chain - pointer to the certificate chain + * @param cert_chain_size - size of the certificate chain + * @param key - pointer to the private key + * @param key_size - size of the private key + * @param local_uuid - pointer to the local UUID + * @param local_vmac - pointer to the local VMAC address + * @param max_local_bvlc_len - maximum BVLC message size + * @param max_local_ndpu_len - maximum NPDU message size + * @param connect_timeout_s - connection timeout in seconds + * @param heartbeat_timeout_s - heartbeat timeout in seconds + * @param disconnect_timeout_s - disconnect timeout in seconds + */ +void bsc_init_ctx_cfg( + BSC_SOCKET_CTX_TYPE type, + BSC_CONTEXT_CFG *cfg, + BSC_WEBSOCKET_PROTOCOL proto, + uint16_t port, + char *iface, + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + uint16_t max_local_bvlc_len, + uint16_t max_local_ndpu_len, + unsigned int connect_timeout_s, + unsigned int heartbeat_timeout_s, + unsigned int disconnect_timeout_s) +{ + DEBUG_PRINTF("bsc_init_ctx_cfg() >>> cfg = %p\n"); + if (cfg) { + cfg->proto = proto; + cfg->port = port; + cfg->type = type; + cfg->iface = iface; + cfg->ca_cert_chain = ca_cert_chain; + cfg->ca_cert_chain_size = ca_cert_chain_size; + cfg->cert_chain = cert_chain; + cfg->cert_chain_size = cert_chain_size; + cfg->priv_key = key; + cfg->priv_key_size = key_size; + bsc_copy_uuid(&cfg->local_uuid, local_uuid); + bsc_copy_vmac(&cfg->local_vmac, local_vmac); + cfg->max_bvlc_len = max_local_bvlc_len; + cfg->max_ndpu_len = max_local_ndpu_len; + cfg->connect_timeout_s = connect_timeout_s; + cfg->heartbeat_timeout_s = heartbeat_timeout_s; + cfg->disconnect_timeout_s = disconnect_timeout_s; + } + DEBUG_PRINTF("bsc_init_ctx_cfg() <<<\n"); +} + +/** + * @brief Find the socket context by the WebSocket handle + * @param ctx - pointer to the socket context + * @param h - pointer to the socket + */ +static BSC_SOCKET * +bsc_find_conn_by_websocket(BSC_SOCKET_CTX *ctx, BSC_WEBSOCKET_HANDLE h) +{ + size_t i; + for (i = 0; i < ctx->sock_num; i++) { + if (ctx->sock[i].state != BSC_SOCK_STATE_IDLE && ctx->sock[i].wh == h) { + return &ctx->sock[i]; + } + } + return NULL; +} + +/** + * @brief Find the free socket + * @param ctx - pointer to the socket context + * @return pointer to the free socket + */ +static BSC_SOCKET *bsc_find_free_socket(BSC_SOCKET_CTX *ctx) +{ + size_t i; + for (i = 0; i < ctx->sock_num; i++) { + if (ctx->sock[i].state == BSC_SOCK_STATE_IDLE) { + bsc_reset_socket(&ctx->sock[i]); + return &ctx->sock[i]; + } + } + return NULL; +} + +/** + * @brief Process the socket error + * @param c - pointer to the socket + * @param reason - error code + */ +static void bsc_srv_process_error(BSC_SOCKET *c, BACNET_ERROR_CODE reason) +{ + DEBUG_PRINTF( + "bsc_srv_process_error() >>> c = %p, reason = %d\n", c, reason); + c->state = BSC_SOCK_STATE_ERROR; + c->reason = reason; + bws_srv_disconnect(c->ctx->sh, c->wh); + DEBUG_PRINTF("bsc_srv_process_error() <<<\n"); +} + +/** + * @brief Process the socket error + * @param c - pointer to the socket + * @param reason - error code + */ +static void bsc_cli_process_error(BSC_SOCKET *c, BACNET_ERROR_CODE reason) +{ + DEBUG_PRINTF("bsc_cli_process_error) >>> c = %p, reason = %d\n", c, reason); + c->state = BSC_SOCK_STATE_ERROR; + c->reason = reason; + bws_cli_disconnect(c->wh); + DEBUG_PRINTF("bsc_cli_process_error) <<<\n"); +} + +/** + * @brief Prepare the error message + * @param c - pointer to the socket + * @param origin - pointer to the origin VMAC address + * @param dest - pointer to the destination VMAC address + * @param bvlc_function - BVLC function + * @param error_header_marker - pointer to the error header marker + * @param error_class - error class + * @param error_code - error code + * @param utf8_details_string - UTF-8 reason text + * @return true if the error was prepared, otherwise false + */ +static bool bsc_prepare_error_extended( + BSC_SOCKET *c, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t bvlc_function, + uint8_t *error_header_marker, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code, + uint8_t *utf8_details_string) +{ + uint16_t eclass = (uint16_t)error_class; + uint16_t ecode = (uint16_t)error_code; + size_t len; + uint16_t message_id; + + DEBUG_PRINTF( + "bsc_prepare_error_extended() >>> bvlc_function = %d\n", bvlc_function); + +#if DEBUG_BSC_SOCKET == 1 + if (error_header_marker) { + DEBUG_PRINTF( + " error_header_marker = %d\n", + *error_header_marker); + } + if (error_class) { + DEBUG_PRINTF( + " error_class = %d\n", error_class); + } + if (error_code) { + DEBUG_PRINTF( + " error_code = %d\n", error_code); + } + if (utf8_details_string) { + DEBUG_PRINTF( + " utf8_details_string = %s\n", + utf8_details_string); + } + if (origin) { + DEBUG_PRINTF( + " origin = %s\n", + bsc_vmac_to_string(origin)); + } + if (dest) { + DEBUG_PRINTF( + " dest = %s\n", + bsc_vmac_to_string(dest)); + } +#endif + + message_id = bsc_get_next_message_id(); + DEBUG_PRINTF(" message_id = %d\n", message_id); + + len = bvlc_sc_encode_result( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), message_id, origin, dest, + bvlc_function, 1, error_header_marker, &eclass, &ecode, + utf8_details_string); + if (len) { + TX_BUF_UPDATE(c, len); + DEBUG_PRINTF( + "bsc_prepare_error_extended() <<< ret = true, pdu_len = %d\n", len); + return true; + } + DEBUG_PRINTF("bsc_prepare_error_extended() <<< ret = false\n"); + return false; +} + +/** + * @brief Prepare the protocol error extended message + * @param c - pointer to the socket + * @param dm - pointer to the decoded message + * @param origin - pointer to the origin VMAC address + * @param dest - pointer to the destination VMAC address + * @param error_header_marker - pointer to the error header marker + * @param error_class - error class + * @param error_code - error code + * @param utf8_details_string - UTF-8 reason text + * @return true if the error was prepared, otherwise false + */ +static bool bsc_prepare_protocol_error_extended( + BSC_SOCKET *c, + BVLC_SC_DECODED_MESSAGE *dm, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *error_header_marker, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code, + uint8_t *utf8_details_string) +{ + if (bvlc_sc_need_send_bvlc_result(dm)) { + return bsc_prepare_error_extended( + c, origin, dest, BVLC_SC_RESULT, error_header_marker, error_class, + error_code, utf8_details_string); + } + return false; +} + +/** + * @brief Prepare the protocol error message + * @param c - pointer to the socket + * @param dm - pointer to the decoded message + * @param origin - pointer to the origin VMAC address + * @param dest - pointer to the destination VMAC address + * @param error_class - error class + * @param error_code - error code + * @param utf8_details_string - UTF-8 reason text + * @return true if the error was prepared, otherwise false + */ +static bool bsc_prepare_protocol_error( + BSC_SOCKET *c, + BVLC_SC_DECODED_MESSAGE *dm, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code, + uint8_t *utf8_details_string) +{ + return bsc_prepare_protocol_error_extended( + c, dm, origin, dest, NULL, error_class, error_code, + utf8_details_string); +} + +/** + * @brief Clear the VMAC and UUID for BACnet Secure Connect socket + * @param c - pointer to the socket + */ +static void bsc_clear_vmac_and_uuid(BSC_SOCKET *c) +{ + memset(&c->vmac, 0, sizeof(c->vmac)); + memset(&c->uuid, 0, sizeof(c->uuid)); +} + +/** + * @brief Set the BACnet Secure Connect socket to the idle state + * @param c - pointer to the socket + */ +static void bsc_set_socket_idle(BSC_SOCKET *c) +{ + c->state = BSC_SOCK_STATE_IDLE; + c->wh = BSC_WEBSOCKET_INVALID_HANDLE; +} + +/** + * @brief Set the BACnet Secure Connect socket to the disconnecting state + * @param c - pointer to the socket + * @param dm - pointer to the decoded message + * @param buf - pointer to the buffer + * @param buflen - buffer length + * @param need_disconnect - pointer to the flag + */ +static void bsc_process_socket_disconnecting( + BSC_SOCKET *c, + BVLC_SC_DECODED_MESSAGE *dm, + uint8_t *buf, + size_t buflen, + bool *need_disconnect) +{ + DEBUG_PRINTF("bsc_process_socket_disconnecting() >>> c = %p\n", c); + + if (dm->hdr.bvlc_function == BVLC_SC_DISCONNECT_ACK) { +#if DEBUG_BSC_SOCKET == 1 + if (dm->hdr.message_id != c->expected_disconnect_message_id) { + DEBUG_PRINTF( + "bsc_process_socket_disconnecting() got disconnect ack with " + "unexpected message id %d for socket %p\n", + dm->hdr.message_id, c); + } else { + DEBUG_PRINTF( + "bsc_process_socket_disconnecting() got disconnect ack for " + "socket %p\n", + c); + } +#endif + *need_disconnect = true; + } else if (dm->hdr.bvlc_function == BVLC_SC_RESULT) { + if (dm->payload.result.bvlc_function == BVLC_SC_DISCONNECT_REQUEST && + dm->payload.result.result != 0) { + DEBUG_PRINTF( + "bsc_process_socket_disconnecting() got BVLC_SC_RESULT " + "NAK on BVLC_SC_DISCONNECT_REQUEST\n"); + *need_disconnect = true; + } + } else if ( + dm->hdr.bvlc_function == BVLC_SC_ENCAPSULATED_NPDU || + dm->hdr.bvlc_function == BVLC_SC_ADDRESS_RESOLUTION || + dm->hdr.bvlc_function == BVLC_SC_ADDRESS_RESOLUTION_ACK || + dm->hdr.bvlc_function == BVLC_SC_ADVERTISIMENT || + dm->hdr.bvlc_function == BVLC_SC_ADVERTISIMENT_SOLICITATION || + dm->hdr.bvlc_function == BVLC_SC_PROPRIETARY_MESSAGE) { + DEBUG_PRINTF( + "bsc_process_socket_disconnecting() emit received event " + "buf = %p, size = %d\n", + buf, buflen); + + c->ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_RECEIVED, 0, NULL, buf, buflen, dm); + } + DEBUG_PRINTF("bsc_process_socket_disconnecting() <<<\n"); +} + +/** + * @brief Process the socket connected state + * @param c - pointer to the socket + * @param dm - pointer to the decoded message + * @param buf - pointer to the buffer + * @param buflen - buffer length + * @param need_disconnect - pointer to the flag + * @param need_send - pointer to the flag + */ +static void bsc_process_socket_connected_state( + BSC_SOCKET *c, + BVLC_SC_DECODED_MESSAGE *dm, + uint8_t *buf, + size_t buflen, + bool *need_disconnect, + bool *need_send) +{ + uint16_t message_id; + size_t len; + + DEBUG_PRINTF( + "bsc_process_socket_connected_state() >>> c = %p, dm = %p, buf = %p, " + "buflen = %d\n", + c, dm, buf, buflen); + + if (dm->hdr.bvlc_function == BVLC_SC_HEARTBEAT_ACK) { +#if DEBUG_ENABLED == 1 + if (dm->hdr.message_id != c->expected_heartbeat_message_id) { + DEBUG_PRINTF( + "bsc_process_socket_connected_state() got heartbeat ack with " + "unexpected message id %d for socket %p\n", + dm->hdr.message_id, c); + } else { + DEBUG_PRINTF( + "bsc_process_socket_connected_state() got heartbeat ack for " + "socket %p\n", + c); + } +#endif + } else if (dm->hdr.bvlc_function == BVLC_SC_HEARTBEAT_REQUEST) { + DEBUG_PRINTF( + "bsc_process_socket_connected_state() got heartbeat " + "request with message_id %d\n", + dm->hdr.message_id); + message_id = dm->hdr.message_id; + len = bvlc_sc_encode_heartbeat_ack( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), message_id); + if (len) { + TX_BUF_UPDATE(c, len); + *need_send = true; + } +#if DEBUG_BSC_SOCKET == 1 + else { + DEBUG_PRINTF( + "bsc_process_socket_connected_state() no resources to " + "answer on heartbeat request " + "socket %p\n", + c); + } +#endif + } else if (dm->hdr.bvlc_function == BVLC_SC_DISCONNECT_REQUEST) { + DEBUG_PRINTF( + "bsc_process_socket_connected_state() got disconnect " + "request with message_id %d\n", + dm->hdr.message_id); + message_id = dm->hdr.message_id; + len = bvlc_sc_encode_disconnect_ack( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), message_id); + if (len) { + TX_BUF_UPDATE(c, len); + c->reason = ERROR_CODE_SUCCESS; + c->state = BSC_SOCK_STATE_ERROR_FLUSH_TX; + *need_send = true; + } else { + DEBUG_PRINTF( + "bsc_process_socket_connected_state() no resources to answer " + "on disconnect request, just disconnecting without ack\n"); + c->state = BSC_SOCK_STATE_DISCONNECTING; + *need_disconnect = true; + } + } else if (dm->hdr.bvlc_function == BVLC_SC_DISCONNECT_ACK) { + /* This is unexpected! We assume that the remote peer is confused and */ + /* thought we sent a Disconnect-Request so we'll close the socket */ + /* and hope that remote peer clears itself up. */ + DEBUG_PRINTF( + "bsc_process_socket_connected_state() got unexpected " + "disconnect ack with message_id %d\n", + dm->hdr.message_id); + c->state = BSC_SOCK_STATE_DISCONNECTING; + *need_disconnect = true; + } else if (dm->hdr.bvlc_function == BVLC_SC_RESULT) { + DEBUG_PRINTF( + "bsc_process_socket_connected_state() emit received event " + "buf = %p, size = %d\n", + buf, buflen); + c->ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_RECEIVED, 0, NULL, buf, buflen, dm); + } else if ( + dm->hdr.bvlc_function == BVLC_SC_ENCAPSULATED_NPDU || + dm->hdr.bvlc_function == BVLC_SC_ADDRESS_RESOLUTION || + dm->hdr.bvlc_function == BVLC_SC_ADDRESS_RESOLUTION_ACK || + dm->hdr.bvlc_function == BVLC_SC_ADVERTISIMENT || + dm->hdr.bvlc_function == BVLC_SC_ADVERTISIMENT_SOLICITATION || + dm->hdr.bvlc_function == BVLC_SC_PROPRIETARY_MESSAGE) { + DEBUG_PRINTF( + "bsc_process_socket_connected_state() emit received event " + "buf = %p, size = %d\n", + buf, buflen); + + c->ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_RECEIVED, 0, NULL, buf, buflen, dm); + } + + DEBUG_PRINTF("bsc_process_socket_connected_state() <<<\n"); +} + +/** + * @brief Process the socket state + * @param c - pointer to the socket + * @param dm - pointer to the decoded message + * @param rx_buf - pointer to the buffer + * @param rx_buf_size - buffer size + * @param need_disconnect - pointer to the flag + * @param need_send - pointer to the flag + */ +static void bsc_process_socket_state( + BSC_SOCKET *c, + BVLC_SC_DECODED_MESSAGE *dm, + uint8_t *rx_buf, + size_t rx_buf_size, + bool *need_disconnect, + bool *need_send) +{ + bool expired; + BACNET_ERROR_CODE code; + BACNET_ERROR_CLASS class; + const char *err_desc = NULL; + bool valid = true; + size_t len; + + DEBUG_PRINTF( + "bsc_process_socket_state() >>> ctx = %p, c = %p, state = %d, " + "rx_buf = %p, rx_buf_size = %d\n", + c->ctx, c, c->state, rx_buf, rx_buf_size); + + if (rx_buf) { + if (!bvlc_sc_decode_message( + rx_buf, rx_buf_size, dm, &code, &class, &err_desc)) { + /* we use this error code+class to indicate that the received bvlc + message has length less than 4 octets. + According EA-001-4 'Clarifying BVLC-Result in BACnet/SC + 'If a BVLC message is received that has fewer than four octets, + a BVLC-Result NAK shall not be returned. The message shall be + discarded and not be processed.' */ + if ((code != ERROR_CODE_DISCARD) && + (class != ERROR_CLASS_COMMUNICATION)) { + *need_send = bsc_prepare_protocol_error( + c, dm, dm->hdr.origin, dm->hdr.dest, class, code, + (uint8_t *)err_desc); + } +#if DEBUG_BSC_SOCKET == 1 + else { + DEBUG_PRINTF( + "bsc_process_socket_state() decoding failed, message " + "is silently dropped cause it's length < 4 bytes\n"); + } +#endif + } else { + DEBUG_PRINTF( + "c->dm.hdr.bvlc_function == %d, message_id = %d\n", + dm->hdr.bvlc_function, dm->hdr.message_id); + if (dm->hdr.bvlc_function == BVLC_SC_ENCAPSULATED_NPDU || + dm->hdr.bvlc_function == BVLC_SC_ADVERTISIMENT || + dm->hdr.bvlc_function == BVLC_SC_ADDRESS_RESOLUTION_ACK || + dm->hdr.bvlc_function == BVLC_SC_ADDRESS_RESOLUTION || + dm->hdr.bvlc_function == BVLC_SC_ADVERTISIMENT_SOLICITATION || + dm->hdr.bvlc_function == BVLC_SC_RESULT) { + if (c->ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR && + c->ctx->cfg->proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + /* this is a case when socket is a hub connector receiving + */ + /* from hub */ + if (dm->hdr.origin == NULL && + dm->hdr.bvlc_function != BVLC_SC_RESULT) { + class = ERROR_CLASS_COMMUNICATION; + code = ERROR_CODE_HEADER_ENCODING_ERROR; + + *need_send = bsc_prepare_protocol_error( + c, dm, NULL, &c->vmac, class, code, + (uint8_t *)s_error_no_origin); + valid = false; + } else if ( + dm->hdr.dest != NULL && + !bvlc_sc_is_vmac_broadcast(dm->hdr.dest)) { + class = ERROR_CLASS_COMMUNICATION; + code = ERROR_CODE_HEADER_ENCODING_ERROR; + *need_send = bsc_prepare_protocol_error( + c, dm, NULL, &c->vmac, class, code, + (uint8_t *)s_error_dest_presented); + valid = false; + } + } else if ( + c->ctx->cfg->type == BSC_SOCKET_CTX_ACCEPTOR && + c->ctx->cfg->proto == BSC_WEBSOCKET_HUB_PROTOCOL) { + /* this is a case when socket is hub function receiving */ + /* from node */ + if (dm->hdr.dest == NULL) { + class = ERROR_CLASS_COMMUNICATION; + code = ERROR_CODE_HEADER_ENCODING_ERROR; + + *need_send = bsc_prepare_protocol_error( + c, dm, NULL, NULL, class, code, + (uint8_t *)s_error_no_dest); + valid = false; + } else if (dm->hdr.origin != NULL) { + class = ERROR_CLASS_COMMUNICATION; + code = ERROR_CODE_HEADER_ENCODING_ERROR; + *need_send = bsc_prepare_protocol_error( + c, dm, NULL, NULL, class, code, + (uint8_t *)s_error_origin_presented); + valid = false; + } + } + } + + /* every valid message restarts heartbeat timeout */ + /* and only valid messages are processed */ + if (valid) { + if (c->ctx->cfg->type == BSC_SOCKET_CTX_ACCEPTOR) { + mstimer_set( + &c->heartbeat, + 2 * c->ctx->cfg->heartbeat_timeout_s * 1000); + } else { + mstimer_set( + &c->heartbeat, c->ctx->cfg->heartbeat_timeout_s * 1000); + } + if (c->state == BSC_SOCK_STATE_CONNECTED) { + bsc_process_socket_connected_state( + c, dm, rx_buf, rx_buf_size, need_disconnect, need_send); + } else if (c->state == BSC_SOCK_STATE_DISCONNECTING) { + bsc_process_socket_disconnecting( + c, dm, rx_buf, rx_buf_size, need_disconnect); + } + } + } + } + + expired = mstimer_expired(&c->t); + DEBUG_PRINTF("bsc_process_socket_state() expired = %d\n", expired); + + if (c->state == BSC_SOCK_STATE_AWAITING_ACCEPT && expired) { + c->state = BSC_SOCK_STATE_ERROR; + c->reason = ERROR_CODE_TIMEOUT; + *need_disconnect = true; + } else if (c->state == BSC_SOCK_STATE_AWAITING_REQUEST && expired) { + c->state = BSC_SOCK_STATE_ERROR; + c->reason = ERROR_CODE_TIMEOUT; + *need_disconnect = true; + } else if (c->state == BSC_SOCK_STATE_DISCONNECTING && expired) { + c->state = BSC_SOCK_STATE_ERROR; + c->reason = ERROR_CODE_TIMEOUT; + *need_disconnect = true; + } else if (c->state == BSC_SOCK_STATE_CONNECTED) { + expired = mstimer_expired(&c->heartbeat); + DEBUG_PRINTF( + "bsc_process_socket_state() heartbeat timeout expired = %d\n", + expired); + if (expired) { + DEBUG_PRINTF( + "bsc_process_socket_state() heartbeat timeout elapsed " + "for socket %p\n", + c); + if (c->ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + DEBUG_PRINTF( + "bsc_process_socket_state() going to send heartbeat " + "request on connection %p\n", + c); + c->expected_heartbeat_message_id = bsc_get_next_message_id(); + DEBUG_PRINTF( + "bsc_process_socket_state() heartbeat message_id %04x\n", + c->expected_heartbeat_message_id); + + len = bvlc_sc_encode_heartbeat_request( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), + c->expected_heartbeat_message_id); + + if (len) { + TX_BUF_UPDATE(c, len); + mstimer_set( + &c->heartbeat, c->ctx->cfg->heartbeat_timeout_s * 1000); + *need_send = true; + } else { + DEBUG_PRINTF( + "bsc_process_socket_state() sending of " + "heartbeat request failed on connection %p\n", + c); + } + } else if (c->ctx->cfg->type == BSC_SOCKET_CTX_ACCEPTOR) { + DEBUG_PRINTF( + "bsc_process_socket_state() zombie socket %p is " + "disconnecting...\n", + c); + c->state = BSC_SOCK_STATE_ERROR; + c->reason = ERROR_CODE_TIMEOUT; + *need_disconnect = true; + } + } + } + DEBUG_PRINTF("bsc_process_socket_state() <<<\n"); +} + +/** + * @brief Run the socket loop + * @param s - pointer to the socket + * @param dm - pointer to the decoded message + * @param rx_buf - pointer to the buffer + * @param rx_buf_size - buffer size + */ +static void bsc_runloop_socket( + BSC_SOCKET *s, + BVLC_SC_DECODED_MESSAGE *dm, + uint8_t *rx_buf, + size_t rx_buf_size) +{ + bool need_disconnect = false; + bool need_send = false; + + if (s->state != BSC_SOCK_STATE_IDLE) { + bsc_process_socket_state( + s, dm, rx_buf, rx_buf_size, &need_disconnect, &need_send); + if (need_disconnect) { + if (s->ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + bws_cli_disconnect(s->wh); + } else { + bws_srv_disconnect(s->ctx->sh, s->wh); + } + } + if (need_send) { + if (s->ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + bws_cli_send(s->wh); + } else { + bws_srv_send(s->ctx->sh, s->wh); + } + } + } +} + +/** + * @brief Run the socket maintenance timer process + * @param seconds - time in seconds + */ +void bsc_socket_maintenance_timer(uint16_t seconds) +{ + int i, j; + (void)seconds; + DEBUG_PRINTF("bsc_socket_maintenance_timer() >>>\n"); + bws_dispatch_lock(); + + for (i = 0; i < BSC_SOCKET_CTX_NUM; i++) { + if (bsc_socket_ctx[i] != NULL) { + if (bsc_socket_ctx[i]->state == BSC_CTX_STATE_INITIALIZED) { + for (j = 0; j < bsc_socket_ctx[i]->sock_num; j++) { + bsc_runloop_socket( + &bsc_socket_ctx[i]->sock[j], NULL, NULL, 0); + } + } + } + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_socket_maintenance_timer() <<<\n"); +} + +/** + * @brief Process the server awaiting request state + * @param c - pointer to the socket + * @param dm - pointer to the decoded message + * @param buf - pointer to the buffer + * @param bufsize - buffer size + */ +static void bsc_process_srv_awaiting_request( + BSC_SOCKET *c, BVLC_SC_DECODED_MESSAGE *dm, uint8_t *buf, size_t bufsize) +{ + BACNET_ERROR_CODE code; + BACNET_ERROR_CLASS class; + BSC_SOCKET *existing = NULL; + uint16_t message_id; + size_t len; + uint16_t ucode; + uint16_t uclass; + const char *err_desc = NULL; + + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() >>> c = %p, dm = %p, buf = %p, " + "bufsize = %d\n", + c, dm, buf, bufsize); + + if (!bvlc_sc_decode_message(buf, bufsize, dm, &code, &class, &err_desc)) { + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() decoding of received message " + "failed, error code = %d, class = %d\n", + code, class); + if (c->ctx->funcs->failed_request) { + c->ctx->funcs->failed_request( + c->ctx, c, NULL, NULL, code, err_desc); + } + } else if (dm->hdr.bvlc_function == BVLC_SC_CONNECT_REQUEST) { + existing = c->ctx->funcs->find_connection_for_uuid( + dm->payload.connect_request.uuid, c->ctx->user_arg); + + if (existing) { + /* Regarding AB.6.2.3 BACnet/SC Connection Accepting Peer */ + /* State Machine: On receipt of a Connect-Request message */ + /* from the initiating peer whose 'Device UUID' is equal to the */ + /* initiating peer device UUID of an existing connection, */ + /* then return a Connect-Accept message, disconnect and */ + /* close the existing connection to the connection peer node */ + /* with matching Device UUID, and enter the CONNECTED state. */ + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() accepting connection from " + "known uuid %s\n and vmac %s\n", + bsc_uuid_to_string(dm->payload.connect_request.uuid), + bsc_vmac_to_string(dm->payload.connect_request.vmac)); + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() existing = %p, " + "existing->state = %d, c = %p\n", + existing, existing->state, c); + bsc_copy_vmac(&c->vmac, dm->payload.connect_request.vmac); + bsc_copy_uuid(&c->uuid, dm->payload.connect_request.uuid); + c->max_npdu_len = dm->payload.connect_request.max_npdu_len; + c->max_bvlc_len = dm->payload.connect_request.max_bvlc_len; + message_id = dm->hdr.message_id; + + len = bvlc_sc_encode_connect_accept( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), message_id, + &c->ctx->cfg->local_vmac, &c->ctx->cfg->local_uuid, + c->ctx->cfg->max_bvlc_len, c->ctx->cfg->max_ndpu_len); + + if (!len) { + if (c->ctx->funcs->failed_request) { + c->ctx->funcs->failed_request( + c->ctx, c, dm->payload.connect_request.vmac, + dm->payload.connect_request.uuid, + ERROR_CODE_ABORT_OUT_OF_RESOURCES, NULL); + } + bsc_srv_process_error(c, ERROR_CODE_ABORT_OUT_OF_RESOURCES); + DEBUG_PRINTF("bsc_process_srv_awaiting_request() <<<\n"); + return; + } else { + TX_BUF_UPDATE(c, len); + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() request to " + "send connect accept to socket %d(%p)\n", + c->wh, c); + bws_srv_send(c->ctx->sh, c->wh); + } + + existing->expected_disconnect_message_id = + bsc_get_next_message_id(); + + len = bvlc_sc_encode_disconnect_request( + TX_BUF_PTR(existing), TX_BUF_BYTES_AVAIL(existing), + existing->expected_disconnect_message_id); + + if (len) { + TX_BUF_UPDATE(existing, len); + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() request to " + "send disconnect request to socket %d(%p)\n", + existing->wh, existing); + bws_srv_send(existing->ctx->sh, existing->wh); + } else { + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() sending of disconnect " + "request failed, err = BSC_SC_NO_RESOURCES\n"); + } + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() set socket %p to " + "connected state\n", + c); + + mstimer_set( + &c->heartbeat, 2 * c->ctx->cfg->heartbeat_timeout_s * 1000); + c->state = BSC_SOCK_STATE_CONNECTED; + c->ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_CONNECTED, 0, NULL, NULL, 0, NULL); + DEBUG_PRINTF("bsc_process_srv_awaiting_request() <<<\n"); + return; + } + + existing = c->ctx->funcs->find_connection_for_vmac( + dm->payload.connect_request.vmac, c->ctx->user_arg); + + if (existing) { + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() rejected connection for " + "duplicated vmac %s from uuid %s," + " vmac is used by uuid %s\n", + bsc_vmac_to_string(dm->payload.connect_request.vmac), + bsc_uuid_to_string(dm->payload.connect_request.uuid), + bsc_uuid_to_string(&existing->uuid)); + + uclass = ERROR_CLASS_COMMUNICATION; + ucode = ERROR_CODE_NODE_DUPLICATE_VMAC; + message_id = dm->hdr.message_id; + if (c->ctx->funcs->failed_request) { + c->ctx->funcs->failed_request( + c->ctx, c, dm->payload.connect_request.vmac, + dm->payload.connect_request.uuid, + ERROR_CODE_NODE_DUPLICATE_VMAC, NULL); + } + len = bvlc_sc_encode_result( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), message_id, NULL, NULL, + BVLC_SC_CONNECT_REQUEST, 1, NULL, &uclass, &ucode, NULL); + + if (len) { + TX_BUF_UPDATE(c, len); + c->state = BSC_SOCK_STATE_ERROR_FLUSH_TX; + c->reason = ERROR_CODE_NODE_DUPLICATE_VMAC; + bws_srv_send(c->ctx->sh, c->wh); + } else { + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() sending of nack result " + "message failed, err = BSC_SC_NO_RESOURCES\n"); + bsc_srv_process_error(c, ERROR_CODE_NODE_DUPLICATE_VMAC); + } + DEBUG_PRINTF("bsc_process_srv_awaiting_request() <<<\n"); + return; + } + + bsc_copy_vmac(&c->vmac, dm->payload.connect_request.vmac); + bsc_copy_uuid(&c->uuid, dm->payload.connect_request.uuid); + + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() local vmac = %s, " + "local uuid = %s\n", + bsc_vmac_to_string(&c->ctx->cfg->local_vmac), + bsc_uuid_to_string(&c->ctx->cfg->local_uuid)); + + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() remote vmac = %s, " + "remote uuid = %s\n", + bsc_vmac_to_string(&c->vmac), bsc_uuid_to_string(&c->uuid)); + + if (memcmp( + &c->vmac.address, &c->ctx->cfg->local_vmac.address, + sizeof(c->ctx->cfg->local_vmac.address)) == 0 && + memcmp( + &c->uuid.uuid, &c->ctx->cfg->local_uuid.uuid, + sizeof(c->ctx->cfg->local_uuid.uuid)) != 0) { + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() rejected " + "connection of a duplicate " + "of this port's vmac %s from uuid %s\n", + bsc_vmac_to_string(&c->vmac), bsc_uuid_to_string(&c->uuid)); + uclass = ERROR_CLASS_COMMUNICATION; + ucode = ERROR_CODE_NODE_DUPLICATE_VMAC; + message_id = dm->hdr.message_id; + if (c->ctx->funcs->failed_request) { + c->ctx->funcs->failed_request( + c->ctx, c, &c->vmac, &c->uuid, + ERROR_CODE_NODE_DUPLICATE_VMAC, NULL); + } + len = bvlc_sc_encode_result( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), message_id, NULL, NULL, + BVLC_SC_CONNECT_REQUEST, 1, NULL, &uclass, &ucode, NULL); + + if (len) { + TX_BUF_UPDATE(c, len); + c->state = BSC_SOCK_STATE_ERROR_FLUSH_TX; + c->reason = ERROR_CODE_NODE_DUPLICATE_VMAC; + bws_srv_send(c->ctx->sh, c->wh); + } else { + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() sending of nack result " + "message failed, err = BSC_SC_NO_RESOURCES\n"); + bsc_srv_process_error(c, ERROR_CODE_NODE_DUPLICATE_VMAC); + } + DEBUG_PRINTF("bsc_process_srv_awaiting_request() <<<\n"); + return; + } + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() accepted connection from new " + "uuid %s with vmac %s\n", + bsc_uuid_to_string(&c->uuid), bsc_vmac_to_string(&c->vmac)); + + message_id = dm->hdr.message_id; + + len = bvlc_sc_encode_connect_accept( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), message_id, + &c->ctx->cfg->local_vmac, &c->ctx->cfg->local_uuid, + c->ctx->cfg->max_bvlc_len, c->ctx->cfg->max_ndpu_len); + + if (len) { + TX_BUF_UPDATE(c, len); + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() set socket %p to " + "connected state\n", + c); + mstimer_set( + &c->heartbeat, 2 * c->ctx->cfg->heartbeat_timeout_s * 1000); + c->state = BSC_SOCK_STATE_CONNECTED; + c->ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_CONNECTED, 0, NULL, NULL, 0, NULL); + bws_srv_send(c->ctx->sh, c->wh); + } else { + DEBUG_PRINTF("bsc_process_srv_awaiting_request() sending of " + "connect accept failed, err = BSC_SC_NO_RESOURCES\n"); + if (c->ctx->funcs->failed_request) { + c->ctx->funcs->failed_request( + c->ctx, c, &c->vmac, &c->uuid, + ERROR_CODE_ABORT_OUT_OF_RESOURCES, NULL); + } + bsc_srv_process_error(c, ERROR_CODE_ABORT_OUT_OF_RESOURCES); + } + } +#if DEBUG_BSC_SOCKET == 1 + else { + DEBUG_PRINTF( + "bsc_process_srv_awaiting_request() unexpected message " + "with bvlc function " + "%d is discarded in awaiting request state\n", + dm->hdr.bvlc_function); + } +#endif + DEBUG_PRINTF("bsc_process_srv_awaiting_request() <<<\n"); +} + +/** + * @brief Dispatch the server function + * @param sh - pointer to the server handle + * @param h - handle + * @param ev - event + * @param ws_reason - reason + * @param ws_reason_desc - reason description + * @param buf - pointer to the buffer + * @param bufsize - buffer size + * @param dispatch_func_user_param - pointer to the user parameter + */ +static void bsc_dispatch_srv_func( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + BSC_WEBSOCKET_EVENT ev, + BACNET_ERROR_CODE ws_reason, + char *ws_reason_desc, + uint8_t *buf, + size_t bufsize, + void *dispatch_func_user_param) +{ + BSC_SOCKET_CTX *ctx = (BSC_SOCKET_CTX *)dispatch_func_user_param; + BSC_SOCKET *c = NULL; + BSC_WEBSOCKET_RET wret; + uint8_t *p; + bool failed = false; + uint16_t len; + size_t i; + + (void)sh; + bws_dispatch_lock(); + DEBUG_PRINTF( + "bsc_dispatch_srv_func() >>> sh = %p, h = %d, ev = %d, " + "reason = %d, desc = %p, buf " + "= %p, bufsize = %ld, ctx = %p\n", + sh, h, ev, ws_reason, ws_reason_desc, buf, bufsize, ctx); + + if (ev == BSC_WEBSOCKET_SERVER_STOPPED) { + for (i = 0; i < ctx->sock_num; i++) { + ctx->sock[i].state = BSC_SOCK_STATE_IDLE; + } + DEBUG_PRINTF("bsc_dispatch_srv_func() ctx %p is deinitialized\n", ctx); + bsc_ctx_remove(ctx); + ctx->state = BSC_CTX_STATE_IDLE; + ctx->funcs->context_event(ctx, BSC_CTX_DEINITIALIZED); + bsc_socket_maintenance_timer(0); + DEBUG_PRINTF("bsc_dispatch_srv_func() <<<\n"); + bws_dispatch_unlock(); + return; + } else if (ev == BSC_WEBSOCKET_SERVER_STARTED) { + ctx->state = BSC_CTX_STATE_INITIALIZED; + DEBUG_PRINTF("bsc_dispatch_srv_func() ctx %p is initialized\n", ctx); + ctx->funcs->context_event(ctx, BSC_CTX_INITIALIZED); + bsc_socket_maintenance_timer(0); + DEBUG_PRINTF("bsc_dispatch_srv_func() <<<\n"); + bws_dispatch_unlock(); + return; + } + + if (ev != BSC_WEBSOCKET_CONNECTED) { + c = bsc_find_conn_by_websocket(ctx, h); + if (!c) { + DEBUG_PRINTF( + "bsc_dispatch_srv_func() can not find socket " + "descriptor for websocket %d\n", + h); + DEBUG_PRINTF("bsc_dispatch_srv_func() <<<\n"); + bws_dispatch_unlock(); + return; + } + DEBUG_PRINTF( + "bsc_dispatch_srv_func() socket %p, state = %d\n", c, c->state); + } + + if (ev == BSC_WEBSOCKET_DISCONNECTED) { + if (c->state == BSC_SOCK_STATE_ERROR) { + bsc_set_socket_idle(c); + ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_DISCONNECTED, c->reason, NULL, NULL, 0, + NULL); + bsc_clear_vmac_and_uuid(c); + } else { + bsc_set_socket_idle(c); + ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_DISCONNECTED, ws_reason, ws_reason_desc, + NULL, 0, NULL); + bsc_clear_vmac_and_uuid(c); + } + } else if (ev == BSC_WEBSOCKET_CONNECTED) { + c = bsc_find_free_socket(ctx); + if (!c) { + DEBUG_PRINTF("bsc_dispatch_srv_func() no free socket, connection " + "is dropped\n"); + bws_srv_disconnect(ctx->sh, h); + } else { + bsc_reset_socket(c); + c->wh = h; + c->ctx = ctx; + c->state = BSC_SOCK_STATE_AWAITING_REQUEST; + mstimer_set(&c->t, c->ctx->cfg->connect_timeout_s * 1000); + } + } else if (ev == BSC_WEBSOCKET_RECEIVED) { + DEBUG_PRINTF("bsc_dispatch_srv_func() processing " + "BSC_WEBSOCKET_RECEIVED event\n"); + DEBUG_PRINTF( + "bsc_dispatch_srv_func() socket %p, state = %d\n", c, c->state); + if (c->state == BSC_SOCK_STATE_AWAITING_REQUEST) { + bsc_process_srv_awaiting_request(c, &bsc_dm, buf, bufsize); + } else if ( + c->state == BSC_SOCK_STATE_DISCONNECTING || + c->state == BSC_SOCK_STATE_CONNECTED) { + bsc_runloop_socket(c, &bsc_dm, buf, bufsize); + } +#if DEBUG_BSC_SOCKET == 1 + else { + DEBUG_PRINTF( + "bsc_dispatch_srv_func() data was dropped for socket " + "%p, state %d, data_size %d\n", + c, c->state, bufsize); + } +#endif + } else if (ev == BSC_WEBSOCKET_SENDABLE) { + p = c->tx_buf; + + while (c->tx_buf_size > 0) { + memcpy(&len, p, sizeof(len)); + wret = bws_srv_dispatch_send( + c->ctx->sh, c->wh, &p[sizeof(len) + BSC_CONF_TX_PRE], len); + if (wret != BSC_WEBSOCKET_SUCCESS) { + DEBUG_PRINTF( + "bsc_dispatch_srv_func() send data failed, start " + "disconnect operation on socket %p\n", + c); + bsc_srv_process_error( + c, + c->state != BSC_SOCK_STATE_ERROR_FLUSH_TX + ? ERROR_CODE_ABORT_OUT_OF_RESOURCES + : c->reason); + failed = true; + break; + } else { + c->tx_buf_size -= len + sizeof(len) + BSC_CONF_TX_PRE; + p += len + sizeof(len) + BSC_CONF_TX_PRE; + } + } + + if (!failed) { + if (c->state == BSC_SOCK_STATE_ERROR_FLUSH_TX) { + bsc_srv_process_error(c, c->reason); + } + } + } + + bsc_socket_maintenance_timer(0); + DEBUG_PRINTF("bsc_dispatch_srv_func() <<<\n"); + bws_dispatch_unlock(); +} + +/** + * @brief Process the client awaiting accept state + * @param c - pointer to the socket + * @param dm - pointer to the decoded message + * @param buf - pointer to the buffer + * @param bufsize - buffer size + */ +static void bsc_process_cli_awaiting_accept( + BSC_SOCKET *c, BVLC_SC_DECODED_MESSAGE *dm, uint8_t *buf, size_t bufsize) +{ + BACNET_ERROR_CODE code; + BACNET_ERROR_CLASS class; + const char *err_desc = NULL; + + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() >>> c = %p, dm = %p, buf = " + "%p, bufsize = %d\n", + c, dm, buf, bufsize); + + if (!bvlc_sc_decode_message(buf, bufsize, dm, &code, &class, &err_desc)) { + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() <<< decoding failed " + "code = %d, class = %d\n", + code, class); + return; + } + + if (dm->hdr.bvlc_function == BVLC_SC_CONNECT_ACCEPT) { + if (dm->hdr.message_id != c->expected_connect_accept_message_id) { + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() got bvlc result packet " + "with unexpected message id %04x\n", + dm->hdr.message_id); + } else { + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() set state of " + "socket %p to BSC_SOCKET_EVENT_CONNECTED\n", + c); + bsc_copy_vmac(&c->vmac, dm->payload.connect_accept.vmac); + bsc_copy_uuid(&c->uuid, dm->payload.connect_accept.uuid); + c->max_bvlc_len = dm->payload.connect_accept.max_bvlc_len; + c->max_npdu_len = dm->payload.connect_accept.max_npdu_len; + mstimer_set(&c->heartbeat, c->ctx->cfg->heartbeat_timeout_s * 1000); + c->state = BSC_SOCK_STATE_CONNECTED; + c->ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_CONNECTED, 0, NULL, NULL, 0, NULL); + } + } else if (dm->hdr.bvlc_function == BVLC_SC_RESULT) { + if (dm->payload.result.bvlc_function != BVLC_SC_CONNECT_REQUEST) { + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() got unexpected " + "bvlc function %d in " + "BVLC-Result message in awaiting accept state\n", + dm->payload.result.bvlc_function); + } else if ( + dm->hdr.message_id != c->expected_connect_accept_message_id) { + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() got bvlc result packet " + "with unexpected message id %04x\n", + dm->hdr.message_id); + } else if ( + dm->payload.result.error_code == ERROR_CODE_NODE_DUPLICATE_VMAC) { + /* According AB.6.2.2 BACnet/SC Connection Initiating */ + /* Peer State Machine: on receipt of a BVLC-Result NAK */ + /* message with an 'Error Code' of NODE_DUPLICATE_VMAC, */ + /* the initiating peer's node shall choose a new */ + /* Random-48 VMAC, close the WebSocket connection, and */ + /* enter the IDLE state. */ + /* Signal upper layer about that error */ + bsc_cli_process_error(c, ERROR_CODE_NODE_DUPLICATE_VMAC); + } +#if DEBUG_BSC_SOCKET == 1 + else { + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() got unexpected " + "BVLC_RESULT error code " + "%d in BVLC-Result message in awaiting accept state\n", + dm->payload.result.error_code); + } +#endif + } else if (dm->hdr.bvlc_function == BVLC_SC_DISCONNECT_REQUEST) { + /* AB.6.2.2 BACnet/SC Connection Initiating Peer State */ + /* Machine does not say anything about situation when */ + /* disconnect request is received from remote peer after */ + /* connect request. Handle this situation as an error, log */ + /* it and close connection. */ + DEBUG_PRINTF("bsc_process_cli_awaiting_accept() got unexpected " + "disconnect request\n"); + bsc_cli_process_error(c, ERROR_CODE_OTHER); + } else if (dm->hdr.bvlc_function == BVLC_SC_DISCONNECT_ACK) { + /* AB.6.2.2 BACnet/SC Connection Initiating Peer State */ + /* Machine does not say anything about situation when */ + /* disconnect ack is received from remote peer after connect */ + /* request. Handle this situation as an error, log it and */ + /* close connection. */ + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() got unexpected disconnect ack " + "request\n"); + bsc_cli_process_error(c, ERROR_CODE_OTHER); + } +#if DEBUG_BSC_SOCKET == 1 + else { + DEBUG_PRINTF( + "bsc_process_cli_awaiting_accept() unexpected message " + "with bvlc function " + "%d is discarded in awaiting accept state\n", + dm->hdr.bvlc_function); + } +#endif + DEBUG_PRINTF("bsc_process_cli_awaiting_accept() <<<\n"); +} + +/** + * @brief Dispatch the client function + * @param h - handle + * @param ev - event + * @param ws_reason - reason + * @param ws_reason_desc - reason description + * @param buf - pointer to the buffer + * @param bufsize - buffer size + * @param dispatch_func_user_param - pointer to the user parameter + */ +static void bsc_dispatch_cli_func( + BSC_WEBSOCKET_HANDLE h, + BSC_WEBSOCKET_EVENT ev, + BACNET_ERROR_CODE ws_reason, + char *ws_reason_desc, + uint8_t *buf, + size_t bufsize, + void *dispatch_func_user_param) +{ + BSC_SOCKET_CTX *ctx = (BSC_SOCKET_CTX *)dispatch_func_user_param; + BSC_SOCKET *c; + size_t len; + uint16_t pdu_len; + BSC_WEBSOCKET_RET wret; + uint8_t *p; + size_t i; + bool all_socket_disconnected = true; + bool failed = false; + + bws_dispatch_lock(); + + DEBUG_PRINTF( + "bsc_dispatch_cli_func() >>> h = %d, ev = %d, reason = %d, " + "reason_desc = %p, buf = %p, " + "bufsize = %ld, ctx = %p\n", + h, ev, ws_reason, ws_reason_desc, buf, bufsize, ctx); + + c = bsc_find_conn_by_websocket(ctx, h); + + if (!c) { + DEBUG_PRINTF( + "bsc_dispatch_cli_func() <<< warning, can not find " + "connection object for websocket %d\n", + h); + bws_dispatch_unlock(); + return; + } + + DEBUG_PRINTF("bsc_dispatch_cli_func() ev = %d, state = %d\n", ev, c->state); + + if (ev == BSC_WEBSOCKET_DISCONNECTED) { + DEBUG_PRINTF("bsc_dispatch_cli_func() ctx->state = %d\n", ctx->state); + if (ctx->state == BSC_CTX_STATE_DEINITIALIZING) { + bsc_set_socket_idle(c); + bsc_clear_vmac_and_uuid(c); + for (i = 0; i < ctx->sock_num; i++) { + if (ctx->sock[i].state != BSC_SOCK_STATE_IDLE) { + all_socket_disconnected = false; + break; + } + } + if (all_socket_disconnected) { + ctx->state = BSC_CTX_STATE_IDLE; + bsc_ctx_remove(ctx); + ctx->funcs->context_event(ctx, BSC_CTX_DEINITIALIZED); + } + } else if (c->state == BSC_SOCK_STATE_ERROR) { + bsc_set_socket_idle(c); + ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_DISCONNECTED, c->reason, NULL, NULL, 0, + NULL); + bsc_clear_vmac_and_uuid(c); + } else { + bsc_set_socket_idle(c); + ctx->funcs->socket_event( + c, BSC_SOCKET_EVENT_DISCONNECTED, ws_reason, ws_reason_desc, + NULL, 0, NULL); + bsc_clear_vmac_and_uuid(c); + } + } else if (ev == BSC_WEBSOCKET_CONNECTED) { + if (c->state == BSC_SOCK_STATE_AWAITING_WEBSOCKET) { + DEBUG_PRINTF( + "bsc_dispatch_cli_func() conn %p, websocket %d, state " + "changed to BSC_SOCK_STATE_AWAITING_ACCEPT\n", + c, h); + c->state = BSC_SOCK_STATE_AWAITING_ACCEPT; + mstimer_set(&c->t, c->ctx->cfg->connect_timeout_s * 1000); + c->expected_connect_accept_message_id = bsc_get_next_message_id(); + DEBUG_PRINTF( + "bsc_dispatch_cli_func() expected connect accept " + "message id = %04x\n", + c->expected_connect_accept_message_id); + + DEBUG_PRINTF( + "bsc_dispatch_cli_func() going to send connect request " + "with uuid %s and vmac %s\n", + bsc_uuid_to_string(&ctx->cfg->local_uuid), + bsc_vmac_to_string(&ctx->cfg->local_vmac)); + + len = bvlc_sc_encode_connect_request( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), + c->expected_connect_accept_message_id, &ctx->cfg->local_vmac, + &ctx->cfg->local_uuid, ctx->cfg->max_bvlc_len, + ctx->cfg->max_ndpu_len); + + if (!len) { + bsc_cli_process_error(c, ERROR_CODE_ABORT_OUT_OF_RESOURCES); + } else { + TX_BUF_UPDATE(c, len); + } + bws_cli_send(c->wh); + } + } else if (ev == BSC_WEBSOCKET_SENDABLE) { + p = c->tx_buf; + + while (c->tx_buf_size > 0) { + memcpy(&pdu_len, p, sizeof(pdu_len)); + DEBUG_PRINTF( + "bsc_dispatch_cli_func() sending pdu of %d bytes\n", pdu_len); + wret = bws_cli_dispatch_send( + c->wh, &p[sizeof(pdu_len) + BSC_CONF_TX_PRE], pdu_len); + if (wret != BSC_WEBSOCKET_SUCCESS) { + DEBUG_PRINTF( + "bsc_dispatch_cli_func() pdu send failed, err = %d, start " + "disconnect operation on socket %p\n", + wret, c); + bsc_cli_process_error( + c, + c->state != BSC_SOCK_STATE_ERROR_FLUSH_TX + ? ERROR_CODE_ABORT_OUT_OF_RESOURCES + : c->reason); + failed = true; + break; + } else { + c->tx_buf_size -= pdu_len + sizeof(pdu_len) + BSC_CONF_TX_PRE; + p += pdu_len + sizeof(pdu_len) + BSC_CONF_TX_PRE; + } + } + if (!failed) { + if (c->state == BSC_SOCK_STATE_ERROR_FLUSH_TX) { + bsc_cli_process_error(c, c->reason); + } + } + } else if (ev == BSC_WEBSOCKET_RECEIVED) { + if (c->state == BSC_SOCK_STATE_AWAITING_ACCEPT) { + bsc_process_cli_awaiting_accept(c, &bsc_dm, buf, bufsize); + } else if ( + c->state == BSC_SOCK_STATE_DISCONNECTING || + c->state == BSC_SOCK_STATE_CONNECTED) { + bsc_runloop_socket(c, &bsc_dm, buf, bufsize); + } +#if DEBUG_BSC_SOCKET == 1 + else { + DEBUG_PRINTF( + "bsc_dispatch_cli_func() data was dropped for socket " + "%p, state %d, data_size %d\n", + c, c->state, bufsize); + } +#endif + } + + bsc_socket_maintenance_timer(0); + DEBUG_PRINTF("bsc_dispatch_cli_func() <<<\n"); + bws_dispatch_unlock(); +} + +/** + * @brief Initialize the context + * @param ctx - pointer to the context + * @param cfg - pointer to the configuration + * @param funcs - pointer to the functions + * @param sockets - pointer to the sockets + * @param sockets_num - number of sockets + * @param user_arg - pointer to the user argument + * @return BSC_SC_RET - status + */ +BSC_SC_RET bsc_init_ctx( + BSC_SOCKET_CTX *ctx, + BSC_CONTEXT_CFG *cfg, + BSC_SOCKET_CTX_FUNCS *funcs, + BSC_SOCKET *sockets, + size_t sockets_num, + void *user_arg) +{ + BSC_WEBSOCKET_RET ret; + BSC_SC_RET sc_ret = BSC_SC_SUCCESS; + size_t i; + + DEBUG_PRINTF( + "bsc_init_сtx() >>> ctx = %p, cfg = %p, funcs = %p, user_arg = %p\n", + ctx, cfg, funcs, user_arg); + + if (!ctx || !cfg || !funcs || !funcs->socket_event || + !funcs->context_event || !sockets || !sockets_num) { + DEBUG_PRINTF("bsc_init_ctx() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + + if (cfg->type == BSC_SOCKET_CTX_ACCEPTOR) { + if (!funcs->find_connection_for_vmac || + !funcs->find_connection_for_uuid) { + DEBUG_PRINTF("bsc_init_ctx() <<< ret = BSC_SC_BAD_PARAM\n"); + return BSC_SC_BAD_PARAM; + } + } + + bws_dispatch_lock(); + if (ctx->state != BSC_CTX_STATE_IDLE) { + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_init_ctx() <<< ret = BSC_SC_INVALID_OPERATION\n"); + return BSC_SC_INVALID_OPERATION; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->user_arg = user_arg; + ctx->cfg = cfg; + ctx->funcs = funcs; + ctx->sock = sockets; + ctx->sock_num = sockets_num; + + for (i = 0; i < sockets_num; i++) { + bsc_set_socket_idle(&ctx->sock[i]); + } + + ctx->state = BSC_CTX_STATE_INITIALIZING; + if (!bsc_ctx_add(ctx)) { + sc_ret = BSC_SC_NO_RESOURCES; + } else { + if (cfg->type == BSC_SOCKET_CTX_ACCEPTOR) { + ret = bws_srv_start( + cfg->proto, cfg->port, cfg->iface, cfg->ca_cert_chain, + cfg->ca_cert_chain_size, cfg->cert_chain, cfg->cert_chain_size, + cfg->priv_key, cfg->priv_key_size, cfg->connect_timeout_s, + bsc_dispatch_srv_func, ctx, &ctx->sh); + + sc_ret = bsc_map_websocket_retcode(ret); + + if (sc_ret != BSC_SC_SUCCESS) { + bsc_ctx_remove(ctx); + } + } else { + ctx->state = BSC_CTX_STATE_INITIALIZED; + ctx->funcs->context_event(ctx, BSC_CTX_INITIALIZED); + } + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_init_ctx() <<< ret = %d \n", sc_ret); + return sc_ret; +} + +/** + * @brief Deinitialize the context + * @param ctx - pointer to the context + */ +void bsc_deinit_ctx(BSC_SOCKET_CTX *ctx) +{ + size_t i; + bool active_socket = false; + DEBUG_PRINTF("bsc_deinit_ctx() >>> ctx = %p\n", ctx); + + bws_dispatch_lock(); + + if (!ctx || ctx->state == BSC_CTX_STATE_IDLE || + ctx->state == BSC_CTX_STATE_DEINITIALIZING) { + DEBUG_PRINTF("bsc_deinit_ctx() no action required\n"); + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_deinit_ctx() <<<\n"); + return; + } + + if (ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + ctx->state = BSC_CTX_STATE_DEINITIALIZING; + for (i = 0; i < ctx->sock_num; i++) { + if (ctx->sock[i].state != BSC_SOCK_STATE_IDLE) { + active_socket = true; + DEBUG_PRINTF( + "bsc_deinit_ctx() disconnect socket %ld(%p) with wh = %d\n", + i, &ctx->sock[i], ctx->sock[i].wh); + bws_cli_disconnect(ctx->sock[i].wh); + } + } + if (!active_socket) { + DEBUG_PRINTF( + "bsc_deinit_ctx() no active sockets, ctx de-initialized\n"); + ctx->state = BSC_CTX_STATE_IDLE; + bsc_ctx_remove(ctx); + ctx->funcs->context_event(ctx, BSC_CTX_DEINITIALIZED); + } + } else { + ctx->state = BSC_CTX_STATE_DEINITIALIZING; + (void)bws_srv_stop(ctx->sh); + } + + bws_dispatch_unlock(); + DEBUG_PRINTF("bsc_deinit_ctx() <<<\n"); +} + +/** + * @brief Connect the socket + * @param ctx - pointer to the context + * @param c - pointer to the socket + * @param url - pointer to the URL + * @return BSC_SC_RET - status + */ +BSC_SC_RET bsc_connect(BSC_SOCKET_CTX *ctx, BSC_SOCKET *c, char *url) +{ + BSC_SC_RET ret = BSC_SC_INVALID_OPERATION; + BSC_WEBSOCKET_RET wret; + + DEBUG_PRINTF("bsc_connect() >>> ctx = %p, c = %p, url = %s\n", ctx, c, url); + + if (!ctx || !c || !url) { + ret = BSC_SC_BAD_PARAM; + } else { + bws_dispatch_lock(); + + if (ctx->state == BSC_CTX_STATE_INITIALIZED && + ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + c->ctx = ctx; + c->state = BSC_SOCK_STATE_AWAITING_WEBSOCKET; + c->tx_buf_size = 0; + + wret = bws_cli_connect( + ctx->cfg->proto, url, ctx->cfg->ca_cert_chain, + ctx->cfg->ca_cert_chain_size, ctx->cfg->cert_chain, + ctx->cfg->cert_chain_size, ctx->cfg->priv_key, + ctx->cfg->priv_key_size, ctx->cfg->connect_timeout_s, + bsc_dispatch_cli_func, ctx, &c->wh); + + ret = bsc_map_websocket_retcode(wret); + if (wret != BSC_WEBSOCKET_SUCCESS) { + bsc_set_socket_idle(c); + bsc_clear_vmac_and_uuid(c); + } + } + + bws_dispatch_unlock(); + } + + DEBUG_PRINTF("bsc_connect() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Disconnect the socket + * @param c - pointer to the socket + */ +void bsc_disconnect(BSC_SOCKET *c) +{ + size_t len; + + DEBUG_PRINTF("bsc_disconnect() >>> c = %p\n", c); + + bws_dispatch_lock(); + + if (c->ctx->state == BSC_CTX_STATE_INITIALIZED) { + if (c->state == BSC_SOCK_STATE_CONNECTED) { + c->expected_disconnect_message_id = bsc_get_next_message_id(); + c->state = BSC_SOCK_STATE_DISCONNECTING; + mstimer_set(&c->t, c->ctx->cfg->disconnect_timeout_s * 1000); + len = bvlc_sc_encode_disconnect_request( + TX_BUF_PTR(c), TX_BUF_BYTES_AVAIL(c), + c->expected_disconnect_message_id); + if (!len) { + DEBUG_PRINTF( + "bsc_disconnect() disconnect request not sent, err = " + "BSC_SC_NO_RESOURCES\n"); + if (c->ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + bsc_cli_process_error(c, ERROR_CODE_ABORT_OUT_OF_RESOURCES); + } else { + bsc_srv_process_error(c, ERROR_CODE_ABORT_OUT_OF_RESOURCES); + } + } else { + TX_BUF_UPDATE(c, len); + if (c->ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + bws_cli_send(c->wh); + } else { + bws_srv_send(c->ctx->sh, c->wh); + } + } + } else if (c->ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + if (c->state != BSC_SOCK_STATE_IDLE) { + bws_cli_disconnect(c->wh); + } + } else if (c->ctx->cfg->type == BSC_SOCKET_CTX_ACCEPTOR) { + if (c->state != BSC_SOCK_STATE_IDLE) { + bws_srv_disconnect(c->ctx->sh, c->wh); + } + } + } + + bws_dispatch_unlock(); + + DEBUG_PRINTF("bsc_disconnect() <<<\n"); +} + +/** + * @brief Send the BACnet Secure Connect PDU + * @param c - pointer to the socket + * @param pdu - pointer to the PDU + * @param pdu_len - PDU length + * @return BSC_SC_RET - status + */ +BSC_SC_RET bsc_send(BSC_SOCKET *c, uint8_t *pdu, size_t pdu_len) +{ + BSC_SC_RET ret = BSC_SC_SUCCESS; + + DEBUG_PRINTF( + "bsc_send() >>> c = %p, pdu = %p, pdu_len = %d\n", c, pdu, pdu_len); + + if (!c || !pdu || !pdu_len) { + ret = BSC_SC_BAD_PARAM; + } else { + bws_dispatch_lock(); + + if (c->ctx->state != BSC_CTX_STATE_INITIALIZED || + c->state != BSC_SOCK_STATE_CONNECTED) { + ret = BSC_SC_INVALID_OPERATION; + } else { + if (TX_BUF_BYTES_AVAIL(c) < pdu_len) { + ret = BSC_SC_NO_RESOURCES; + } else { + memcpy(TX_BUF_PTR(c), pdu, pdu_len); + TX_BUF_UPDATE(c, pdu_len); + if (c->ctx->cfg->type == BSC_SOCKET_CTX_INITIATOR) { + bws_cli_send(c->wh); + } else { + bws_srv_send(c->ctx->sh, c->wh); + } + } + } + + bws_dispatch_unlock(); + } + + DEBUG_PRINTF("bsc_send() <<< ret = %d\n", ret); + return ret; +} + +/** + * @brief Get the next message ID + * @return uint16_t - message ID + */ +uint16_t bsc_get_next_message_id(void) +{ + static uint16_t message_id; + static bool initialized = false; + uint16_t ret; + + bws_dispatch_lock(); + if (!initialized) { + message_id = (uint16_t)(rand() % USHRT_MAX); + initialized = true; + } else { + message_id++; + } + ret = message_id; + DEBUG_PRINTF("next message id = %u(%04x)\n", ret, ret); + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Get the socket peer address + * @param c - pointer to the socket + * @param data - pointer to the address data + * @return true on success, false on failure + */ +bool bsc_socket_get_peer_addr(BSC_SOCKET *c, BACNET_HOST_N_PORT_DATA *data) +{ + bool ret = false; + + if (!c || !data) { + return false; + } + + bws_dispatch_lock(); + if (c->ctx->cfg->type == BSC_SOCKET_CTX_ACCEPTOR) { + data->type = BACNET_HOST_N_PORT_IP; + ret = bws_srv_get_peer_ip_addr( + c->ctx->sh, c->wh, (uint8_t *)data->host, sizeof(data->host), + &data->port); + } + bws_dispatch_unlock(); + return ret; +} + +/** + * @brief Get the socket global buffer + * @return pointer to the buffer + */ +BACNET_STACK_EXPORT +uint8_t *bsc_socket_get_global_buf(void) +{ + static uint8_t buf[BSC_PRE + BVLC_SC_NPDU_SIZE_CONF]; + return &buf[BSC_PRE]; +} + +/** + * @brief Get the socket global buffer size + * @return buffer size + */ +BACNET_STACK_EXPORT +size_t bsc_socket_get_global_buf_size(void) +{ + return BVLC_SC_NPDU_SIZE_CONF; +} diff --git a/src/bacnet/datalink/bsc/bsc-socket.h b/src/bacnet/datalink/bsc/bsc-socket.h new file mode 100644 index 00000000..c59683fc --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-socket.h @@ -0,0 +1,308 @@ +/** + * @file + * @brief BACNet secure connect socket API. + * In general, user should not use that API directly, + * BACNet/SC datalink API should be used. + * @author Kirill Neznamov + * @date December 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_SOCKET_H +#define BACNET_DATALINK_BSC_SOCKET_H +#include +#include +#include +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/npdu.h" +#include "bacnet/datalink/bsc/websocket.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/bsc-retcodes.h" +#include "bacnet/datalink/bsc/bsc-conf.h" +#include "bacnet/basic/sys/mstimer.h" +#include "bacnet/basic/object/sc_netport.h" + +#define BSC_RX_BUFFER_SIZE BSC_CONF_SOCK_RX_BUFFER_SIZE +#define BSC_TX_BUFFER_SIZE BSC_CONF_SOCK_TX_BUFFER_SIZE + +#define BSC_SOCKET_CTX_NUM \ + (BSC_CONF_NODES_NUM * \ + (BSC_CONF_HUB_CONNECTORS_NUM + 2 * BSC_CONF_NODE_SWITCHES_NUM + \ + BSC_CONF_HUB_FUNCTIONS_NUM)) + +struct BSC_Socket; +typedef struct BSC_Socket BSC_SOCKET; + +struct BSC_SocketContext; +typedef struct BSC_SocketContext BSC_SOCKET_CTX; + +struct BSC_SocketContextFuncs; +typedef struct BSC_SocketContextFuncs BSC_SOCKET_CTX_FUNCS; + +struct BSC_ContextCFG; +typedef struct BSC_ContextCFG BSC_CONTEXT_CFG; + +typedef enum { + BSC_SOCKET_CTX_INITIATOR = 1, + BSC_SOCKET_CTX_ACCEPTOR = 2 +} BSC_SOCKET_CTX_TYPE; + +typedef enum { + BSC_SOCKET_EVENT_CONNECTED = 0, + BSC_SOCKET_EVENT_DISCONNECTED = 1, + BSC_SOCKET_EVENT_RECEIVED = 2 +} BSC_SOCKET_EVENT; + +typedef enum { + BSC_CTX_INITIALIZED = 0, + BSC_CTX_DEINITIALIZED = 1 +} BSC_CTX_EVENT; + +typedef enum { + BSC_CTX_STATE_IDLE = 0, + BSC_CTX_STATE_INITIALIZING = 1, + BSC_CTX_STATE_INITIALIZED = 2, + BSC_CTX_STATE_DEINITIALIZING = 3 +} BSC_CTX_STATE; + +typedef enum { + BSC_SOCK_STATE_IDLE = 0, + BSC_SOCK_STATE_AWAITING_WEBSOCKET = 1, + BSC_SOCK_STATE_AWAITING_REQUEST = 2, + BSC_SOCK_STATE_AWAITING_ACCEPT = 3, + BSC_SOCK_STATE_CONNECTED = 4, + BSC_SOCK_STATE_DISCONNECTING = 5, + BSC_SOCK_STATE_ERROR = 6, + BSC_SOCK_STATE_ERROR_FLUSH_TX = 7 +} BSC_SOCKET_STATE; + +struct BSC_Socket { + BSC_SOCKET_CTX *ctx; + BSC_WEBSOCKET_HANDLE wh; + BSC_SOCKET_STATE state; + BACNET_ERROR_CODE reason; + struct mstimer t; + struct mstimer heartbeat; + BACNET_SC_VMAC_ADDRESS vmac; /* VMAC address of the requesting node. */ + BACNET_SC_UUID uuid; + + /* Regarding max_bvlc_len and max_npdu_len: */ + /* These are the datalink limits and are passed up the stack to let */ + /* the application layer know one of the several numbers that go into + * computing */ + /* how big an NPDU/APDU can be. */ + + uint16_t max_bvlc_len; /* remote peer max bvlc len */ + uint16_t max_npdu_len; /* remote peer max npdu len */ + + uint16_t expected_connect_accept_message_id; + uint16_t expected_disconnect_message_id; + uint16_t expected_heartbeat_message_id; + + uint8_t tx_buf[BSC_TX_BUFFER_SIZE]; + size_t tx_buf_size; +}; + +struct BSC_ContextCFG { + BSC_SOCKET_CTX_TYPE type; + BSC_WEBSOCKET_PROTOCOL proto; + uint16_t port; + char *iface; + uint8_t *ca_cert_chain; + size_t ca_cert_chain_size; + uint8_t *cert_chain; + size_t cert_chain_size; + uint8_t *priv_key; + size_t priv_key_size; + BACNET_SC_VMAC_ADDRESS local_vmac; + BACNET_SC_UUID local_uuid; + uint16_t max_bvlc_len; /* local peer max bvlc len */ + uint16_t max_ndpu_len; /* local peer max npdu len */ + + /* According AB.6.2 BACnet/SC Connection Establishment and Termination */ + /* recommended default value for establishing of connection 10 seconds */ + uint16_t connect_timeout_s; + uint16_t disconnect_timeout_s; + + /* According 12.56.Y10 SC_Heartbeat_Timeout */ + /* (http://www.bacnet.org/Addenda/Add-135-2020cc.pdf) the recommended + * default */ + /* value is 300 seconds. */ + uint16_t heartbeat_timeout_s; +}; + +struct BSC_SocketContextFuncs { + BSC_SOCKET *(*find_connection_for_vmac)( + BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg); + BSC_SOCKET *(*find_connection_for_uuid)( + BACNET_SC_UUID *uuid, void *user_arg); + /* We always reserve BSC_PRE bytes before BVLC message header */ + /* to avoid copying of packet payload during manipulation with */ + /* origin and dest addresses (e.g. adding them to received PDU) */ + /* That's why pdu pointer has always reserved BSC_PRE bytes behind */ + /* The params disconnect_reason and disconnect_reason_desc are meanfull */ + /* only for disconnect events, e.g. when ev == BSC_SOCKET_EVENT_DISCONNECTED + */ + + void (*socket_event)( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE disconnect_reason, + const char *disconnect_reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu); + + void (*context_event)(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev); + void (*failed_request)( + BSC_SOCKET_CTX *ctx, + BSC_SOCKET *c, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE error, + const char *error_desc); +}; + +struct BSC_SocketContext { + BSC_CTX_STATE state; + BSC_WEBSOCKET_SRV_HANDLE sh; + BSC_SOCKET *sock; + size_t sock_num; + BSC_SOCKET_CTX_FUNCS *funcs; + BSC_CONTEXT_CFG *cfg; + bool deinit_in_progress; + void *user_arg; +}; + +/* max_local_bvlc_len - The maximum BVLC message size int bytes that can be */ +/* received and processed by BSC/SC datalink. */ +/* max_local_ndpu_len - The maximum NPDU message size in bytes hat can be */ +/* handled by BSC/SC datalink. */ + +BACNET_STACK_EXPORT +void bsc_init_ctx_cfg( + BSC_SOCKET_CTX_TYPE type, + BSC_CONTEXT_CFG *cfg, + BSC_WEBSOCKET_PROTOCOL proto, + uint16_t port, + char *iface, + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + uint16_t max_local_bvlc_len, + uint16_t max_local_ndpu_len, + unsigned int connect_timeout_s, + unsigned int heartbeat_timeout_s, + unsigned int disconnect_timeout_s); + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_init_ctx( + BSC_SOCKET_CTX *ctx, + BSC_CONTEXT_CFG *cfg, + BSC_SOCKET_CTX_FUNCS *funcs, + BSC_SOCKET *sockets, + size_t sockets_num, + void *user_arg); + +BACNET_STACK_EXPORT +void bsc_deinit_ctx(BSC_SOCKET_CTX *ctx); + +/** + * @brief bsc_connect() function starts connect operation for a + * specified BACNet socket. The function call be called only + * for initiator context otherwise BSC_SC_INVALID_OPERATION + * error is returned. As a result if bsc_connect() was + * succeeded for given param c, that leads to emitting of + * BSC_SOCKET_EVENT_CONNECTED or BSC_SOCKET_EVENT_DISCONNECTED + * events depending on the result of connect operation. + * If connect operation is failed, BSC_SOCKET_EVENT_DISCONNECTED + * event is emitted and user can determine the reason why it + * happened using disconnect_reason and disconnect_reason_desc + * parameters in callback function. + * If connect operation succeeded, BSC_SOCKET_EVENT_CONNECTED + * event is emitted. + * + * @param ctx - socket context. + * @param c - BACNet socket descriptor . + * @param url - url to connect to. For example: wss://legrand.com:8080. + * + * @return error code from BSC_SC_RET enum. + * The following error codes can be returned: + * BSC_SC_BAD_PARAM - In a case if some input parameter is + * incorrect. + * BSC_SC_INVALID_OPERATION - if socket is not in opened state, + or disconnect operation is in progress using + bsc_disconnect() or bsc_deinit_ctx(). + * BSC_SC_SUCCESS - operation has succeeded. + * BSC_SC_NO_RESOURCES - there are not resources (memory, etc.. ) + * to send data + */ + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_connect(BSC_SOCKET_CTX *ctx, BSC_SOCKET *c, char *url); + +BACNET_STACK_EXPORT +void bsc_disconnect(BSC_SOCKET *c); + +/** + * @brief bsc_send() function schedules transmitting of pdu to + * another BACNet socket. The function may be used only + * when the socket is in a connected state + * otherwise BSC_SC_INVALID_OPERATION error is returned. + * + * @param c - BACNet socket descriptor initialized by bsc_accept() or + * bsc_connect() calls. + * @param pdu - pointer to a data to send. + * @param pdu_len - size in bytes of data to send. + * + * @return error code from BSC_SC_RET enum. + * The following error codes can be returned: + * BSC_SC_BAD_PARAM - In a case if some input parameter is + * incorrect. + * BSC_SC_INVALID_OPERATION - if socket is not in opened state, + or disconnect operation is in progress using + bsc_disconnect() or bsc_deinit_ctx(). + * BSC_SC_SUCCESS - operation has succeeded. + * BSC_SC_NO_RESOURCES - there are not resources (memory, etc.. ) + * to send data + */ + +BACNET_STACK_EXPORT +BSC_SC_RET bsc_send(BSC_SOCKET *c, uint8_t *pdu, size_t pdu_len); + +BACNET_STACK_EXPORT +uint16_t bsc_get_next_message_id(void); + +BACNET_STACK_EXPORT +void bsc_socket_maintenance_timer(uint16_t seconds); + +/** + * @brief bsc_socket_get_peer_addr() function gets information + * about remote peer address only for socket with acceptor cotext. + * + * @param c - BACNet socket descriptor initialized by bsc_accept() call. + * @param data - pointer to a struct holding address information. + * @param pdu_len - size in bytes of data to send. + * + * @return false if socket is not acceptor or bad parameter was passed or + * if getting of address information failed. + * true if function succeeds. + */ + +BACNET_STACK_EXPORT +bool bsc_socket_get_peer_addr(BSC_SOCKET *c, BACNET_HOST_N_PORT_DATA *data); + +BACNET_STACK_EXPORT +uint8_t *bsc_socket_get_global_buf(void); + +BACNET_STACK_EXPORT +size_t bsc_socket_get_global_buf_size(void); +#endif diff --git a/src/bacnet/datalink/bsc/bsc-util.c b/src/bacnet/datalink/bsc/bsc-util.c new file mode 100644 index 00000000..427caeb6 --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-util.c @@ -0,0 +1,343 @@ +/** + * @file + * @brief module for common function for BACnet/SC implementation + * @author Kirill Neznamov + * @date Jule 2022 + * SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include "bacnet/datalink/bsc/bsc-util.h" +#include "bacnet/basic/object/bacfile.h" +#include "bacnet/basic/object/netport.h" +#include "bacnet/basic/object/sc_netport.h" +#include "bacnet/basic/object/bacfile.h" +#include "bacnet/basic/sys/debug.h" +#include + +#define PRINTF debug_aprintf +#define PRINTF_ERR debug_perror + +/** + * @brief Map websocket return code to BACnet/SC return code + * @param ret - websocket return code + * @return BACnet/SC return code + */ +BSC_SC_RET bsc_map_websocket_retcode(BSC_WEBSOCKET_RET ret) +{ + switch (ret) { + case BSC_WEBSOCKET_SUCCESS: + return BSC_SC_SUCCESS; + case BSC_WEBSOCKET_NO_RESOURCES: + return BSC_SC_NO_RESOURCES; + case BSC_WEBSOCKET_BAD_PARAM: + return BSC_SC_BAD_PARAM; + case BSC_WEBSOCKET_INVALID_OPERATION: + default: + return BSC_SC_INVALID_OPERATION; + } +} + +/** + * @brief Copy BACnet Secure Connect VMAC address + * @param dst - destination VMAC address + * @param src - source VMAC address + */ +void bsc_copy_vmac(BACNET_SC_VMAC_ADDRESS *dst, BACNET_SC_VMAC_ADDRESS *src) +{ + memcpy(dst->address, src->address, sizeof(src->address)); +} + +/** + * @brief Copy the BACnet Secure Connect UUID + * @param dst - destination UUID + * @param src - source UUID + */ +void bsc_copy_uuid(BACNET_SC_UUID *dst, BACNET_SC_UUID *src) +{ + memcpy(dst->uuid, src->uuid, sizeof(src->uuid)); +} + +/** + * @brief Convert BACnet Secure Connect VMAC address to string + * @param vmac - VMAC address + * @return string representation of VMAC address + */ +char *bsc_vmac_to_string(BACNET_SC_VMAC_ADDRESS *vmac) +{ + static char buf[128]; + sprintf( + buf, "%02x%02x%02x%02x%02x%02x", vmac->address[0], vmac->address[1], + vmac->address[2], vmac->address[3], vmac->address[4], vmac->address[5]); + return buf; +} + +/** + * @brief Convert BACnet Secure Connect UUID to string + * @param uuid - UUID + * @return string representation of UUID + */ +char *bsc_uuid_to_string(BACNET_SC_UUID *uuid) +{ + static char buf[128]; + sprintf( + buf, + "%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x", + uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], + uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7], + uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], + uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]); + return buf; +} + +/** + * @brief Generate random BACnet Secure Connect VMAC address + * @param p - pointer to the VMAC address + */ +void bsc_generate_random_vmac(BACNET_SC_VMAC_ADDRESS *p) +{ + int i; + + for (i = 0; i < BVLC_SC_VMAC_SIZE; i++) { + p->address[i] = rand() % 255; + if (i == 0) { + /* According H.7.3 EUI-48 and Random-48 VMAC Address: + The Random-48 VMAC is a 6-octet VMAC address in which the least + significant 4 bits (Bit 3 to Bit 0) in the first octet shall be + B'0010' (X'2'), and all other 44 bits are randomly selected to be + 0 or 1. */ + p->address[i] = (p->address[i] & 0xF0) | 0x02; + } + } + debug_printf_hex( + 0, p->address, BVLC_SC_VMAC_SIZE, "bsc_generate_random_vmac"); +} + +/** + * @brief Generate random BACnet Secure Connect UUID + * @param p - pointer to the UUID + */ +void bsc_generate_random_uuid(BACNET_SC_UUID *p) +{ + int i; + + for (i = 0; i < BVLC_SC_UUID_SIZE; i++) { + p->uuid[i] = rand() % 255; + } + debug_printf_hex(0, p->uuid, BVLC_SC_UUID_SIZE, "bsc_generate_random_uuid"); +} + +/* + * bsc_node_load_cert_bacfile loads one credentional file from bacfile object + * Note: the function adds null-terminated byte to loaded file + * (certificate, certificate key). + * The MbedTLS PEM parser requires data to be null-terminated. + */ +#ifdef CONFIG_MBEDTLS +#define ZERO_BYTE 1 +#else +#define ZERO_BYTE 0 +#endif + +/** + * @brief Load certificate from BACnet file object + * @param file_instance - file instance + * @param pbuf - pointer to the buffer + * @param psize - pointer to the size + * @return true if successful, else false + */ +static bool bsc_node_load_cert_bacfile( + uint32_t file_instance, uint8_t **pbuf, size_t *psize) +{ + uint32_t file_length; + uint8_t *buf; + + *psize = bacfile_file_size(file_instance) + ZERO_BYTE; + if (*psize == 0) { + return false; + } + + buf = calloc(1, *psize); + if (buf == NULL) { + return false; + } + + file_length = + bacfile_read(file_instance, buf, (uint32_t)(*psize - ZERO_BYTE)); +#ifdef CONFIG_MBEDTLS + buf[*psize - 1] = 0; +#endif + if (file_length == 0) { + PRINTF_ERR("Can't read %s file\n", bacfile_pathname(file_instance)); + free(buf); + return false; + } + *pbuf = buf; + return true; +} + +/** + * @brief Fill BACnet/SC node configuration from network port object + * @param bsc_conf - pointer to the BACnet/SC node configuration + * @param event_func - event function + * @return true if successful, else false + */ +bool bsc_node_conf_fill_from_netport( + BSC_NODE_CONF *bsc_conf, BSC_NODE_EVENT_FUNC event_func) +{ + uint32_t instance; + uint32_t file_instance; + + instance = Network_Port_Index_To_Instance(0); + bsc_conf->ca_cert_chain = NULL; + bsc_conf->cert_chain = NULL; + bsc_conf->key = NULL; + + file_instance = Network_Port_Issuer_Certificate_File(instance, 0); + if (!bsc_node_load_cert_bacfile( + file_instance, &bsc_conf->ca_cert_chain, + &bsc_conf->ca_cert_chain_size)) { + bsc_node_conf_cleanup(bsc_conf); + return false; + } + + file_instance = Network_Port_Operational_Certificate_File(instance); + if (!bsc_node_load_cert_bacfile( + file_instance, &bsc_conf->cert_chain, &bsc_conf->cert_chain_size)) { + bsc_node_conf_cleanup(bsc_conf); + return false; + } + + file_instance = Network_Port_Certificate_Key_File(instance); + bsc_conf->key_size = bacfile_file_size(file_instance) + 1; + if (!bsc_node_load_cert_bacfile( + file_instance, &bsc_conf->key, &bsc_conf->key_size)) { + bsc_node_conf_cleanup(bsc_conf); + return false; + } + bsc_conf->local_uuid = + (BACNET_SC_UUID *)Network_Port_SC_Local_UUID(instance); + Network_Port_MAC_Address_Value( + instance, bsc_conf->local_vmac.address, + sizeof(bsc_conf->local_vmac.address)); + bsc_conf->max_local_bvlc_len = + (uint16_t)Network_Port_Max_BVLC_Length_Accepted(instance); + bsc_conf->max_local_npdu_len = + (uint16_t)Network_Port_Max_NPDU_Length_Accepted(instance); + bsc_conf->connect_timeout_s = + (uint16_t)Network_Port_SC_Connect_Wait_Timeout(instance); + bsc_conf->heartbeat_timeout_s = + (uint16_t)Network_Port_SC_Heartbeat_Timeout(instance); + bsc_conf->disconnect_timeout_s = + (uint16_t)Network_Port_SC_Disconnect_Wait_Timeout(instance); + bsc_conf->reconnnect_timeout_s = + (uint16_t)Network_Port_SC_Maximum_Reconnect_Time(instance); + bsc_conf->address_resolution_timeout_s = bsc_conf->connect_timeout_s; + bsc_conf->address_resolution_freshness_timeout_s = + bsc_conf->connect_timeout_s; + bsc_conf->primaryURL = + (char *)Network_Port_SC_Primary_Hub_URI_char(instance); + bsc_conf->failoverURL = + (char *)Network_Port_SC_Failover_Hub_URI_char(instance); +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + bsc_conf->direct_connect_initiate_enable = + Network_Port_SC_Direct_Connect_Initiate_Enable(instance); + bsc_conf->direct_connect_accept_enable = + Network_Port_SC_Direct_Connect_Accept_Enable(instance); + Network_Port_SC_Direct_Connect_Binding_get( + instance, &bsc_conf->direct_server_port, &bsc_conf->direct_iface); +#endif +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + Network_Port_SC_Hub_Function_Binding_get( + instance, &bsc_conf->hub_server_port, &bsc_conf->hub_iface); + bsc_conf->hub_function_enabled = + Network_Port_SC_Hub_Function_Enable(instance); +#endif + +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + bsc_conf->direct_connection_accept_uris = + Network_Port_SC_Direct_Connect_Accept_URIs_char(instance); + bsc_conf->direct_connection_accept_uris_len = + strlen(bsc_conf->direct_connection_accept_uris); +#endif + bsc_conf->event_func = event_func; + return true; +} + +/** + * @brief Cleanup BACnet/SC node configuration + * @param bsc_conf - pointer to the BACnet/SC node configuration + */ +void bsc_node_conf_cleanup(BSC_NODE_CONF *bsc_conf) +{ + bsc_conf->ca_cert_chain_size = 0; + if (bsc_conf->ca_cert_chain) { + free(bsc_conf->ca_cert_chain); + } + bsc_conf->cert_chain_size = 0; + if (bsc_conf->cert_chain) { + free(bsc_conf->cert_chain); + } + bsc_conf->key_size = 0; + if (bsc_conf->key) { + free(bsc_conf->key); + } +} + +/** + * @brief Copy string + * @param dst - destination string + * @param src - source string + * @param dst_len - destination string length + */ +void bsc_copy_str(char *dst, const char *src, size_t dst_len) +{ + size_t len; + if (dst_len > 0) { + len = strlen(src) >= dst_len ? dst_len - 1 : strlen(src); + memcpy(dst, src, len); + dst[len] = 0; + } +} + +/** + * @brief Set timestamp + * @param timestamp - pointer to the timestamp + */ +void bsc_set_timestamp(BACNET_DATE_TIME *timestamp) +{ + int16_t utc_offset_minutes; + bool dst_active; + datetime_local( + ×tamp->date, ×tamp->time, &utc_offset_minutes, &dst_active); +} + +/** + * @brief Check if BACnet/SC certificate files exist + * @return true if all files exist, else false + */ +bool bsc_cert_files_check(void) +{ + uint32_t instance; + uint32_t file_instance; + + instance = Network_Port_Index_To_Instance(0); + + file_instance = Network_Port_Issuer_Certificate_File(instance, 0); + if (bacfile_file_size(file_instance) == 0) { + PRINTF_ERR("CA certificate file not exist\n"); + return false; + } + + file_instance = Network_Port_Operational_Certificate_File(instance); + if (bacfile_file_size(file_instance) == 0) { + PRINTF_ERR("Certificate file not exist\n"); + return false; + } + + file_instance = Network_Port_Certificate_Key_File(instance); + if (bacfile_file_size(file_instance) == 0) { + PRINTF_ERR("Certificate key file not exist\n"); + return false; + } + + return true; +} diff --git a/src/bacnet/datalink/bsc/bsc-util.h b/src/bacnet/datalink/bsc/bsc-util.h new file mode 100644 index 00000000..29482a6b --- /dev/null +++ b/src/bacnet/datalink/bsc/bsc-util.h @@ -0,0 +1,36 @@ +/** + * @file + * @brief module for common function for BACnet/SC implementation + * @author Kirill Neznamov + * @date Jule 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_UTIL_H +#define BACNET_DATALINK_BSC_UTIL_H +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/basic/sys/mstimer.h" +#include "bacnet/basic/sys/debug.h" +#include "bacnet/datalink/bsc/bsc-node.h" +#include "bacnet/datalink/bsc/bsc-retcodes.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/websocket.h" +#include "bacnet/datetime.h" + +BSC_SC_RET bsc_map_websocket_retcode(BSC_WEBSOCKET_RET ret); + +void bsc_copy_vmac(BACNET_SC_VMAC_ADDRESS *dst, BACNET_SC_VMAC_ADDRESS *src); +void bsc_copy_uuid(BACNET_SC_UUID *dst, BACNET_SC_UUID *src); +char *bsc_vmac_to_string(BACNET_SC_VMAC_ADDRESS *vmac); +char *bsc_uuid_to_string(BACNET_SC_UUID *uuid); +void bsc_generate_random_vmac(BACNET_SC_VMAC_ADDRESS *p); +void bsc_generate_random_uuid(BACNET_SC_UUID *p); +bool bsc_node_conf_fill_from_netport( + BSC_NODE_CONF *bsc_conf, BSC_NODE_EVENT_FUNC event_func); +void bsc_node_conf_cleanup(BSC_NODE_CONF *bsc_conf); +void bsc_copy_str(char *dst, const char *src, size_t dst_len); +void bsc_set_timestamp(BACNET_DATE_TIME *timestamp); + +#endif diff --git a/src/bacnet/datalink/bsc/bvlc-sc.c b/src/bacnet/datalink/bsc/bvlc-sc.c new file mode 100644 index 00000000..b5284713 --- /dev/null +++ b/src/bacnet/datalink/bsc/bvlc-sc.c @@ -0,0 +1,2357 @@ +/** + * @file + * @brief API for encoding/decoding of BACnet/SC BVLC messages + * @author Kirill Neznamov + * @date May 2022 + * SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include "bacnet/datalink/bsc/bvlc-sc.h" + +typedef enum BACNet_Option_Validation_Type { + BACNET_USER_OPTION_VALIDATION, + BACNET_PDU_DEST_OPTION_VALIDATION, + BACNET_PDU_DATA_OPTION_VALIDATION +} BACNET_OPTION_VALIDATION_TYPE; + +static const char *s_message_is_incompleted = "header options is truncated"; +static const char *s_invalid_header_option_type = + "header option type must be 'Secure Path' or 'Proprietary Header'"; +static const char *s_invalid_header_1 = + "'Secure Path' header option can be added only to data options in bvlc " + "message"; +static const char *s_invalid_header_2 = + "'Secure Path' header option must not have header data"; +static const char *s_invalid_header_3 = + "'Proprietary Header' option must have header data"; +static const char *s_result_incomplete = + "BVLC-Result message has incomplete payload"; +static const char *s_result_incorrect_bvlc_function = + "parameter 'Result For BVLC Function' is out of range"; +static const char *s_result_incorrect_result_code = + "parameter 'Result Code' must be 0x00 (ACK) or 0x01(NAK)"; +static const char *s_result_inconsistent = + "BVLC-Result message has data inconsistency in payload"; +static const char *s_result_unexpected_data = + "BVLC-Result message is longer than expected"; +static const char *s_advertisiment_incomplete = + "advertisiment message has incomplete payload"; +static const char *s_advertisiment_unexpected = + "advertisiment message is longer than expected"; +static const char *s_advertisiment_param1_error = + "parameter 'Hub Connection Status' in advertisiment message must be in " + "range [0, 2]"; +static const char *s_advertisiment_param2_error = + "parameter 'Accept Direct Connections'in advertisiment message must be in " + "range [0, 1]"; +static const char *s_connect_request_incomplete = + "connect-request message has incomplete payload"; +static const char *s_connect_request_unexpected = + "connect-request message is longer than expected"; +static const char *s_connect_accept_incomplete = + "connect-accept message has incomplete payload"; +static const char *s_connect_accept_unexpected = + "connect-accept is longer than expected"; +static const char *s_proprietary_incomplete = + "proprietary message has incomplete payload"; +static const char *s_hdr_incomplete1 = + "message is incomplete, 'Originating Virtual Address' field is truncated"; +static const char *s_hdr_incomplete2 = + "message is incomplete, 'Destination Virtual Address' field is truncated"; +static const char *s_unknown_bvlc_function = + "unknown value of 'BVLC Function' field in message"; +static const char *s_dest_options_list_too_long = + "message contains more than #BVLC_SC_HEADER_OPTION_MAX options in destion " + "options list"; +static const char *s_data_options_list_too_long = + "message contains more than #BVLC_SC_HEADER_OPTION_MAX options in data " + "options list"; +static const char *s_result_unexpected_data_options = + "BVLC-Result message must not have data options"; +static const char *s_result_payload_expected = + "BVLC-Result message must have payload"; +static const char *s_encapsulated_npdu_payload_expected = + "encapsulated-npdu message must have payload"; +static const char *s_address_resolution_data_options = + "address-resolution message must not have data options"; +static const char *s_address_resolution_unexpected = + "address-resolution message is longer than expected"; +static const char *s_address_resolution_ack_data_options = + "address-resolutio-ack message must not have data options"; +static const char *s_advertisiment_data_options = + "advertisiment message must not have data options"; +static const char *s_advertisiment_payload_expected = + "advertisiment message must have payload"; +static const char *s_advertisiment_solicitation_data_options = + "advertisiment solicitation message must not have data options"; +static const char *s_advertisiment_solicitation_payload_expected = + "advertisiment solicitation message must have payload"; +static const char *s_origin_unexpected = + "'Originating Virtual Address' field must be absent in message"; +static const char *s_dest_unexpected = + "'Destination Virtual Address' field must be absent in message"; +static const char *s_data_option_unexpected = + "message must not have data options"; +static const char *s_message_too_long = "message is longer than expected"; +static const char *s_absent_payload = "payload is absent in the message"; +static const char *s_proprietary_data_options = + "proprietary message must not have data options"; +static const char *s_proprietary_payload = + "proprietary message must have payload"; + +/** + * @brief Validate BVLC-SC header options + * @param validation_type - type of validation + * @param option_headers - pointer to the option headers + * @param option_headers_max_len - maximum length of the option headers + * @param out_option_headers_real_length - pointer to the real length of the + * option headers + * @param out_option_header_num - pointer to the number of the option headers + * @param error - pointer to the error code + * @param class - pointer to the error class + * @param error_desc_string - pointer to the error description string + * @return true if the option headers are valid, false otherwise + */ +static bool bvlc_sc_validate_options_headers( + BACNET_OPTION_VALIDATION_TYPE validation_type, + uint8_t *option_headers, + size_t option_headers_max_len, + size_t *out_option_headers_real_length, + size_t *out_option_header_num, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **error_desc_string) +{ + int options_len = 0; + uint8_t flags = 0; + uint8_t option; + uint16_t hdr_len; + + if (!option_headers_max_len || !out_option_headers_real_length) { + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *error_desc_string = s_message_is_incompleted; + return false; + } + + if (out_option_header_num) { + *out_option_header_num = 0; + } + + while (options_len < option_headers_max_len) { + flags = option_headers[options_len]; + + option = flags & BVLC_SC_HEADER_OPTION_TYPE_MASK; + + if (option != BVLC_SC_OPTION_TYPE_SECURE_PATH && + option != BVLC_SC_OPTION_TYPE_PROPRIETARY) { + *error = ERROR_CODE_HEADER_ENCODING_ERROR; + *class = ERROR_CLASS_COMMUNICATION; + *error_desc_string = s_invalid_header_option_type; + return false; + } + + if (option == BVLC_SC_OPTION_TYPE_SECURE_PATH) { + if (validation_type == BACNET_PDU_DEST_OPTION_VALIDATION) { + /* According BACNet standard secure path header option can be + added only to data options. (AB.2.3.1 Secure Path Header + Option) */ + *error = ERROR_CODE_HEADER_ENCODING_ERROR; + *class = ERROR_CLASS_COMMUNICATION; + *error_desc_string = s_invalid_header_1; + return false; + } + if (flags & BVLC_SC_HEADER_DATA) { + /* securepath option header does not have data header + according bacnet stadard */ + *error = ERROR_CODE_HEADER_ENCODING_ERROR; + *class = ERROR_CLASS_COMMUNICATION; + *error_desc_string = s_invalid_header_2; + return false; + } + options_len++; + } else if (option == BVLC_SC_OPTION_TYPE_PROPRIETARY) { + if (!(flags & BVLC_SC_HEADER_DATA)) { + /* proprietary option must have header data */ + *error = ERROR_CODE_HEADER_ENCODING_ERROR; + *class = ERROR_CLASS_COMMUNICATION; + *error_desc_string = s_invalid_header_3; + return false; + } + options_len++; + if (options_len + 2 > option_headers_max_len) { + /* not enough data to process header that means + that probably message is incomplete */ + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *error_desc_string = s_message_is_incompleted; + return false; + } + memcpy(&hdr_len, &option_headers[options_len], 2); + options_len += + 2 /* header length */ + hdr_len /* length of data header */; + } + + if (options_len > option_headers_max_len) { + /* not enough data to process header that means + that probably message is incomplete */ + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *error_desc_string = s_message_is_incompleted; + return false; + } + + if (out_option_header_num && (*out_option_header_num < USHRT_MAX)) { + *out_option_header_num += 1; + } + + if (!(flags & BVLC_SC_HEADER_MORE)) { + break; + } + } + + *out_option_headers_real_length = options_len; + return true; +} + +/** + * @brief Function adds header option to data option list + * into provided pdu and stores the result in out_pdu. + * Note that last added option will be the first option in + * the list. + * @param out_pdu - buffer where modified pdu will be stored in + * @param out_pdu_size - size of the out_pdu + * @param pdu - input pdu to which user wants to add option + * @param pdu_size - size of input pdu + * @param sc_option - pointer to encoded header option + * @param sc_option_len - length of encoded header option + * @return returns length (>0) in bytes of a pdu with added option + */ +static size_t bvlc_sc_add_option( + bool to_data_option, + uint8_t *pdu, + size_t pdu_size, + uint8_t *in_pdu, + size_t in_pdu_len, + uint8_t *sc_option, + size_t sc_option_len) +{ + size_t offs = 4; + uint8_t flags = 0; + size_t options_len = 0; + uint8_t mask; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + BACNET_OPTION_VALIDATION_TYPE vt; + const char *err_desc; + + if (!in_pdu_len || !in_pdu || !sc_option_len || !pdu_size || !pdu || + !sc_option) { + return 0; + } + + if (pdu_size < 4 || in_pdu_len < 4) { + return 0; + } + + if (sc_option_len + in_pdu_len > USHRT_MAX) { + return 0; + } + + if (pdu_size < in_pdu_len) { + return 0; + } + + if (pdu_size < sc_option_len + in_pdu_len) { + return 0; + } + + /* ensure that sc_option does not have flag more options */ + + if (sc_option[0] & BVLC_SC_HEADER_MORE) { + return 0; + } + + if (!to_data_option && + (sc_option[0] & BVLC_SC_HEADER_OPTION_TYPE_MASK) == + BVLC_SC_OPTION_TYPE_SECURE_PATH) { + /* According BACNet standard secure path header option can be added + only to data options. (AB.2.3.1 Secure Path Header Option) */ + return 0; + } + + /* ensure that user wants to add valid option */ + if (!bvlc_sc_validate_options_headers( + BACNET_USER_OPTION_VALIDATION, sc_option, sc_option_len, + &options_len, NULL, &error, &class, &err_desc)) { + return 0; + } + + flags = in_pdu[1]; + + if (flags & BVLC_SC_CONTROL_DEST_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + if (flags & BVLC_SC_CONTROL_ORIG_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + if (offs > in_pdu_len) { + return 0; + } + + /* insert options */ + + if (to_data_option) { + mask = BVLC_SC_CONTROL_DATA_OPTIONS; + vt = BACNET_PDU_DATA_OPTION_VALIDATION; + + if (flags & BVLC_SC_CONTROL_DEST_OPTIONS) { + /* some options are already presented in message. + Validate them at first. */ + if (!bvlc_sc_validate_options_headers( + BACNET_PDU_DEST_OPTION_VALIDATION, &in_pdu[offs], + in_pdu_len - offs, &options_len, NULL, &error, &class, + &err_desc)) { + return 0; + } + offs += options_len; + } + } else { + mask = BVLC_SC_CONTROL_DEST_OPTIONS; + vt = BACNET_PDU_DEST_OPTION_VALIDATION; + } + + if ((flags & mask)) { + /* some options are already presented in message. + Validate them at first. */ + + if (!bvlc_sc_validate_options_headers( + vt, &in_pdu[offs], in_pdu_len - offs, &options_len, NULL, + &error, &class, &err_desc)) { + return 0; + } + } + + if (pdu == in_pdu) { + /* user would like to use the same buffer where in_pdu + is already stored. So we need to expand it for sc_option_len */ + memmove(&pdu[offs + sc_option_len], &pdu[offs], in_pdu_len - offs); + memcpy(&pdu[offs], sc_option, sc_option_len); + } else { + /* user would like to use a new buffer for in_pdu with option.*/ + memcpy(pdu, in_pdu, offs); + memcpy(&pdu[offs], sc_option, sc_option_len); + if ((flags & mask)) { + pdu[offs] |= BVLC_SC_HEADER_MORE; + } + memcpy(&pdu[offs + sc_option_len], &in_pdu[offs], in_pdu_len - offs); + } + if ((flags & mask)) { + pdu[offs] |= BVLC_SC_HEADER_MORE; + } else { + pdu[1] |= mask; + } + return in_pdu_len + sc_option_len; +} + +/** + * @brief Function adds header option to destination option list + into provided pdu and stores the result in out_pdu. + Note that last added option will be the first option in + the list. + * @param out_pdu - buffer where modified pdu will be stored in + * @param out_pdu_size - size of the out_pdu + * @param pdu - input pdu to which user wants to add option + * @param pdu_size - size of input pdu + * @param sc_option - pointer to encoded header option + * @param sc_option_len - length of encoded header option + * @return returns length (>0) in bytes of a pdu with added option + or 0 in a case of error (validation of pdu or sc_option fails) + */ +size_t bvlc_sc_add_option_to_destination_options( + uint8_t *out_pdu, + size_t out_pdu_size, + uint8_t *pdu, + size_t pdu_size, + uint8_t *sc_option, + size_t sc_option_len) +{ + return bvlc_sc_add_option( + false, out_pdu, out_pdu_size, pdu, pdu_size, sc_option, sc_option_len); +} + +/** + * @brief Function adds header option to data option list + into provided pdu and stores the result in out_pdu. + Note that last added option will be the first option in + the list. + * @param out_pdu - buffer where modified pdu will be stored in + * @param out_pdu_size - size of the out_pdu + * @param pdu - input pdu to which user wants to add option + * @param pdu_size - size of input pdu + * @param sc_option - pointer to encoded header option + * @param sc_option_len - length of encoded header option + * @return returns length (>0) in bytes of a pdu with added option + or 0 in a case of error (validation of pdu or sc_option fails) + */ +size_t bvlc_sc_add_option_to_data_options( + uint8_t *out_pdu, + size_t out_pdu_size, + uint8_t *pdu, + size_t pdu_size, + uint8_t *sc_option, + size_t sc_option_len) +{ + return bvlc_sc_add_option( + true, out_pdu, out_pdu_size, pdu, pdu_size, sc_option, sc_option_len); +} + +/** + * @brief Function encodes proprietary header option in correspondence + * of BACNet standard AB.2.3.2 Proprietary Header Options. + * Any proprietary header option shall consist of the + * following fields: + * + * Header Marker 1-octet 'More Options' = 0 or 1, + * 'Must Understand' = 0 or 1, + * 'Header Data Flag' = 1, + * 'Header Option Type' = 31 + * Header Length 2-octets Length of 'Header Data' field, + * in octets. + * Header Data 3-N octets Required to include: + * Vendor Identifier 2-octets Vendor Identifier, with most + * significant octet first, of the + * organization defining this option. + * Proprietary Option Type 1-octet An indication of the proprietary + * header option type. + * Proprietary Header data Variable A proprietary string of octets. + * Can be zero length. + * + * @param pdu - buffer to store encoded option + * @param pdu_size - size of pdu + * @param must_understand - value for 'Must Understand' flag. + * @param pdu_size - size of input pdu + * @param vendor_id - vendor identifier + * @param proprietary_option_type - proprietary header option type. + * @param proprietary_data - buffer with proprietary data + * @param proprietary_data_len - size of buffer with proprietary data + * @return value (>0) in bytes of a encoded options or 0 in a case of + * an error + */ +size_t bvlc_sc_encode_proprietary_option( + uint8_t *pdu, + size_t pdu_size, + bool must_understand, + uint16_t vendor_id, + uint8_t proprietary_option_type, + uint8_t *proprietary_data, + size_t proprietary_data_len) +{ + size_t total_len = + sizeof(vendor_id) + proprietary_data_len + sizeof(uint8_t); + + if (proprietary_data_len > BVLC_SC_NPDU_SIZE - + 3 /* header marker len + header length len) */ - + sizeof(vendor_id)) { + return 0; + } + + if (pdu_size < total_len + 3 /* header marker len + header data len) */) { + return 0; + } + + /* reset More Options, Must Understand and Header Data flags + they will be updated as a result of call bvlc_sc_add_sc_option() */ + + pdu[0] = BVLC_SC_OPTION_TYPE_PROPRIETARY; + + if (must_understand) { + pdu[0] |= BVLC_SC_HEADER_MUST_UNDERSTAND; + } + + pdu[0] |= BVLC_SC_HEADER_DATA; + memcpy(&pdu[1], &total_len, sizeof(total_len)); + memcpy(&pdu[3], &vendor_id, sizeof(vendor_id)); + memcpy(&pdu[5], &proprietary_option_type, sizeof(proprietary_option_type)); + memcpy(&pdu[6], proprietary_data, proprietary_data_len); + return total_len + 3; +} + +/** + * @brief Function encodes security path header option in correspondence + * of BACNet standard AB.2.3.1 Secure Path Header Option. + * Any proprietary header option shall consist of the + * following fields: + * + * Header Marker 1-octet 'Last Option' = 0 or 1, + * 'Must Understand' = 0 or 1, + * 'Header Data Flag' = 1, + * 'Header Option Type' = 31 + * + * @param pdu - buffer to store encoded option + * @param pdu_size - size of pdu + * @param must_understand - value for 'Must Understand' flag. + * @return value (>0) in bytes of a encoded options or 0 in a case of + * an error + */ +size_t bvlc_sc_encode_secure_path_option( + uint8_t *pdu, size_t pdu_size, bool must_understand) +{ + if (pdu_size < 1) { + return 0; + } + + pdu[0] = BVLC_SC_OPTION_TYPE_SECURE_PATH; + + if (must_understand) { + pdu[0] |= BVLC_SC_HEADER_MUST_UNDERSTAND; + } + return 1; +} + +/** + * @brief Decodes BVLC header options marker. Function assumes to be called + * iteratively until end of option list will be reached. User must + * call this function on previously validated options list only + * because sanity checks are omitted.That valided option list can + * be get by some bvlc_sc_encode_ function calls. + * + * @param in_option_list - buffer containing list of header options.It must + point to head list item to be decoded. + * @param out_opt_type - pointer to store decoded option type, must not + be NULL + * @param out_must_understand - pointer to store decoded 'must understand' + * flag from options marker, must not be NULL. + * @param out_next_option - pointer to next option list item to be decoded, + * can be NULL if no any option items left. + * + * @return 0 in a case if decoding failed, otherwise returns + * length value of decoded option in bytes. + * + * Typically code for parsing of option list looks like following: + * + * uint8_t *current = in_options_list; + * int option_len = 0; + * while(current) { + * bvlc_sc_decode_option_hdr(current, + * &type, ...., ¤t); + * } + * + */ + +static void bvlc_sc_decode_option_hdr( + uint8_t *in_options_list, + BVLC_SC_OPTION_TYPE *out_opt_type, + bool *out_must_understand, + uint8_t **out_next_option) +{ + *out_next_option = NULL; + *out_opt_type = (BVLC_SC_OPTION_TYPE)(in_options_list[0] & + BVLC_SC_HEADER_OPTION_TYPE_MASK); + *out_must_understand = + (in_options_list[0] & BVLC_SC_HEADER_MUST_UNDERSTAND) ? true : false; + + if (*out_opt_type == BVLC_SC_OPTION_TYPE_SECURE_PATH) { + if (in_options_list[0] & BVLC_SC_HEADER_MORE) { + *out_next_option = in_options_list + 1; + } + } else if (*out_opt_type == BVLC_SC_OPTION_TYPE_PROPRIETARY) { + uint16_t hdr_len; + memcpy(&hdr_len, &in_options_list[1], sizeof(hdr_len)); + hdr_len += sizeof(hdr_len) + 1; + if (in_options_list[0] & BVLC_SC_HEADER_MORE) { + *out_next_option = in_options_list + hdr_len; + } + } +} + +/** + * @brief Decodes BVLC header proprietary option + * Function assumes to be called iteratively until end of option list + * will be reached. User must call this function on previously validated + * options list only because sanity checks are omitted. That valided + * option list can be get by some bvlc_sc_encode_ function calls. + * @param in_options_list - buffer containing list of header options.It must + * point to head list item to be decoded. + * @param out_vendor_id - pointer to store vendor id, must not be NULL + * @param out_proprietary_option_type - pointer to store proprietary option + * type, must not be NULL + * @param out_proprietary_data - pointer to store proprietary data, can be NULL + * @param out_proprietary_data_len - pointer to store length of proprietary + * data, can be NULL + * @return 0 in a case if decoding failed, otherwise return + */ +static void bvlc_sc_decode_proprietary_option( + uint8_t *in_options_list, + uint16_t *out_vendor_id, + uint8_t *out_proprietary_option_type, + uint8_t **out_proprietary_data, + size_t *out_proprietary_data_len) +{ + uint16_t hdr_len; + + *out_proprietary_data = NULL; + *out_proprietary_data_len = 0; + memcpy(&hdr_len, &in_options_list[1], sizeof(hdr_len)); + memcpy(out_vendor_id, &in_options_list[3], sizeof(uint16_t)); + *out_proprietary_option_type = in_options_list[5]; + + if (hdr_len > 3) { + *out_proprietary_data = &in_options_list[6]; + *out_proprietary_data_len = hdr_len - 3; + } +} + +/** + * @brief encode BVLC-SC header + * + * BACNet standard AB.2.2 BVLC-SC Header Format: + * BVLC Function 1-octet BVLC function code + * Control Flags 1-octet Control flags + * Message ID 2-octets Message identifier + * Originating Virtual Address 0 or 6-octets If absent, message is from + * connection peer node + * Destination Virtual Address 0 or 6-octets If absent, message is for + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param bvlc_function - BVLC function code, check BVLC_SC_MESSAGE_TYPE enum. + * @param message_id - The message identifier + * @param origin - Originating virtual address, can be NULL + * @param dest - Destination virtual address, can be NULL + * @return number of bytes encoded, in a case of error returns 0. + */ +static size_t bvlc_sc_encode_common( + uint8_t *pdu, + size_t pdu_len, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest) +{ + size_t offs = 4; + if (pdu_len < 4) { + return 0; + } + pdu[0] = bvlc_function; + pdu[1] = 0; + memcpy(&pdu[2], &message_id, sizeof(message_id)); + + if (origin) { + if (pdu_len < offs + BVLC_SC_VMAC_SIZE) { + return 0; + } + pdu[1] |= BVLC_SC_CONTROL_ORIG_VADDR; + memcpy(&pdu[offs], &origin->address[0], BVLC_SC_VMAC_SIZE); + offs += BVLC_SC_VMAC_SIZE; + } + + if (dest) { + if (pdu_len < offs + BVLC_SC_VMAC_SIZE) { + return 0; + } + pdu[1] |= BVLC_SC_CONTROL_DEST_VADDR; + memcpy(&pdu[offs], &dest->address[0], BVLC_SC_VMAC_SIZE); + offs += BVLC_SC_VMAC_SIZE; + } + + return offs; +} + +/** + * @brief Function encodes the BVLC-Result message according BACNet standard + * AB.2.4.1 BVLC-Result Format. + * + * BVLC Function 1-octet (X'00') BVLC-Result + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 or 6-octets If absent, message is from + * connection peer node + * Destination Virtual Address 0 or 6-octets If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * Payload + * Result For BVLC Function 1-octet BVLC function for which this + * is a result + * Result Code 1-octet(X'00') ACK: Successful completion. The + * 'Error Header Marker' and all + * subsequent parameters shall be + * absent. + * (X'01') NAK: The BVLC function failed. + * The 'Error Header Marker', + * the 'Error Class', the 'Error + * Code', and the 'Error Details' + * shall be present. + * Error Header Marker 1-octet The header marker of the + * (Conditional) destination option that caused + * the BVLC function to fail. If + * the NAK is unrelated to a header + * option, this parameter shall be + * X'00'. + * Error Class 2-octets The 'Error Class' field of the + * (Conditional) 'Error' datatype defined in + * Clause 21. + * Error Code 2-octets The 'Error Code' field of the + * (Conditional) 'Error' datatype defined in + * Clause 21. + * Error Details Variable UTF-8 reason text. Can be an + * (Conditional) empty string using no octets. + * Note that this string is not + * encoded as defined in Clause + * 20.2.9, has no character set + * indication octet, and no trailing + * zero octets. See BVLC-Result + * examples in Clause AB.2.17. + * + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param origin - Originating virtual address, can be NULL + * @param dest - Destination virtual address, can be NULL + * @param bvlc_function - BVLC function for which this is a result. + * check BVLC_SC_MESSAGE_TYPE enum. + * @param result_code - 0 (ACK) or 1 (NAK) + * @param error_header_marker -can be NULL depending on result code. + * The header marker of the destination option + * that caused the BVLC function to fail + * @param error_class - can be NULL depending on result code. + * Check BACNET_ERROR_CLASS enum. + * @param error_code - can be NULL depending on result code. + * check BACNET_ERROR_CODE enum. + * @param utf8_details_string - can be NULL depending on result code. + * UTF-8 reason text. + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_result( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t bvlc_function, + uint8_t result_code, + uint8_t *error_header_marker, + uint16_t *error_class, + uint16_t *error_code, + uint8_t *utf8_details_string) +{ + size_t offs; + + if (bvlc_function > (uint16_t)BVLC_SC_PROPRIETARY_MESSAGE) { + /* unsupported bvlc function */ + return 0; + } + + if (result_code != 0 && result_code != 1) { + /* According AB.2.4.1 BVLC-Result Format result code + must be 0x00 or 0x01 */ + return 0; + } + + if (result_code) { + if (!error_class || !error_code) { + /* According AB.2.4.1 BVLC-Result Format error_class + and error_code must be presented */ + return 0; + } + } + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_RESULT, message_id, origin, dest); + + if (!offs) { + return 0; + } + + if (pdu_len < offs + 2) { + return 0; + } + + pdu[offs++] = bvlc_function; + pdu[offs++] = result_code; + + if (!result_code) { + if (error_header_marker || error_class || error_code || + utf8_details_string) { + return 0; + } + return offs; + } + + if (pdu_len < offs + 4) { + return 0; + } + + if (error_header_marker) { + pdu[offs++] = *error_header_marker; + } else { + pdu[offs++] = 0; + } + + memcpy(&pdu[offs], error_class, sizeof(*error_class)); + offs += sizeof(*error_class); + memcpy(&pdu[offs], error_code, sizeof(*error_code)); + offs += sizeof(*error_code); + + if (utf8_details_string) { + if (pdu_len < offs + strlen((const char *)utf8_details_string)) { + return 0; + } + memcpy( + &pdu[offs], utf8_details_string, + strlen((const char *)utf8_details_string)); + offs += strlen((const char *)utf8_details_string); + } + + return offs; +} + +/** + * @brief Function decodes the BVLC-Result message according BACNet standard + * AB.2.4.1 BVLC-Result Format. + * @param payload - pointer to the decoded data + * @param packed_payload - pointer to the packed data + * @param packed_payload_len - size of the packed data + * @param error - pointer to the error code + * @param class - pointer to the error class + * @param err_desc - pointer to the error description string + * @return true if the data is decoded successfully, false otherwise + */ +static bool bvlc_sc_decode_result( + BVLC_SC_DECODED_DATA *payload, + uint8_t *packed_payload, + size_t packed_payload_len, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **err_desc) +{ + size_t i; + + if (packed_payload_len < 2) { + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_result_incomplete; + return false; + } + + memset(&payload->result, 0, sizeof(payload->result)); + + if (packed_payload[0] > (uint8_t)BVLC_SC_PROPRIETARY_MESSAGE) { + /* unknown BVLC function */ + *error = ERROR_CODE_PARAMETER_OUT_OF_RANGE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_result_incorrect_bvlc_function; + return false; + } + + payload->result.bvlc_function = packed_payload[0]; + if (packed_payload[1] != 0 && packed_payload[1] != 1) { + /* result code must be 1 or 0 */ + *error = ERROR_CODE_PARAMETER_OUT_OF_RANGE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_result_incorrect_result_code; + return false; + } + + payload->result.result = packed_payload[1]; + + if (packed_payload[1] == 1) { + if (packed_payload_len < 7) { + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_result_incomplete; + return false; + } + + payload->result.error_header_marker = packed_payload[2]; + memcpy( + &payload->result.error_class, &packed_payload[3], + sizeof(payload->result.error_class)); + memcpy( + &payload->result.error_code, &packed_payload[5], + sizeof(payload->result.error_code)); + + if (packed_payload_len > 7) { + payload->result.utf8_details_string = &packed_payload[7]; + payload->result.utf8_details_string_len = packed_payload_len - 7; + for (i = 0; i < packed_payload_len - 7; i++) { + if (payload->result.utf8_details_string[i] == 0) { + break; + } + } + if (i != packed_payload_len - 7) { + *error = ERROR_CODE_INCONSISTENT_PARAMETERS; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_result_inconsistent; + return false; + } + } + } else if (packed_payload_len > 2) { + /* According EA-001-4 'Clarifying BVLC-Result in BACnet/SC */ + /* If a BVLC message is received that is longer than expected, */ + /* a BVLC-Result NAK shall be returned if it was a unicast message, */ + /* indicating an 'Error Class' of COMMUNICATION and 'Error Code' of */ + /* UNEXPECTED_DATA. The message shall be discarded and not be processed. + */ + *error = ERROR_CODE_UNEXPECTED_DATA; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_result_unexpected_data; + return false; + } + return true; +} + +/** + * @brief Function encodes the Encapsulated-NPDU message according + * BACNet standard AB.2.5 Encapsulated-NPDU. + * + * BVLC Function 1-octet (X'01') Encapsulated-NPDU + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 or 6-octets If absent, message is from + * connection peer node + * Destination Virtual Address 0 or 6-octets If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Optional, 0 to N header options + * Payload + * BACnet NPDU Variable + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param origin - Originating virtual address, can be NULL + * @param dest - Destination virtual address, can be NULL + * @param npdu - Buffer which contains BACnet NPDU + * @param npdu_size - Size of buffer which contains BACnet NPDU + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_encapsulated_npdu( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *npdu, + size_t npdu_size) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_ENCAPSULATED_NPDU, message_id, origin, dest); + if (!offs) { + return 0; + } + + if (pdu_len < offs + npdu_size) { + return 0; + } + + memcpy(&pdu[offs], npdu, npdu_size); + offs += npdu_size; + return offs; +} + +/** + * @brief Function encodes the Address-Resolution message according BACNet + * standard AB.2.6 Address-Resolution + * + * BVLC Function 1-octet (X'02') Address-Resolution + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 or 6-octets If absent, message is from + * connection peer node + * Destination Virtual Address 0 or 6-octets If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param origin - Originating virtual address, can be NULL + * @param dest - Destination virtual address, can be NULL + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_address_resolution( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_ADDRESS_RESOLUTION, message_id, origin, dest); + + return offs; +} + +/** + * @brief Function encodes the Address-Resolution-ACK message according + * BACNet standard AB.2.7.1 Address-Resolution-ACK Format. + * + * BVLC Function 1-octet (X'03') Address-Resolution-ACK + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 or 6-octets If absent, message is from + * connection peer node + * Destination Virtual Address 0 or 6-octets If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * Payload + * WebSocket-URIs Variable UTF-8 string containing a list + * of WebSocket URIs as of RFC3986, + * separated by a single space + * character (X'20'), where the + * source BACnet/SC node accepts + * direct connections. Can be an + * empty string using zero octets. + * See Clause AB.3.3. + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param origin - Originating virtual address, can be NULL + * @param dest - Destination virtual address, can be NULL + * @param web_socket_uris - UTF-8 string containing a list of + * WebSocket URIs as of RFC 3986, separated by a + * single space character (X'20'), where the source + * BACnet/SC node accepts direct connections. + * Can be an empty string using zero octets. + * @param web_socket_uris_len- length of web_socket_uris without trailing zero. + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_address_resolution_ack( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *web_socket_uris, + size_t web_socket_uris_len) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, origin, dest); + if (!offs) { + return 0; + } + + if (web_socket_uris && web_socket_uris_len) { + memcpy(&pdu[offs], web_socket_uris, web_socket_uris_len); + offs += web_socket_uris_len; + } + + return offs; +} + +/** + * @brief Function encodes the Advertisement message according + * BACNet standard AB.2.8.1 Advertisement Format. + * + * BVLC Function 1-octet (X'04') Advertisement + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 or 6-octets If absent, message is from + * connection peer node + * Destination Virtual Address 0 or 6-octets If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * + * Payload + * Hub Connection Status 1-octet X'00' No hub connection. + * X'01' Connected to primary hub. + * X'02' Connected to failover hub. + * Accept Direct Connections 1-octet X'00' The node does not support + * accepting direct + * connections. + * X'01' The node supports accepting + * direct connections. + * Maximum BVLC Length 2-octet The maximum BVLC message size + * that can be received and + * processed by the node, in number + * of octets. + * Maximum NPDU Length 2-octet The maximum NPDU message size + * that can be handled by the node's + * network entity, in number of + * octets. + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param origin - Originating virtual address, can be NULL + * @param dest - Destination virtual address, can be NULL + * @param hub_status - hub connection status + * @param support- accept direct connections + * @param max_bvlc_len - the maximum BVLC message size + * @param max_npdu_size - the maximum NPDU message size + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_advertisiment( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + BACNET_SC_HUB_CONNECTOR_STATE hub_status, + BVLC_SC_DIRECT_CONNECTION_SUPPORT support, + uint16_t max_bvlc_len, + uint16_t max_npdu_size) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_ADVERTISIMENT, message_id, origin, dest); + if (!offs) { + return 0; + } + + pdu[offs++] = (uint8_t)hub_status; + pdu[offs++] = (uint8_t)support; + memcpy(&pdu[offs], &max_bvlc_len, sizeof(max_bvlc_len)); + offs += sizeof(max_bvlc_len); + memcpy(&pdu[offs], &max_npdu_size, sizeof(max_npdu_size)); + offs += sizeof(max_npdu_size); + return offs; +} + +/** + * @brief Function encodes the Advertisement-Solicitation message according + * BACNet standard AB.2.9.1 Advertisement-Solicitation Format. + * @param payload - pointer to the decoded data + * @param packed_payload - pointer to packed data + * @param packed_payload_len - size of packed data + * @param error - pointer to the error code + * @param class - pointer to the error class + * @param err_desc - pointer to the error description string + * @return true if the data is decoded successfully, false otherwise + */ +static bool bvlc_sc_decode_advertisiment( + BVLC_SC_DECODED_DATA *payload, + uint8_t *packed_payload, + size_t packed_payload_len, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **err_desc) +{ + if (packed_payload_len < 6) { + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_advertisiment_incomplete; + return false; + } + if (packed_payload_len > 6) { + *error = ERROR_CODE_UNEXPECTED_DATA; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_advertisiment_unexpected; + return false; + } + if (packed_payload[0] > BACNET_SC_HUB_CONNECTOR_STATE_MAX) { + *error = ERROR_CODE_PARAMETER_OUT_OF_RANGE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_advertisiment_param1_error; + return false; + } + + if (packed_payload[1] > BVLC_SC_DIRECT_CONNECTION_SUPPORT_MAX) { + *error = ERROR_CODE_PARAMETER_OUT_OF_RANGE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_advertisiment_param2_error; + return false; + } + + payload->advertisiment.hub_status = packed_payload[0]; + payload->advertisiment.support = packed_payload[1]; + memcpy( + &payload->advertisiment.max_bvlc_len, &packed_payload[2], + sizeof(payload->advertisiment.max_bvlc_len)); + memcpy( + &payload->advertisiment.max_npdu_len, &packed_payload[4], + sizeof(payload->advertisiment.max_npdu_len)); + return true; +} + +/** + * @brief Function encodes the Advertisement-Solicitation message according + * BACNet standard AB.2.9.1 Advertisement-Solicitation Format. + * + * BVLC Function 1-octet (X'05') Advertisement + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 or 6-octets If absent, message is from + * connection peer node + * Destination Virtual Address 0 or 6-octets If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param origin - Originating virtual address, can be NULL + * @param dest - Destination virtual address, can be NULL + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_advertisiment_solicitation( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, origin, + dest); + return offs; +} + +/** + * @brief Function encodes the Connect-Request message according + * BACNet standard AB.2.10.1 Connect-Request Format. + * + * BVLC Function 1-octet (X'06') Advertisement + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 Absent, is always for + * connection peer node. + * Destination Virtual Address 0 If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * + * Payload + * VMAC Address 6-octets The VMAC address of the + * requesting node. + * Device UUID 16-octet The device UUID of the + * requesting node. + * Maximum BVLC Length 2-octet The maximum BVLC message size + * that can be received and + * processed by the node, in number + * of octets. + * Maximum NPDU Length 2-octet The maximum NPDU message size + * that can be handled by the node's + * network entity, in number of + * octets. + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param local_vmac - VMAC address + * @param local_uuid - The device UUID + * @param max_bvlc_len - the maximum BVLC message size + * @param max_npdu_size - the maximum NPDU message size + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_connect_request( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *local_vmac, + BACNET_SC_UUID *local_uuid, + uint16_t max_bvlc_len, + uint16_t max_npdu_size) +{ + size_t offs; + + if (!local_vmac || !local_uuid) { + return 0; + } + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_CONNECT_REQUEST, message_id, NULL, NULL); + if (!offs) { + return 0; + } + + if (pdu_len < + (offs + BVLC_SC_VMAC_SIZE + BVLC_SC_UUID_SIZE + 2 * sizeof(uint16_t))) { + return 0; + } + + memcpy(&pdu[offs], local_vmac, BVLC_SC_VMAC_SIZE); + offs += BVLC_SC_VMAC_SIZE; + memcpy(&pdu[offs], local_uuid, BVLC_SC_UUID_SIZE); + offs += BVLC_SC_UUID_SIZE; + memcpy(&pdu[offs], &max_bvlc_len, sizeof(max_bvlc_len)); + offs += sizeof(max_bvlc_len); + memcpy(&pdu[offs], &max_npdu_size, sizeof(max_npdu_size)); + offs += sizeof(max_npdu_size); + return (unsigned int)offs; +} + +/** + * @brief Function decodes the Connect-Request message according + * BACNet standard AB.2.10.1 Connect-Request Format. + * @param payload - pointer to the decoded data + * @param packed_payload - pointer to the packed data + * @param packed_payload_len - size of the packed data + * @param error - pointer to the error code + * @param class - pointer to the error class + * @param err_desc - pointer to the error description string + * @return true if the data is decoded successfully, false otherwise + */ +static bool bvlc_sc_decode_connect_request( + BVLC_SC_DECODED_DATA *payload, + uint8_t *packed_payload, + size_t packed_payload_len, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **err_desc) +{ + if (packed_payload_len < 26) { + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_connect_request_incomplete; + return false; + } else if (packed_payload_len > 26) { + *error = ERROR_CODE_UNEXPECTED_DATA; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_connect_request_unexpected; + return false; + } + payload->connect_request.vmac = (BACNET_SC_VMAC_ADDRESS *)packed_payload; + packed_payload += BVLC_SC_VMAC_SIZE; + payload->connect_request.uuid = (BACNET_SC_UUID *)packed_payload; + packed_payload += BVLC_SC_UUID_SIZE; + memcpy( + &payload->connect_request.max_bvlc_len, packed_payload, + sizeof(payload->connect_request.max_bvlc_len)); + packed_payload += sizeof(payload->connect_request.max_bvlc_len); + memcpy( + &payload->connect_request.max_npdu_len, packed_payload, + sizeof(payload->connect_request.max_npdu_len)); + return true; +} + +/** + * @brief Function encodes the Connect-Accept message according + * BACNet standard AB.2.11.1 Connect-Accept Format. + * + * BVLC Function 1-octet (X'07') Advertisement + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 Absent, is always for + * connection peer node. + * Destination Virtual Address 0 If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * + * Payload + * VMAC Address 6-octets The VMAC address of the + * requesting node. + * Device UUID 16-octet The device UUID of the + * requesting node. + * Maximum BVLC Length 2-octet The maximum BVLC message size + * that can be received and + * processed by the node, in number + * of octets. + * Maximum NPDU Length 2-octet The maximum NPDU message size + * that can be handled by the node's + * network entity, in number of + * octets. + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param local_vmac - VMAC address + * @param local_uuid - The device UUID + * @param max_bvlc_len - the maximum BVLC message size + * @param max_npdu_size - the maximum NPDU message size + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_connect_accept( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *local_vmac, + BACNET_SC_UUID *local_uuid, + uint16_t max_bvlc_len, + uint16_t max_npdu_len) +{ + size_t offs; + + if (!local_vmac || !local_uuid) { + return 0; + } + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_CONNECT_ACCEPT, message_id, NULL, NULL); + if (!offs) { + return 0; + } + + if (pdu_len < + (offs + BVLC_SC_VMAC_SIZE + BVLC_SC_UUID_SIZE + 2 * sizeof(uint16_t))) { + return 0; + } + + memcpy(&pdu[offs], local_vmac, BVLC_SC_VMAC_SIZE); + offs += BVLC_SC_VMAC_SIZE; + memcpy(&pdu[offs], local_uuid, BVLC_SC_UUID_SIZE); + offs += BVLC_SC_UUID_SIZE; + memcpy(&pdu[offs], &max_bvlc_len, sizeof(max_bvlc_len)); + offs += sizeof(max_bvlc_len); + memcpy(&pdu[offs], &max_npdu_len, sizeof(max_npdu_len)); + offs += sizeof(max_npdu_len); + return (unsigned int)offs; +} + +/** + * @brief Function decodes the Connect-Accept message according + * BACNet standard AB.2.11.1 Connect-Accept Format. + * @param payload - pointer to the decoded data + * @param packed_payload - pointer to the packed data + * @param packed_payload_len - size of the packed data + * @param error - pointer to the error code + * @param class - pointer to the error class + * @param err_desc - pointer to the error description string + * @return true if the data is decoded successfully, false otherwise + */ +static bool bvlc_sc_decode_connect_accept( + BVLC_SC_DECODED_DATA *payload, + uint8_t *packed_payload, + size_t packed_payload_len, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **err_desc) +{ + if (packed_payload_len < 26) { + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_connect_accept_incomplete; + return false; + } else if (packed_payload_len > 26) { + *error = ERROR_CODE_UNEXPECTED_DATA; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_connect_accept_unexpected; + return false; + } + + payload->connect_accept.vmac = (BACNET_SC_VMAC_ADDRESS *)packed_payload; + packed_payload += BVLC_SC_VMAC_SIZE; + payload->connect_accept.uuid = (BACNET_SC_UUID *)packed_payload; + packed_payload += BVLC_SC_UUID_SIZE; + memcpy( + &payload->connect_accept.max_bvlc_len, packed_payload, + sizeof(payload->connect_accept.max_bvlc_len)); + packed_payload += sizeof(payload->connect_accept.max_bvlc_len); + memcpy( + &payload->connect_accept.max_npdu_len, packed_payload, + sizeof(payload->connect_accept.max_npdu_len)); + return true; +} + +/** + * @brief Function encodes the Disconnect-Request message according + * BACNet standard AB.2.12.1 Disconnect-Request Format. + * + * BVLC Function 1-octet (X'08') Advertisement + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 Absent, is always for + * connection peer node. + * Destination Virtual Address 0 If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_disconnect_request( + uint8_t *pdu, size_t pdu_len, uint16_t message_id) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_DISCONNECT_REQUEST, message_id, NULL, NULL); + return offs; +} + +/** + * @brief Function encodes the Disconnect-ACK message according + * BACNet standard AB.2.13.1 Disconnect-ACK Format. + * + * BVLC Function 1-octet (X'09') Advertisement + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 Absent, is always for + * connection peer node. + * Destination Virtual Address 0 If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t +bvlc_sc_encode_disconnect_ack(uint8_t *pdu, size_t pdu_len, uint16_t message_id) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_DISCONNECT_ACK, message_id, NULL, NULL); + return offs; +} + +/** + * @brief Function encodes the Heartbeat-Request message according + * BACNet standard AB.2.14.1 Heartbeat-Request Format. + * + * BVLC Function 1-octet (X'0A') Advertisement + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 Absent, is always for + * connection peer node. + * Destination Virtual Address 0 If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_heartbeat_request( + uint8_t *pdu, size_t pdu_len, uint16_t message_id) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_HEARTBEAT_REQUEST, message_id, NULL, NULL); + return offs; +} + +/** + * @brief Function encodes the Heartbeat-ACK message according + * BACNet standard AB.2.15.1 Heartbeat-ACK Format. + * + * BVLC Function 1-octet (X'0B') Advertisement + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 Absent, is always for + * connection peer node. + * Destination Virtual Address 0 If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t +bvlc_sc_encode_heartbeat_ack(uint8_t *pdu, size_t pdu_len, uint16_t message_id) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_HEARTBEAT_ACK, message_id, NULL, NULL); + return offs; +} + +/** + * @brief Function encodes the Proprietary Message according BACNet standard + * AB.2.16.1 Proprietary Message Format. + * + * BVLC Function 1-octet (X'0C') Proprietary-Message. + * Control Flags 1-octet Control flags. + * Message ID 2-octets The message identifier of the + * message for which this message + * is the result. + * Originating Virtual Address 0 or 6-octets If absent, message is from + * connection peer node + * Destination Virtual Address 0 or 6-octets If absent, message is for + * connection peer node + * Destination Options Variable Optional, 0 to N header options + * Data Options 0-octets Shall be absent. + * Payload 3-N octets The payload shall consist of at + * least the vendor identifier and + * the proprietary function octet. + * Vendor identifier 2-octets Vendor Identifier, with most + * significant octet first, of the + * organization defining this + * message. + * Proprietary Function 1-octet The vendor-defined function code. + * + * Proprietary Data Variable Optional vendor-defined payload + * data. + * @param pdu - A buffer to store the encoded pdu. + * @param pdu_len - Size of the buffer to store the encoded pdu. + * @param message_id- The message identifier + * @param origin - Originating virtual address, can be NULL + * @param dest - Destination virtual address, can be NULL + * @param vendor_id - vendor identifier. + * @param proprietary_function - The vendor-defined function code + * @param proprietary_data - buffer which holds optional vendor-defined + * payload data. + * @param proprietary_data_len - length of proprietary_data buffer. + * @return number of bytes encoded, in a case of error returns 0. + */ +size_t bvlc_sc_encode_proprietary_message( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint16_t vendor_id, + uint8_t proprietary_function, + uint8_t *proprietary_data, + size_t proprietary_data_len) +{ + size_t offs; + + offs = bvlc_sc_encode_common( + pdu, pdu_len, BVLC_SC_PROPRIETARY_MESSAGE, message_id, origin, dest); + if (!offs) { + return 0; + } + + if (pdu_len < (offs + sizeof(vendor_id) + sizeof(proprietary_function) + + proprietary_data_len)) { + return 0; + } + + memcpy(&pdu[offs], &vendor_id, sizeof(vendor_id)); + offs += sizeof(vendor_id); + memcpy(&pdu[offs], &proprietary_function, sizeof(proprietary_function)); + offs += sizeof(proprietary_function); + memcpy(&pdu[offs], proprietary_data, proprietary_data_len); + offs += proprietary_data_len; + return (unsigned int)offs; +} + +/** + * @brief Function decodes the Proprietary Message according BACNet standard + * AB.2.16.1 Proprietary Message Format. + * @param payload - pointer to the decoded data + * @param packed_payload - pointer to the packed data + * @param packed_payload_len - size of the packed data + * @param error - pointer to the error code + * @param class - pointer to the error class + * @param err_desc - pointer to the error description string + * @return true if the data is decoded successfully, false otherwise + */ +static bool bvlc_sc_decode_proprietary( + BVLC_SC_DECODED_DATA *payload, + uint8_t *packed_payload, + size_t packed_payload_len, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **err_desc) +{ + if (packed_payload_len < 3) { + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_proprietary_incomplete; + return false; + } + + memcpy( + &payload->proprietary.vendor_id, packed_payload, + sizeof(payload->proprietary.vendor_id)); + packed_payload += sizeof(payload->proprietary.vendor_id); + payload->proprietary.function = packed_payload[0]; + packed_payload++; + if (packed_payload_len - 3 > 0) { + payload->proprietary.data = packed_payload; + payload->proprietary.data_len = packed_payload_len - 3; + } else { + payload->proprietary.data = NULL; + payload->proprietary.data_len = 0; + } + return true; +} + +/** + * @brief Decode the BVLC-SC header + * @param message - buffer with the message + * @param message_len - length of the message + * @param hdr - pointer to the decoded header + * @param error - pointer to the error code + * @param class - pointer to the error class + * @param err_desc - pointer to the error description string + * @return true if the data is decoded successfully, false otherwise + */ +static bool bvlc_sc_decode_hdr( + uint8_t *message, + size_t message_len, + BVLC_SC_DECODED_HDR *hdr, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **err_desc) +{ + size_t offs = 4; + bool ret = false; + size_t hdr_opt_len = 0; + + memset(hdr, 0, sizeof(*hdr)); + + if (message_len < 4) { + /* According EA-001-4 'Clarifying BVLC-Result in BACnet/SC ' */ + /* If a BVLC message is received that has fewer than four octets, a */ + /* BVLC-Result NAK shall not be returned. */ + /* The message shall be discarded and not be processed. */ + *error = ERROR_CODE_DISCARD; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = NULL; + return ret; + } + + hdr->bvlc_function = message[0]; + memcpy(&hdr->message_id, &message[2], sizeof(hdr->message_id)); + + if (message[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + hdr->origin = (BACNET_SC_VMAC_ADDRESS *)&message[offs]; + offs += BVLC_SC_VMAC_SIZE; + if (offs > message_len) { + hdr->origin = NULL; + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_hdr_incomplete1; + return false; + } + } + + if (message[1] & BVLC_SC_CONTROL_DEST_VADDR) { + hdr->dest = (BACNET_SC_VMAC_ADDRESS *)&message[offs]; + offs += BVLC_SC_VMAC_SIZE; + if (offs > message_len) { + hdr->dest = NULL; + *error = ERROR_CODE_MESSAGE_INCOMPLETE; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_hdr_incomplete2; + return false; + } + } + + /* in order to handle correctly item AB.3.1.5 Common Error Situations, */ + /* in a case a BVLC message is received with unknown BVLC function, */ + /* upper layer logic must understand if the message is unicast or not. */ + /* That's why that error handling is put after filling of address fields. */ + + if (message[0] > BVLC_SC_PROPRIETARY_MESSAGE) { + *error = ERROR_CODE_BVLC_FUNCTION_UNKNOWN; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_unknown_bvlc_function; + return ret; + } + + if (message[1] & BVLC_SC_CONTROL_DEST_OPTIONS) { + ret = bvlc_sc_validate_options_headers( + BACNET_PDU_DEST_OPTION_VALIDATION, &message[offs], + message_len - offs, &hdr_opt_len, &hdr->dest_options_num, error, + class, err_desc); + + if (!ret) { + return false; + } + hdr->dest_options = &message[offs]; + hdr->dest_options_len = hdr_opt_len; + offs += hdr_opt_len; + } + + if (message[1] & BVLC_SC_CONTROL_DATA_OPTIONS) { + ret = bvlc_sc_validate_options_headers( + BACNET_PDU_DATA_OPTION_VALIDATION, &message[offs], + message_len - offs, &hdr_opt_len, &hdr->data_options_num, error, + class, err_desc); + if (!ret) { + return false; + } + hdr->data_options = &message[offs]; + hdr->data_options_len = hdr_opt_len; + offs += hdr_opt_len; + } + + if (message_len - offs <= 0) { + /* no payload */ + return true; + } + + hdr->payload = &message[offs]; + hdr->payload_len = message_len - offs; + return true; +} + +/** + * @brief Function decodes the BACnet SC header options. + * @param option_array - pointer to the array of decoded options + * @param options_list - pointer to the packed options list + * @return true if the data is decoded successfully, false otherwise + */ +static bool bvlc_sc_decode_header_options( + BVLC_SC_DECODED_HDR_OPTION *option_array, uint8_t *options_list) +{ + uint8_t *next_option = options_list; + size_t i = 0; + + while (next_option) { + bvlc_sc_decode_option_hdr( + options_list, &option_array[i].type, + &option_array[i].must_understand, &next_option); + + option_array[i].packed_header_marker = options_list[0]; + + if (option_array[i].type == BVLC_SC_OPTION_TYPE_PROPRIETARY) { + bvlc_sc_decode_proprietary_option( + options_list, &option_array[i].specific.proprietary.vendor_id, + &option_array[i].specific.proprietary.option_type, + &option_array[i].specific.proprietary.data, + &option_array[i].specific.proprietary.data_len); + } + i++; + options_list = next_option; + } + + return true; +} + +/** + * @brief Function decodes the BACnet SC header options if they exist. + * @param message - pointer to the decoded message + */ +static void +bvlc_sc_decode_dest_options_if_exists(BVLC_SC_DECODED_MESSAGE *message) +{ + if (message->hdr.dest_options) { + bvlc_sc_decode_header_options( + message->dest_options, message->hdr.dest_options); + } +} + +/** + * @brief Function decodes the BACnet SC header options if they exist. + * @param message - pointer to the decoded message + */ +static void +bvlc_sc_decode_data_options_if_exists(BVLC_SC_DECODED_MESSAGE *message) +{ + if (message->hdr.data_options) { + bvlc_sc_decode_header_options( + message->data_options, message->hdr.data_options); + } +} + +/** + * @brief Function decodes BACNet/SC message. + * + * @param buf - A buffer which holds BACNet/SC PDU. + * @param buf_len - length of a buffer which holds BACNet/SC PDU. + * @param message- pointer to structure for decoded data. + * @param error - the value of parameter is filled if function returns false. + * Check BACNET_ERROR_CLASS enum. + * @param class - the value of parameter is filled if function returns false. + * heck BACNET_ERROR_CODE enum. + * @return true if PDU was successfully decoded otherwise returns false, + * and error and class parameters are filled with corresponded codes. + */ +bool bvlc_sc_decode_message( + uint8_t *buf, + size_t buf_len, + BVLC_SC_DECODED_MESSAGE *message, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **err_desc) +{ + if (!message || !buf_len || !error || !class || !buf || !err_desc) { + return false; + } + + memset(message->data_options, 0, sizeof(message->data_options)); + memset(message->dest_options, 0, sizeof(message->dest_options)); + memset(&message->payload, 0, sizeof(message->payload)); + + if (!bvlc_sc_decode_hdr( + buf, buf_len, &message->hdr, error, class, err_desc)) { + return false; + } + + if (message->hdr.dest_options) { + if (message->hdr.dest_options_num > BVLC_SC_HEADER_OPTION_MAX) { + /* dest header options list is too long */ + *error = ERROR_CODE_OUT_OF_MEMORY; + *class = ERROR_CLASS_RESOURCES; + *err_desc = s_dest_options_list_too_long; + return false; + } + } + + if (message->hdr.data_options) { + if (message->hdr.data_options_num > BVLC_SC_HEADER_OPTION_MAX) { + /* data header options list is too long */ + *error = ERROR_CODE_OUT_OF_MEMORY; + *class = ERROR_CLASS_RESOURCES; + *err_desc = s_data_options_list_too_long; + return false; + } + } + + switch (message->hdr.bvlc_function) { + case BVLC_SC_RESULT: { + if (message->hdr.data_options) { + /* The BVLC-Result message must not have data options */ + *error = ERROR_CODE_INCONSISTENT_PARAMETERS; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_result_unexpected_data_options; + return false; + } + + if (!message->hdr.payload || !message->hdr.payload_len) { + *error = ERROR_CODE_PAYLOAD_EXPECTED; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_result_payload_expected; + return false; + } + + bvlc_sc_decode_dest_options_if_exists(message); + + if (!bvlc_sc_decode_result( + &message->payload, message->hdr.payload, + message->hdr.payload_len, error, class, err_desc)) { + return false; + } + break; + } + case BVLC_SC_ENCAPSULATED_NPDU: { + if (!message->hdr.payload || !message->hdr.payload_len) { + *error = ERROR_CODE_PAYLOAD_EXPECTED; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_encapsulated_npdu_payload_expected; + return false; + } + + bvlc_sc_decode_dest_options_if_exists(message); + bvlc_sc_decode_data_options_if_exists(message); + + message->payload.encapsulated_npdu.npdu = message->hdr.payload; + message->payload.encapsulated_npdu.npdu_len = + message->hdr.payload_len; + break; + } + case BVLC_SC_ADDRESS_RESOLUTION: { + if (message->hdr.data_options) { + /* The Address-Resolution message must not have data options */ + *error = ERROR_CODE_INCONSISTENT_PARAMETERS; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_address_resolution_data_options; + return false; + } + + if (message->hdr.payload || message->hdr.payload_len) { + /* According EA-001-4 'Clarifying BVLC-Result in BACnet/SC */ + /* If a BVLC message is received that is longer than expected, + */ + /* a BVLC-Result NAK shall be returned if it was a unicast */ + /* message, indicating an 'Error Class' of COMMUNICATION and */ + /* 'Error Code' of UNEXPECTED_DATA. The message shall be */ + /* discarded and not be processed. */ + + *error = ERROR_CODE_UNEXPECTED_DATA; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_address_resolution_unexpected; + return false; + } + + bvlc_sc_decode_dest_options_if_exists(message); + break; + } + case BVLC_SC_ADDRESS_RESOLUTION_ACK: { + if (message->hdr.data_options) { + /* The Address-Resolution Ack message must not have data options + */ + *error = ERROR_CODE_INCONSISTENT_PARAMETERS; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_address_resolution_ack_data_options; + return false; + } + + bvlc_sc_decode_dest_options_if_exists(message); + + if (message->hdr.payload_len) { + message->payload.address_resolution_ack + .utf8_websocket_uri_string = message->hdr.payload; + } else { + message->payload.address_resolution_ack + .utf8_websocket_uri_string = NULL; + } + message->payload.address_resolution_ack + .utf8_websocket_uri_string_len = message->hdr.payload_len; + break; + } + case BVLC_SC_ADVERTISIMENT: { + if (message->hdr.data_options) { + /* The advertisiment message must not have data options */ + *error = ERROR_CODE_INCONSISTENT_PARAMETERS; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_advertisiment_data_options; + return false; + } + + if (!message->hdr.payload || !message->hdr.payload_len) { + *error = ERROR_CODE_PAYLOAD_EXPECTED; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_advertisiment_payload_expected; + return false; + } + + bvlc_sc_decode_dest_options_if_exists(message); + + if (!bvlc_sc_decode_advertisiment( + &message->payload, message->hdr.payload, + message->hdr.payload_len, error, class, err_desc)) { + return false; + } + break; + } + case BVLC_SC_ADVERTISIMENT_SOLICITATION: { + if (message->hdr.data_options) { + /* The advertisiment solicitation message must not have data + * options */ + *error = ERROR_CODE_INCONSISTENT_PARAMETERS; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_advertisiment_solicitation_data_options; + return false; + } + + if (message->hdr.payload || message->hdr.payload_len) { + *error = ERROR_CODE_UNEXPECTED_DATA; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_advertisiment_solicitation_payload_expected; + return false; + } + + bvlc_sc_decode_dest_options_if_exists(message); + break; + } + case BVLC_SC_CONNECT_REQUEST: + case BVLC_SC_CONNECT_ACCEPT: + case BVLC_SC_DISCONNECT_REQUEST: + case BVLC_SC_DISCONNECT_ACK: + case BVLC_SC_HEARTBEAT_REQUEST: + case BVLC_SC_HEARTBEAT_ACK: { + if (message->hdr.origin != 0) { + *error = ERROR_CODE_HEADER_ENCODING_ERROR; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_origin_unexpected; + return false; + } + + if (message->hdr.dest != 0) { + *error = ERROR_CODE_HEADER_ENCODING_ERROR; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_dest_unexpected; + return false; + } + + if (message->hdr.data_options) { + *error = ERROR_CODE_INCONSISTENT_PARAMETERS; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_data_option_unexpected; + return false; + } + + if (message->hdr.bvlc_function == BVLC_SC_CONNECT_REQUEST || + message->hdr.bvlc_function == BVLC_SC_CONNECT_ACCEPT) { + if (!message->hdr.payload || !message->hdr.payload_len) { + *error = ERROR_CODE_PAYLOAD_EXPECTED; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_absent_payload; + return false; + } + } else { + if (message->hdr.payload || message->hdr.payload_len) { + *error = ERROR_CODE_UNEXPECTED_DATA; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_message_too_long; + return false; + } + } + + bvlc_sc_decode_dest_options_if_exists(message); + + if (message->hdr.bvlc_function == BVLC_SC_CONNECT_REQUEST) { + if (!bvlc_sc_decode_connect_request( + &message->payload, message->hdr.payload, + message->hdr.payload_len, error, class, err_desc)) { + return false; + } + } else if (message->hdr.bvlc_function == BVLC_SC_CONNECT_ACCEPT) { + if (!bvlc_sc_decode_connect_accept( + &message->payload, message->hdr.payload, + message->hdr.payload_len, error, class, err_desc)) { + return false; + } + } + break; + } + case BVLC_SC_PROPRIETARY_MESSAGE: { + if (message->hdr.data_options) { + /* The proprietary message must not have data options */ + *error = ERROR_CODE_INCONSISTENT_PARAMETERS; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_proprietary_data_options; + return false; + } + + if (!message->hdr.payload || !message->hdr.payload_len) { + *error = ERROR_CODE_PAYLOAD_EXPECTED; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_proprietary_payload; + return false; + } + + bvlc_sc_decode_dest_options_if_exists(message); + + if (!bvlc_sc_decode_proprietary( + &message->payload, message->hdr.payload, + message->hdr.payload_len, error, class, err_desc)) { + return false; + } + break; + } + default: + *error = ERROR_CODE_BVLC_FUNCTION_UNKNOWN; + *class = ERROR_CLASS_COMMUNICATION; + *err_desc = s_unknown_bvlc_function; + return false; + } + return true; +} + +/** + * @brief Function removes destination address of BACNet/SC message + * and sets originating address instead of it. + * It does it job only if message has destination address + * and does not have origination address, otherwise pdu + * stays unchanged. + * @param pdu - BACNet/SC PDU. + * @param pdu_len - length of a buffer which holds BACNet/SC PDU. + * @param orig- origination vmac. + */ +void bvlc_sc_remove_dest_set_orig( + uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig) +{ + size_t offs = 4; + if (pdu && pdu_len > 4) { + if (!(pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) && + (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR)) { + pdu[1] &= ~(BVLC_SC_CONTROL_DEST_VADDR); + pdu[1] |= BVLC_SC_CONTROL_ORIG_VADDR; + memcpy(&pdu[offs], orig, sizeof(*orig)); + } + } +} + +/** + * @brief Function changes or adds originating address into BACNet/SC message. + * It is assumed that ppdu points to buffer with pdu that has + * BSC_PRE bytes behind. + * @param ppdu - pointer to buffer which holds BACNet/SC PDU. + * @param pdu_len - length of a buffer which holds BACNet/SC PDU. + * @param orig- origination vmac. + * @return new pdu length if function succeeded and ppdu points to beginning of + * changed pdu, otherwise returns old pdu_len and ppdu is not changed. + */ +size_t +bvlc_sc_set_orig(uint8_t **ppdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig) +{ + uint8_t buf[BSC_PRE]; + uint8_t *pdu = *ppdu; + size_t offs = 4; + if (pdu && pdu_len > 4) { + if (pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + memcpy(&pdu[offs], orig, sizeof(orig->address)); + return pdu_len; + } else { + memcpy(buf, pdu, 4); + buf[1] |= BVLC_SC_CONTROL_ORIG_VADDR; + memcpy(&buf[4], orig, sizeof(orig->address)); + memcpy(pdu - BVLC_SC_VMAC_SIZE, buf, BVLC_SC_VMAC_SIZE + 4); + *ppdu = pdu - BVLC_SC_VMAC_SIZE; + return pdu_len + BVLC_SC_VMAC_SIZE; + } + } else { + return pdu_len; + } +} + +/** + * @brief Function checks if vmac address is broadcast. + * @param vmac - pointer vmac address. + * @return true if vmac is broadcast. otherwise returns false. + */ +bool bvlc_sc_is_vmac_broadcast(BACNET_SC_VMAC_ADDRESS *vmac) +{ + int i; + for (i = 0; i < BVLC_SC_VMAC_SIZE; i++) { + if (vmac->address[i] != 0xFF) { + return false; + } + } + return true; +} + +/** + * @brief Function checks if it is needed to send BVLC result + * response message for given decoded BACNet/SC message. + * In a case of errors, standard requires to send such kind + * of responses for unicast messages of specific types. + * @param dm - pointer to decoded BACNet/SC message. + * @return true if vmac is broadcast, otherwise returns false. + */ +bool bvlc_sc_need_send_bvlc_result(BVLC_SC_DECODED_MESSAGE *dm) +{ + if (dm->hdr.dest == NULL || !bvlc_sc_is_vmac_broadcast(dm->hdr.dest)) { + if (dm->hdr.bvlc_function == BVLC_SC_CONNECT_REQUEST || + dm->hdr.bvlc_function == BVLC_SC_DISCONNECT_REQUEST || + dm->hdr.bvlc_function == BVLC_SC_ENCAPSULATED_NPDU || + dm->hdr.bvlc_function == BVLC_SC_ADDRESS_RESOLUTION || + dm->hdr.bvlc_function == BVLC_SC_ADVERTISIMENT_SOLICITATION || + dm->hdr.bvlc_function == BVLC_SC_HEARTBEAT_REQUEST || + dm->hdr.bvlc_function > BVLC_SC_PROPRIETARY_MESSAGE) { + return true; + } + } + return false; +} + +/** + * @brief Function checks if destination address of input BACNet/SC + * message is broadcast. + * @param pdu- buffer with BACNet/SC message. + * @param pdu_len- length of buffer of BACNet/SC message. + * @return true if destination address of input BACNet/SC + * is broadcast, otherwise returns false. + */ +bool bvlc_sc_pdu_has_dest_broadcast(uint8_t *pdu, size_t pdu_len) +{ + size_t offs = 4; + + if (pdu_len >= 4) { + if (pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + if (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR && + (offs + BVLC_SC_VMAC_SIZE) <= pdu_len) { + return bvlc_sc_is_vmac_broadcast( + (BACNET_SC_VMAC_ADDRESS *)&pdu[offs]); + } + } + return false; +} + +/** + * @brief Function checks if input BACNet/SC message has + * destination address field. + * @param pdu- buffer with BACNet/SC message. + * @param pdu_len- length of buffer of BACNet/SC message. + * @return true if destination address is presented in + * input BACNet/SC message, otherwise returns false. + */ +bool bvlc_sc_pdu_has_no_dest(uint8_t *pdu, size_t pdu_len) +{ + if (pdu_len >= 4) { + if (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR) { + return false; + } + } + return true; +} + +/** + * @brief Function puts destination address of + * BACNet/SC message into vmac if message + * contains it. + * @param pdu- buffer with BACNet/SC message. + * @param pdu_len- length of buffer of BACNet/SC message. + * @return true if destination address is presented and was + * placed into vmac, otherwise returns false. + */ +bool bvlc_sc_pdu_get_dest( + uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *vmac) +{ + size_t offs = 4; + + if (pdu_len >= 4) { + if (pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + if (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR && + (offs + BVLC_SC_VMAC_SIZE) <= pdu_len) { + memcpy(&vmac->address[0], &pdu[offs], BVLC_SC_VMAC_SIZE); + return true; + } + } + return false; +} + +/** + * @brief Function removes originating and destination + * address fields from input BACNet/SC message. + * @param ppdu- pointer to buffer of BACNet/SC message. + * @param pdu_len- length of buffer of BACNet/SC message. + * @return new length of changed pdu if originating or destination + * addresses were removed or old pdu length if + * pdu was not changed. If pdu was changed, ppdu contains + * updated pointer to buffer to modified BACNet/SC message. + */ +size_t bvlc_sc_remove_orig_and_dest(uint8_t **ppdu, size_t pdu_len) +{ + uint8_t *pdu = *ppdu; + size_t offs = 4; + + if (pdu_len > 4) { + if (pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + if (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + pdu[1] &= ~(BVLC_SC_CONTROL_ORIG_VADDR); + pdu[1] &= ~(BVLC_SC_CONTROL_DEST_VADDR); + memmove(&pdu[offs - 4], pdu, 4); + *ppdu = &pdu[offs - 4]; + return pdu_len - offs + 4; + } + return pdu_len; +} diff --git a/src/bacnet/datalink/bsc/bvlc-sc.h b/src/bacnet/datalink/bsc/bvlc-sc.h new file mode 100644 index 00000000..7e62c5d3 --- /dev/null +++ b/src/bacnet/datalink/bsc/bvlc-sc.h @@ -0,0 +1,425 @@ +/** + * @file + * @brief API for encoding/decoding of BACnet/SC BVLC messages + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_BVLC_SC_H +#define BACNET_DATALINK_BSC_BVLC_SC_H +#include +#include +#include +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/npdu.h" + +#ifndef BVLC_SC_NPDU_SIZE_CONF +#define BVLC_SC_NPDU_SIZE 1440 +#else +#define BVLC_SC_NPDU_SIZE BVLC_SC_NPDU_SIZE_CONF +#endif + +#define BVLC_SC_NPDU_MAX_SIZE \ + 61327 /* Table 6-1. NPDU Lengths of BACnet Data Link Layers */ +#define BVLC_SC_VMAC_SIZE 6 +#define BVLC_SC_UUID_SIZE 16 +#define BSC_PRE (2 * BVLC_SC_VMAC_SIZE) + +#if !defined(USER_DEFINED_BVLC_SC_HEADER_OPTION_MAX) +#define BVLC_SC_HEADER_OPTION_MAX \ + 4 /* though BACNet standard does not limit number of option headers \ + the implementation defines max value */ +#else +#define BVLC_SC_HEADER_OPTION_MAX USER_DEFINED_BVLC_SC_HEADER_OPTION_MAX +#endif + +#if BVLC_SC_NPDU_SIZE > BVLC_SC_NPDU_MAX_SIZE +#error \ + "Maximum NPDU Length on BACNet/SC Data Link must be <= BVLC_SC_NPDU_MAX_SIZE" +#endif + +/* + * BACnet/SC BVLC Messages (functions) (AB.2 BACnet/SC Virtual Link Layer + * Messages) + */ + +typedef enum BVLC_SC_Message_Type { + BVLC_SC_RESULT = 0x00, + BVLC_SC_ENCAPSULATED_NPDU = 0x01, + BVLC_SC_ADDRESS_RESOLUTION = 0x02, + BVLC_SC_ADDRESS_RESOLUTION_ACK = 0x03, + BVLC_SC_ADVERTISIMENT = 0x04, + BVLC_SC_ADVERTISIMENT_SOLICITATION = 0x05, + BVLC_SC_CONNECT_REQUEST = 0x06, + BVLC_SC_CONNECT_ACCEPT = 0x07, + BVLC_SC_DISCONNECT_REQUEST = 0x08, + BVLC_SC_DISCONNECT_ACK = 0x09, + BVLC_SC_HEARTBEAT_REQUEST = 0x0a, + BVLC_SC_HEARTBEAT_ACK = 0x0b, + BVLC_SC_PROPRIETARY_MESSAGE = 0x0c +} BVLC_SC_MESSAGE_TYPE; + +/* + * AB.2.2 Control Flags + */ + +#define BVLC_SC_CONTROL_DATA_OPTIONS (1 << 0) +#define BVLC_SC_CONTROL_DEST_OPTIONS (1 << 1) +#define BVLC_SC_CONTROL_DEST_VADDR (1 << 2) +#define BVLC_SC_CONTROL_ORIG_VADDR (1 << 3) + +/* + * AB.2.3 Header Options + */ + +#define BVLC_SC_HEADER_DATA (1 << 5) +#define BVLC_SC_HEADER_MUST_UNDERSTAND (1 << 6) +#define BVLC_SC_HEADER_MORE (1 << 7) +#define BVLC_SC_HEADER_OPTION_TYPE_MASK (0x1F) + +/** + * BACnet SC VMAC Address + * + * B.1.5.2 VMAC Addressing of Nodes + * For the BVLC message exchange, BACnet/SC nodes are identified by their + * 6-octet virtual MAC address as defined in Clause H.7.3. + * For broadcast BVLC messages that need to reach all nodes of the BACnet/SC + * network, the destination VMAC address shall be the non-EUI-48 value + * X'FFFFFFFFFFFF', referred to as the Local Broadcast VMAC address. + * The reserved EUI-48 value X'000000000000' is not used by this data link + * and therefore can be used internally to indicate that a VMAC is unknown + * or uninitialized. + * @{ + */ + +typedef struct BACnet_SC_VMAC_Address { + uint8_t address[BVLC_SC_VMAC_SIZE]; +} BACNET_SC_VMAC_ADDRESS; +/** @} */ + +/** + * BACnet SC UUID + * AB.1.5.3 Device UUID + * Every BACnet device that supports one or more BACnet/SC network ports shall + * have a Universally Unique ID (UUID) as defined in RFC 4122. This UUID + * identifies the device regardless of its current VMAC address or device + * instance number and is referred to as the device UUID. + * This device UUID shall be generated before first deployment of the device in + * an installation, shall be persistently stored across device restarts, and + * shall not change over the entire lifetime of a device. + * If a device is replaced in an installation, the new device is not required + * to reuse the UUID of the replaced device. For BACnet/SC, it is assumed + * that existing connections to the device being replaced are all terminated + * before the new device comes into operation. + * @{ + */ + +typedef struct BACnet_SC_Uuid { + uint8_t uuid[BVLC_SC_UUID_SIZE]; +} BACNET_SC_UUID; +/** @} */ + +/* + * AB.2.3.1 Secure Path Header Option + */ +typedef enum BVLC_SC_Option_Type { + BVLC_SC_OPTION_TYPE_SECURE_PATH = 1, + BVLC_SC_OPTION_TYPE_PROPRIETARY = 31 +} BVLC_SC_OPTION_TYPE; + +typedef enum BVLC_SC_Direct_Connection_Support { + BVLC_SC_DIRECT_CONNECTION_ACCEPT_UNSUPPORTED = 0, + BVLC_SC_DIRECT_CONNECTION_ACCEPT_SUPPORTED = 1, + BVLC_SC_DIRECT_CONNECTION_SUPPORT_MAX = 1 +} BVLC_SC_DIRECT_CONNECTION_SUPPORT; + +typedef struct BVLC_SC_Decoded_Hdr { + uint8_t bvlc_function; + uint16_t message_id; + BACNET_SC_VMAC_ADDRESS *origin; + BACNET_SC_VMAC_ADDRESS *dest; + uint8_t *dest_options; /* pointer to packed dest options list in message */ + size_t dest_options_len; + size_t dest_options_num; /* number of filled items in dest_options */ + uint8_t *data_options; /* pointer to packed data options list in message */ + size_t data_options_len; + size_t data_options_num; /* number of filled items in data_options */ + uint8_t *payload; /* packed payload, points to data in message */ + size_t payload_len; +} BVLC_SC_DECODED_HDR; + +typedef struct BVLC_SC_Decoded_Result { + uint8_t bvlc_function; + uint8_t result; + uint8_t error_header_marker; + uint16_t error_class; + uint16_t error_code; + uint8_t *utf8_details_string; /* NOTE!: this is utf 8 string without + trailing zero */ + size_t utf8_details_string_len; +} BVLC_SC_DECODED_RESULT; + +typedef struct BVLC_SC_Decoded_Address_Resolution_Ack { + uint8_t *utf8_websocket_uri_string; /* NOTE!: this is utf 8 string without + trailing zero */ + size_t utf8_websocket_uri_string_len; +} BVLC_SC_DECODED_ADDRESS_RESOLUTION_ACK; + +typedef struct BVLC_SC_Decoded_Ecapsulated_NPDU { + uint8_t *npdu; + size_t npdu_len; +} BVLC_SC_DECODED_ENCAPSULATED_NPDU; + +typedef struct BVLC_SC_Decoded_Advertisiment { + BACNET_SC_HUB_CONNECTOR_STATE hub_status; + BVLC_SC_DIRECT_CONNECTION_SUPPORT support; + uint16_t max_bvlc_len; + uint16_t max_npdu_len; +} BVLC_SC_DECODED_ADVERTISIMENT; + +typedef struct BVLC_SC_Decoded_Connect_Request { + BACNET_SC_VMAC_ADDRESS *vmac; + BACNET_SC_UUID *uuid; + uint16_t max_bvlc_len; + uint16_t max_npdu_len; +} BVLC_SC_DECODED_CONNECT_REQUEST; + +typedef struct BVLC_SC_Decoded_Connect_Accept { + BACNET_SC_VMAC_ADDRESS *vmac; + BACNET_SC_UUID *uuid; + uint16_t max_bvlc_len; + uint16_t max_npdu_len; +} BVLC_SC_DECODED_CONNECT_ACCEPT; + +typedef struct BVLC_SC_Decoded_Proprietary { + uint16_t vendor_id; + uint8_t function; + uint8_t *data; + size_t data_len; +} BVLC_SC_DECODED_PROPRIETARY; + +typedef union BVLC_SC_Decoded_Data { + BVLC_SC_DECODED_RESULT result; + BVLC_SC_DECODED_ENCAPSULATED_NPDU encapsulated_npdu; + BVLC_SC_DECODED_ADDRESS_RESOLUTION_ACK address_resolution_ack; + BVLC_SC_DECODED_ADVERTISIMENT advertisiment; + BVLC_SC_DECODED_CONNECT_REQUEST connect_request; + BVLC_SC_DECODED_CONNECT_ACCEPT connect_accept; + BVLC_SC_DECODED_PROPRIETARY proprietary; +} BVLC_SC_DECODED_DATA; + +typedef struct BVLC_SC_Decoded_Hdr_Proprietary_Option { + uint16_t vendor_id; + uint8_t option_type; + uint8_t *data; + size_t data_len; +} BVLC_SC_DECODED_HDR_PROPRIETARY_OPTION; + +typedef union BVLC_SC_Decoded_Specific_Option_Data { + BVLC_SC_DECODED_HDR_PROPRIETARY_OPTION proprietary; +} BVLC_SC_DECODED_SPECIFIC_OPTION_DATA; + +typedef struct BVLC_SC_Decoded_Hdr_Option { + uint8_t packed_header_marker; + BVLC_SC_OPTION_TYPE type; + bool must_understand; + BVLC_SC_DECODED_SPECIFIC_OPTION_DATA specific; +} BVLC_SC_DECODED_HDR_OPTION; + +typedef struct BVLC_SC_Decoded_Message { + BVLC_SC_DECODED_HDR hdr; + BVLC_SC_DECODED_HDR_OPTION data_options[BVLC_SC_HEADER_OPTION_MAX]; + BVLC_SC_DECODED_HDR_OPTION dest_options[BVLC_SC_HEADER_OPTION_MAX]; + BVLC_SC_DECODED_DATA payload; +} BVLC_SC_DECODED_MESSAGE; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +BACNET_STACK_EXPORT +size_t bvlc_sc_add_option_to_destination_options( + uint8_t *out_pdu, + size_t out_pdu_size, + uint8_t *pdu, + size_t pdu_size, + uint8_t *sc_option, + size_t sc_option_len); + +BACNET_STACK_EXPORT +size_t bvlc_sc_add_option_to_data_options( + uint8_t *out_pdu, + size_t out_pdu_size, + uint8_t *pdu, + size_t pdu_size, + uint8_t *sc_option, + size_t sc_option_len); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_proprietary_option( + uint8_t *pdu, + size_t pdu_size, + bool must_understand, + uint16_t vendor_id, + uint8_t proprietary_option_type, + uint8_t *proprietary_data, + size_t proprietary_data_len); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_secure_path_option( + uint8_t *pdu, size_t pdu_size, bool must_understand); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_result( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t bvlc_function, + uint8_t result_code, + uint8_t *error_header_marker, + uint16_t *error_class, + uint16_t *error_code, + uint8_t *utf8_details_string); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_encapsulated_npdu( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *npdu, + size_t npdu_size); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_address_resolution( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_address_resolution_ack( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *web_socket_uris, + size_t web_socket_uris_len); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_advertisiment( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + BACNET_SC_HUB_CONNECTOR_STATE hub_status, + BVLC_SC_DIRECT_CONNECTION_SUPPORT support, + uint16_t max_bvlc_len, + uint16_t max_npdu_size); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_advertisiment_solicitation( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_connect_request( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *local_vmac, + BACNET_SC_UUID *local_uuid, + uint16_t max_bvlc_len, + uint16_t max_npdu_size); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_connect_accept( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *local_vmac, + BACNET_SC_UUID *local_uuid, + uint16_t max_bvlc_len, + uint16_t max_npdu_len); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_disconnect_request( + uint8_t *pdu, size_t pdu_len, uint16_t message_id); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_disconnect_ack( + uint8_t *pdu, size_t pdu_len, uint16_t message_id); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_heartbeat_request( + uint8_t *out_buf, size_t out_buf_len, uint16_t message_id); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_heartbeat_ack( + uint8_t *out_buf, size_t out_buf_len, uint16_t message_id); + +BACNET_STACK_EXPORT +size_t bvlc_sc_encode_proprietary_message( + uint8_t *pdu, + size_t pdu_len, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint16_t vendor_id, + uint8_t proprietary_function, + uint8_t *proprietary_data, + size_t proprietary_data_len); + +BACNET_STACK_EXPORT +bool bvlc_sc_decode_message( + uint8_t *buf, + size_t buf_len, + BVLC_SC_DECODED_MESSAGE *message, + BACNET_ERROR_CODE *error, + BACNET_ERROR_CLASS *class, + const char **err_desc); + +BACNET_STACK_EXPORT +void bvlc_sc_remove_dest_set_orig( + uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig); + +BACNET_STACK_EXPORT +size_t +bvlc_sc_set_orig(uint8_t **ppdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig); + +BACNET_STACK_EXPORT +bool bvlc_sc_is_vmac_broadcast(BACNET_SC_VMAC_ADDRESS *vmac); + +BACNET_STACK_EXPORT +bool bvlc_sc_need_send_bvlc_result(BVLC_SC_DECODED_MESSAGE *dm); + +BACNET_STACK_EXPORT +bool bvlc_sc_pdu_has_dest_broadcast(uint8_t *pdu, size_t pdu_len); + +BACNET_STACK_EXPORT +bool bvlc_sc_pdu_has_no_dest(uint8_t *pdu, size_t pdu_len); + +BACNET_STACK_EXPORT +bool bvlc_sc_pdu_get_dest( + uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *vmac); + +BACNET_STACK_EXPORT +size_t bvlc_sc_remove_orig_and_dest(uint8_t **ppdu, size_t pdu_len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/src/bacnet/datalink/bsc/websocket.h b/src/bacnet/datalink/bsc/websocket.h new file mode 100644 index 00000000..a2684761 --- /dev/null +++ b/src/bacnet/datalink/bsc/websocket.h @@ -0,0 +1,450 @@ +/** + * @file + * @brief Client/Server thread-safe websocket interface API. + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DATALINK_BSC_WEBSOCKET_H +#define BACNET_DATALINK_BSC_WEBSOCKET_H +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/datalink/bsc/bsc-conf.h" + +/** + * Maximum number of sockets that can be opened on client's side. + * @{ + */ +#ifndef BSC_CONF_CLIENT_CONNECTIONS_NUM +#define BSC_CLIENT_WEBSOCKETS_MAX_NUM 4 +#else +#define BSC_CLIENT_WEBSOCKETS_MAX_NUM BSC_CONF_CLIENT_CONNECTIONS_NUM +#endif + +/** @} */ + +/** + * Maximum number of server instances. Value 10 means that + * 10 hub servers and 10 direct servers can be started. + * @{ + */ + +#ifndef BSC_CONF_WEBSOCKET_SERVERS_NUM +#define BSC_CONF_WEBSOCKET_SERVERS_NUM 1 +#endif + +/** @} */ + +/** @} */ + +/** + * Maximum number of sockets supported for hub websocket server + * @{ + */ +#ifndef BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM +#define BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM 4 +#else +#define BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM \ + BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM +#endif +/** @} */ + +/** + * Initial size of websocket buffer used on receive. It can be increased + * dynamically depending on received packet size. + * @{ + */ + +#ifndef BSC_CONF_WEBSOCKET_RX_BUFFER_LEN +#define BSC_WEBSOCKET_RX_BUFFER_LEN 512 +#else +#define BSC_WEBSOCKET_RX_BUFFER_LEN BSC_CONF_WEBSOCKET_RX_BUFFER_LEN +#endif +/** @} */ + +/** + * Maximum number of sockets supported for direct websocket server + * @{ + */ +#ifndef BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM +#define BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM 4 +#else +#define BSC_SERVER_DIRECT_WEBSOCKETS_MAX_NUM \ + BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM +#endif + +#ifndef BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN +#define BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN 128 +#else +#define BSC_WEBSOCKET_ERR_DESC_STR_MAX_LEN \ + BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN +#endif + +#define BSC_WSURL_MAX_LEN BSC_CONF_WSURL_MAX_LEN + +typedef int BSC_WEBSOCKET_HANDLE; +typedef void *BSC_WEBSOCKET_SRV_HANDLE; +#define BSC_WEBSOCKET_INVALID_HANDLE (-1) + +/* Websockets protocol defined in BACnet/SC \S AB.7.1. */ +#define BSC_WEBSOCKET_HUB_PROTOCOL_STR "hub.bsc.bacnet.org" +#define BSC_WEBSOCKET_DIRECT_PROTOCOL_STR "dc.bsc.bacnet.org" + +typedef enum { + BSC_WEBSOCKET_HUB_PROTOCOL = 0, + BSC_WEBSOCKET_DIRECT_PROTOCOL = 1, + BSC_WEBSOCKET_PROTOCOLS_AMOUNT = 2 /* must be always last */ +} BSC_WEBSOCKET_PROTOCOL; + +typedef enum { + BSC_WEBSOCKET_SUCCESS = 0, + BSC_WEBSOCKET_NO_RESOURCES = 1, + BSC_WEBSOCKET_BAD_PARAM = 2, + BSC_WEBSOCKET_INVALID_OPERATION = 3 +} BSC_WEBSOCKET_RET; + +typedef enum { + BSC_WEBSOCKET_CONNECTED = 0, + BSC_WEBSOCKET_DISCONNECTED = 1, + BSC_WEBSOCKET_RECEIVED = 2, + BSC_WEBSOCKET_SENDABLE = 3, + BSC_WEBSOCKET_SERVER_STARTED = 4, + BSC_WEBSOCKET_SERVER_STOPPED = 5 +} BSC_WEBSOCKET_EVENT; + +/* + values of ws_reason and ws_reason_desc parameters are actual + only for BSC_WEBSOCKET_DISCONNECTED event. +*/ + +typedef void (*BSC_WEBSOCKET_CLI_DISPATCH)( + BSC_WEBSOCKET_HANDLE h, + BSC_WEBSOCKET_EVENT ev, + BACNET_ERROR_CODE ws_reason, + char *ws_reason_desc, + uint8_t *buf, + size_t bufsize, + void *dispatch_func_user_param); + +typedef void (*BSC_WEBSOCKET_SRV_DISPATCH)( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + BSC_WEBSOCKET_EVENT ev, + BACNET_ERROR_CODE ws_reason, + char *ws_reason_desc, + uint8_t *buf, + size_t bufsize, + void *dispatch_func_user_param); + +/** + * @brief Asynchronous bws_cli_сonnect() function starts establishing + * of a new connection to a websocket server specified by url parameter. + * Result of completion of operation is call of dispatch_func() with + * BSC_WEBSOCKET_CONNECTED in a case if connection established successfully or + * BSC_WEBSOCKET_DISCONNECTED if connection attempt failed. + * + * @param type - type of BACNet/SC connection, check + * BSC_WEBSOCKET_CONNECTION_TYPE enum. According BACNet standard + * different type of connections require different websocket protocols. + * @param url - BACNet/SC server URL. For example: wss://legrand.com:8080. + * @param ca_cert - pointer to certificate authority (CA) cert in PEM or DER + * format. + * @param ca_cert_size - size in bytes of CA cert. + * @param cert - pointer to client certificate in PEM or DER format. + * @param cert_size - size in bytes of client certificate. + * @param key - pointer to client private key in PEM or DER format. + * @param key_size - size of private key in bytes of of client certificate. + * @param timeout_s - timeout for socket operations (connect, etc..) in seconds. + * Must be > 0. + * @param dispatch_func - pointer to dispatch callback function to handle + * events from websocket specified by *out_handle. + * @param dispatch_func_user_param - parameter which is passed into + dispatch_func call. + * @param out_handle - pointer to a websocket handle. + * + * @return error code from BSC_WEBSOCKET_RET enum. + * The following error codes can be returned: + * BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is + * incorrect. + * BSC_WEBSOCKET_NO_RESOURCES - if a user has already opened + * more sockets than the limit defined by BSC_CLIENT_WEBSOCKETS_MAX_NUM, + * or if some mem allocation has failed or some allocation of system + * resources like mutex, thread, etc .., failed. + * BSC_WEBSOCKET_SUCCESS - connect operation was successfully started. + */ + +BSC_WEBSOCKET_RET bws_cli_connect( + BSC_WEBSOCKET_PROTOCOL proto, + char *url, + uint8_t *ca_cert, + size_t ca_cert_size, + uint8_t *cert, + size_t cert_size, + uint8_t *key, + size_t key_size, + size_t timeout_s, + BSC_WEBSOCKET_CLI_DISPATCH dispatch_func, + void *dispatch_func_user_param, + BSC_WEBSOCKET_HANDLE *out_handle); + +/** + * @brief Asynchronous bws_cli_disconnnect() function starts process of + * disconnection for specified websocket handle. When the process completes, + * dispatch_func() with event BSC_WEBSOCKET_DISCONNECTED is called. + * connection to some websocket server. + * + * @param h - websocket handle. + * + */ + +void bws_cli_disconnect(BSC_WEBSOCKET_HANDLE h); + +/** + * @brief Non-blocked bws_cli_send() function signals to the websocket + * specified by websocket handle h that some data is needed to be sent. + * When websocket becomes sendable, dispatch_func() is called with + * event BSC_WEBSOCKET_SENDABLE and data can be sent from dispatch_func() + * call using bws_cli_dispatch_send() call. + * + * @param h - websocket handle. + * + */ + +void bws_cli_send(BSC_WEBSOCKET_HANDLE h); + +/** + * @brief bws_cli_dispatch_send() function sends data to a websocket server + * in a case if websocket handle is sendable (e.g. ready to send data). + * In as case if data was not sent for some reasons thic could result + * dispatch_func() cal with event BSC_WEBSOCKET_DISCONNECTED + * @param h - websocket handle. + * @param payload - pointer to a data to send. It is assumed that + * BSC_CONF_TX_PRE bytes are available before payload. + * So for example you need this kind of code to use + * bws_srv_dispatch_send with a 128-byte payload: + * + * char buf[BSC_CONF_TX_PRE + 128]; + * + * // fill your part of the buffer... for example here + * // it's all zeros + * + * memset(&buf[BSC_CONF_TX_PRE], 0, 128); + * bws_cli_dispatch_send(sh, h, &buf[BSC_CONF_TX_PRE], 128); + * + * @param payload_size - size in bytes of data to send. + * + * @return error code from BSC_WEBSOCKET_RET enum. + * The following error codes can be returned: + * BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is + * incorrect. + * BSC_WEBSOCKET_NO_RESOURCES - if some mem allocation has failed o + * some allocation of system resources like mutex, thread, + * etc .., has failed. + * BSC_WEBSOCKET_INVALID_OPERATION - if the function was called not from + * dispatch_func() callback context or websocket is not in connected + * state. + * BSC_WEBSOCKET_SUCCESS - data is sent successfully. + */ + +BSC_WEBSOCKET_RET bws_cli_dispatch_send( + BSC_WEBSOCKET_HANDLE h, uint8_t *payload, size_t payload_size); + +/** + * @brief Asynchronous bws_srv_start() function triggers process of + * starting of a websocket server on a specified port for specified + * BACNet websocket protocol. At present time peer can have only 2 + * instances of server: onerelates to BSC_WEBSOCKET_HUB_PROTOCOL and + * the other to BSC_WEBSOCKET_HUB_PROTOCOL. When process completes, + * dispatch_func() is called with BSC_WEBSOCKET_SERVER_STARTED + * event. + * + * @param proto - type of BACNet websocket protocol defined in + * BSC_WEBSOCKET_PROTOCOL enum. + * @param port - port number. + * @param iface - name of interface to bind to. If the parameter is NULL + * that means that websocket server binds to all interfaces. + * @param ca_cert - pointer to certificate authority (CA) cert in PEM or DER + * format. + * @param ca_cert_size - size in bytes of CA cert. + * @param cert - pointer to server certificate in PEM or DER format. + * @param cert_size - size in bytes of server certificate. + * @param key - pointer to server private key in PEM or DER format. + * @param key_size - size of private key in bytes of of client certificate. + * @param timeout_s - timeout for socket operations (connect, etc..) in seconds. + * Must be > 0. + * @param dispatch_func - pointer to dispatch callback function to handle + * events from a websocket which is corresponded to + * server specified by proto param. + * @param dispatch_func_user_param - parameter which is passed into + * dispatch_func call. + * @param sh - pointer to receive websocket server handle + * + * @return error code from BSC_WEBSOCKET_RET enum. + * The following error codes can be returned: + * BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is + * incorrect. + * BSC_WEBSOCKET_NO_RESOURCES - if a user has already opened + * more sockets than the limit defined to corresponded protocol + * (BSC_SERVER_HUB_WEBSOCKETS_MAX_NUM or + * BSC_CLIENT_WEBSOCKETS_MAX_NUM), or if some mem allocation + * has failed or some allocation of system resources like + * mutex, thread, condition variable etc .., failed. + * BSC_WEBSOCKET_SUCCESS - the start operation is in progress. + * BSC_WEBSOCKET_INVALID_OPERATION - operation is not started because + * server in a process of shutdown of server is already started. + */ + +BSC_WEBSOCKET_RET bws_srv_start( + BSC_WEBSOCKET_PROTOCOL proto, + int port, + char *iface, + uint8_t *ca_cert, + size_t ca_cert_size, + uint8_t *cert, + size_t cert_size, + uint8_t *key, + size_t key_size, + size_t timeout_s, + BSC_WEBSOCKET_SRV_DISPATCH dispatch_func, + void *dispatch_func_user_param, + BSC_WEBSOCKET_SRV_HANDLE *sh); + +/** + * @brief Asynchronous bws_srv_stop() function starts process of a shutdowns + * of a websocket server specified by proto param. + * opened websocket connections are closed. + * + * @param sh - websocket server handle + * + * @return error code from BSC_WEBSOCKET_RET enum. + * The following error codes can be returned: + * BSC_WEBSOCKET_SUCCESS - the operation is started or server was + * already stopped before. BSC_WEBSOCKET_INVALID_OPERATION - if server was not + * started or server shutdown is already in progress. + */ + +BSC_WEBSOCKET_RET bws_srv_stop(BSC_WEBSOCKET_SRV_HANDLE sh); + +/** + * @brief Asynchronous bws_srv_disconnnect() function starts process of + * disconnection for specified websocket handle h for specified server type + * by proto parameter. When the process completes, dispatch_func() with event + * BSC_WEBSOCKET_DISCONNECTED is called. + * + * @param sh - websocket server handle. + * @param h - websocket handle. + * + */ + +void bws_srv_disconnect(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h); + +/** + * @brief Asynchronous bws_srv_send() function signals to a websocket + * specified by handle h for specified server type by proto param that + * some data is needed to be sent. + * + * When websocket becomes sendable, dispatch_func() is called with + * event BSC_WEBSOCKET_SENDABLE and data can be sent from dispatch_func() + * call using bws_srv_dispatch_send() call. + * + * @param sh - websocket server handle. + * @param h - websocket handle. + * + */ + +void bws_srv_send(BSC_WEBSOCKET_SRV_HANDLE sh, BSC_WEBSOCKET_HANDLE h); + +/** + * @brief bws_srv_dispatch_send() function sends data to a websocket server + * in a case if websocket handle is sendable (e.g. ready to send data). + * In as case if data was not sent for some reasons thic could result + * dispatch_func() call with event BSC_WEBSOCKET_DISCONNECTED + * + * @param sh - websocket server handle. + * @param h - websocket handle. + * @param payload - pointer to a data to send. It is assumed that + * BSC_CONF_TX_PRE bytes are available before payload. + * So for example you need this kind of code to use + * bws_srv_dispatch_send with a 128-byte payload: + * + * char buf[BSC_CONF_TX_PRE + 128]; + * + * // fill your part of the buffer... for example here + * // it's all zeros + * + * memset(&buf[BSC_CONF_TX_PRE], 0, 128); + * bws_srv_dispatch_send(sh, h, &buf[BSC_CONF_TX_PRE], 128); + * + * @param payload_size - size in bytes of data to send. + * + * @return error code from BSC_WEBSOCKET_RET enum. + * The following error codes can be returned: + * BSC_WEBSOCKET_BAD_PARAM - In a case if some input parameter is + * incorrect. + * BSC_WEBSOCKET_NO_RESOURCES - if some mem allocation has failed o + * some allocation of system resources like mutex, thread, + * etc .., has failed. + * BSC_WEBSOCKET_INVALID_OPERATION - if the function was called not from + * dispatch_func() callback context or websocket is not in connected + * state or server in a process of a shutdown or server is not started. + * BSC_WEBSOCKET_SUCCESS - data is sent successfully. + */ + +BSC_WEBSOCKET_RET bws_srv_dispatch_send( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + uint8_t *payload, + size_t payload_size); + +/** + * @brief bws_dispatch_lock() function acquires internal websocket + * global mutex. As a result dispatch_func() callback for all + * websocket server and client instances won't be called until user + * calls bws_dispatch_unlock(). + */ +#ifndef BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED +#define BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED 0 +#endif + +#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1) +void bws_dispatch_lock(void); +#else +extern void bws_dispatch_lock_dbg(char *f, int line); +#define bws_dispatch_lock() bws_dispatch_lock_dbg(__FILE__, __LINE__) +#endif + +/** + * @brief bws_dispatch_unlock() function releases internal websocket + * global mutex. + */ + +#if (BSC_DEBUG_WEBSOCKET_MUTEX_ENABLED != 1) +void bws_dispatch_unlock(void); +#else +extern void bws_dispatch_unlock_dbg(char *f, int line); +#define bws_dispatch_unlock() bws_dispatch_unlock_dbg(__FILE__, __LINE__) +#endif + +/** + * @brief bws_srv_get_peer_ip_addr() gets ipv4 or ipv6 address as ANSI string + * and port of remote peer. + * + * @param sh - websocket server handle. + * @param h - websocket handle. + * @param ip_str - buffer to store null terminated string of ip address. + * @param ip_str_len - size of ip_str buffer + * @param port- pointer to store port of a remote node. + * + * @return true if function succeeded otherwise returns false + * if peer's address information can't be retrieved from + * underlying websocket library. + */ + +bool bws_srv_get_peer_ip_addr( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + uint8_t *ip_str, + size_t ip_str_len, + uint16_t *port); +#endif diff --git a/src/bacnet/datalink/datalink.c b/src/bacnet/datalink/datalink.c index 8ff21208..cf08e5e4 100644 --- a/src/bacnet/datalink/datalink.c +++ b/src/bacnet/datalink/datalink.c @@ -29,6 +29,9 @@ #if defined(BACDL_MSTP) #include "bacnet/datalink/dlmstp.h" #endif +#if defined(BACDL_BSC) +#include "bacnet/datalink/bsc/bsc-datalink.h" +#endif #ifdef HAVE_STRINGS_H #include /* for strcasecmp() */ #endif @@ -39,7 +42,8 @@ static enum { DATALINK_ETHERNET, DATALINK_BIP, DATALINK_BIP6, - DATALINK_MSTP + DATALINK_MSTP, + DATALINK_BSC } Datalink_Transport; void datalink_set(char *datalink_string) @@ -72,6 +76,11 @@ void datalink_set(char *datalink_string) Datalink_Transport = DATALINK_MSTP; } #endif +#if defined(BACDL_BSC) + else if (strcasecmp("bsc", datalink_string) == 0) { + Datalink_Transport = DATALINK_BSC; + } +#endif } bool datalink_init(char *ifname) @@ -106,6 +115,11 @@ bool datalink_init(char *ifname) case DATALINK_MSTP: status = dlmstp_init(ifname); break; +#endif +#if defined(BACDL_BSC) + case DATALINK_BSC: + status = bsc_init(ifname); + break; #endif default: break; @@ -150,6 +164,11 @@ int datalink_send_pdu( case DATALINK_MSTP: bytes = dlmstp_send_pdu(dest, npdu_data, pdu, pdu_len); break; +#endif +#if defined(BACDL_BSC) + case DATALINK_BSC: + bytes = bsc_send_pdu(dest, npdu_data, pdu, pdu_len); + break; #endif default: break; @@ -190,6 +209,11 @@ uint16_t datalink_receive( case DATALINK_MSTP: bytes = dlmstp_receive(src, pdu, max_pdu, timeout); break; +#endif +#if defined(BACDL_BSC) + case DATALINK_BSC: + bytes = bsc_receive(src, pdu, max_pdu, timeout); + break; #endif default: break; @@ -227,6 +251,11 @@ void datalink_cleanup(void) case DATALINK_MSTP: dlmstp_cleanup(); break; +#endif +#if defined(BACDL_BSC) + case DATALINK_BSC: + bsc_cleanup(); + break; #endif default: break; @@ -262,6 +291,11 @@ void datalink_get_broadcast_address(BACNET_ADDRESS *dest) case DATALINK_MSTP: dlmstp_get_broadcast_address(dest); break; +#endif +#if defined(BACDL_BSC) + case DATALINK_BSC: + bsc_get_broadcast_address(dest); + break; #endif default: break; @@ -297,6 +331,11 @@ void datalink_get_my_address(BACNET_ADDRESS *my_address) case DATALINK_MSTP: dlmstp_get_my_address(my_address); break; +#endif +#if defined(BACDL_BSC) + case DATALINK_BSC: + bsc_get_my_address(my_address); + break; #endif default: break; @@ -333,6 +372,11 @@ void datalink_set_interface(char *ifname) case DATALINK_MSTP: (void)ifname; break; +#endif +#if defined(BACDL_BSC) + case DATALINK_BSC: + (void)ifname; + break; #endif default: break; @@ -365,6 +409,11 @@ void datalink_maintenance_timer(uint16_t seconds) #if defined(BACDL_MSTP) case DATALINK_MSTP: break; +#endif +#if defined(BACDL_BSC) + case DATALINK_BSC: + bsc_maintenance_timer(seconds); + break; #endif default: break; diff --git a/src/bacnet/datalink/datalink.h b/src/bacnet/datalink/datalink.h index 09f46af5..ff0dae74 100644 --- a/src/bacnet/datalink/datalink.h +++ b/src/bacnet/datalink/datalink.h @@ -32,6 +32,11 @@ #include "bacnet/basic/bbmd6/h_bbmd6.h" #endif +#if defined(BACDL_BSC) +#include "bacnet/datalink/bsc/bsc-conf.h" +#include "bacnet/datalink/bsc/bsc-datalink.h" +#endif + #if defined(BACDL_ETHERNET) && !defined(BACDL_MULTIPLE) #define MAX_MPDU ETHERNET_MPDU_MAX @@ -99,6 +104,17 @@ void routed_get_my_address(BACNET_ADDRESS *my_address); #define datalink_get_my_address bip6_get_my_address #define datalink_maintenance_timer(s) bvlc6_maintenance_timer(s) +#elif defined(BACDL_BSC) && !defined(BACDL_MULTIPLE) +#define MAX_MPDU BVLC_SC_NPDU_SIZE_CONF + +#define datalink_init bsc_init +#define datalink_send_pdu bsc_send_pdu +#define datalink_receive bsc_receive +#define datalink_cleanup bsc_cleanup +#define datalink_get_broadcast_address bsc_get_broadcast_address +#define datalink_get_my_address bsc_get_my_address +#define datalink_maintenance_timer(s) bsc_maintenance_timer(s) + #elif !defined(BACDL_TEST) /* Multiple, none or custom datalink */ #include "bacnet/npdu.h" diff --git a/src/bacnet/datalink/dlenv.c b/src/bacnet/datalink/dlenv.c index ce859c3a..df3116d0 100644 --- a/src/bacnet/datalink/dlenv.c +++ b/src/bacnet/datalink/dlenv.c @@ -26,6 +26,14 @@ #if (BACNET_PROTOCOL_REVISION >= 17) #include "bacnet/basic/object/netport.h" #endif +#if defined(BACDL_BSC) +#include "bacnet/basic/object/bacfile.h" +#include "bacnet/basic/object/sc_netport.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" +#include "bacnet/datalink/bsc/bsc-util.h" +#include "bacnet/datalink/bsc/bsc-datalink.h" +#include "bacnet/datalink/bsc/bsc-event.h" +#endif /** @file dlenv.c Initialize the DataLink configuration. */ /* timer used to renew Foreign Device Registration */ @@ -459,6 +467,179 @@ void dlenv_network_port_init(void) since they are already set */ Network_Port_Changes_Pending_Set(instance, false); } +#elif defined(BACDL_BSC) +/** + * @brief Datalink network port object settings + * @param primary_hub_uri + * @param failover_hub_uri + * @param filename_ca_1_cert + * @param filename_ca_2_cert + * @param filename_cert + * @param filename_key + * @param direct_connect_port + * @param hub_function_port + * @param direct_connect_initiate + * @param direct_connect_accept_urls + */ +static void bacnet_secure_connect_network_port_init( + char *primary_hub_uri, + char *failover_hub_uri, + char *filename_ca_1_cert, + char *filename_ca_2_cert, + char *filename_cert, + char *filename_key, + char *direct_binding, + char *hub_binding, + char *direct_connect_initiate, + char *direct_connect_accept_urls) +{ + const uint32_t instance = 1; + BACNET_SC_UUID uuid = { 0 }; + BACNET_SC_VMAC_ADDRESS vmac = { 0 }; + long seed; + char c; + + seed = (long)&instance; + srand((int)seed); + Network_Port_Object_Instance_Number_Set(0, instance); + Network_Port_Name_Set(instance, "BACnet/BSC Port"); + Network_Port_Type_Set(instance, PORT_TYPE_BSC); + + bsc_generate_random_uuid(&uuid); + Network_Port_SC_Local_UUID_Set(instance, (BACNET_UUID *)&uuid); + bsc_generate_random_vmac(&vmac); + Network_Port_MAC_Address_Set(instance, vmac.address, sizeof(vmac)); + + /* common NP data */ + Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED); + Network_Port_Out_Of_Service_Set(instance, false); + Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN); + Network_Port_APDU_Length_Set(instance, MAX_APDU); + Network_Port_Network_Number_Set(instance, 0); + + /* SC parameters */ + Network_Port_Max_BVLC_Length_Accepted_Set(instance, SC_NETPORT_BVLC_MAX); + Network_Port_Max_NPDU_Length_Accepted_Set(instance, SC_NETPORT_NPDU_MAX); + Network_Port_SC_Connect_Wait_Timeout_Set( + instance, SC_NETPORT_CONNECT_TIMEOUT); + Network_Port_SC_Heartbeat_Timeout_Set( + instance, SC_NETPORT_HEARTBEAT_TIMEOUT); + Network_Port_SC_Disconnect_Wait_Timeout_Set( + instance, SC_NETPORT_DISCONNECT_TIMEOUT); + Network_Port_SC_Maximum_Reconnect_Time_Set( + instance, SC_NETPORT_RECONNECT_TIME); + + if (filename_ca_1_cert == NULL) { + fprintf(stderr, "BACNET_SC_ISSUER_1_CERTIFICATE_FILE must be set\n"); + return; + } + bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE); + bacfile_pathname_set( + BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE, filename_ca_1_cert); + Network_Port_Issuer_Certificate_File_Set( + instance, 0, BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE); + + if (filename_ca_2_cert) { + bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE); + bacfile_pathname_set( + BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE, filename_ca_2_cert); + Network_Port_Issuer_Certificate_File_Set( + instance, 1, BSC_ISSUER_CERTIFICATE_FILE_2_INSTANCE); + } + + if (filename_cert == NULL) { + fprintf(stderr, "BACNET_SC_OPERATIONAL_CERTIFICATE_FILE must be set\n"); + return; + } + bacfile_create(BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE); + bacfile_pathname_set( + BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE, filename_cert); + Network_Port_Operational_Certificate_File_Set( + instance, BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE); + + if (filename_key == NULL) { + fprintf( + stderr, + "BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE must be set\n"); + return; + } + bacfile_create(BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE); + bacfile_pathname_set( + BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE, filename_key); + Network_Port_Certificate_Key_File_Set( + instance, BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE); + + if ((primary_hub_uri == NULL) && (failover_hub_uri == NULL) && + (direct_binding == NULL) && (hub_binding == NULL)) { + fprintf( + stderr, + "At least must be set:\n" + "BACNET_SC_HUB_FUNCTION_BINDING for HUB or\n" + "BACNET_SC_PRIMARY_HUB_URI and BACNET_SC_FAILOVER_HUB_URI for node " + "or\n" + "BACNET_SC_DIRECT_CONNECT_BINDING for direct connect.\n"); + return; + } + + Network_Port_SC_Primary_Hub_URI_Set(instance, primary_hub_uri); + Network_Port_SC_Failover_Hub_URI_Set(instance, failover_hub_uri); + + Network_Port_SC_Direct_Connect_Binding_Set(instance, direct_binding); + Network_Port_SC_Direct_Connect_Accept_Enable_Set( + instance, direct_binding != NULL); + + c = direct_connect_initiate ? direct_connect_initiate[0] : '0'; + if ((c != '0') && (c != 'n') && (c != 'N')) { + Network_Port_SC_Direct_Connect_Initiate_Enable_Set(instance, true); + } else { + Network_Port_SC_Direct_Connect_Initiate_Enable_Set(instance, false); + } + + Network_Port_SC_Direct_Connect_Accept_URIs_Set( + instance, direct_connect_accept_urls); + + /* HUB parameters */ + Network_Port_SC_Hub_Function_Binding_Set(instance, hub_binding); + Network_Port_SC_Hub_Function_Enable_Set(instance, hub_binding != NULL); + + /* last thing - clear pending changes - we don't want to set these + since they are already set */ + Network_Port_Changes_Pending_Set(instance, false); +} + +static bool dlenv_hub_connection_status_check(void) +{ + uint32_t instance = Network_Port_Index_To_Instance(0); + BACNET_SC_HUB_CONNECTION_STATUS *status; + + status = Network_Port_SC_Primary_Hub_Connection_Status(instance); + if (status && status->State == BACNET_SC_CONNECTION_STATE_CONNECTED) { + return true; + } + + status = Network_Port_SC_Failover_Hub_Connection_Status(instance); + if (status && status->State == BACNET_SC_CONNECTION_STATE_CONNECTED) { + return true; + } + + return false; +} + +/** + * Datalink network port object settings for BACnet/SC + */ +void dlenv_network_port_init(void) +{ + /* if a user has configured BACnet/SC port with primary hub URI, */ + /* wait for a establishin of a connection to BACnet/SC hub at first */ + /* to reduce possibility of packet losses. */ + if (Network_Port_SC_Primary_Hub_URI_char(1)) { + while (!dlenv_hub_connection_status_check()) { + bsc_wait(1); + bsc_maintenance_timer(1); + } + } +} #else /** * Datalink network port object settings @@ -548,6 +729,24 @@ void dlenv_maintenance_timer(uint16_t elapsed_seconds) * - BACNET_BIP6_PORT - UDP/IP port number (0..65534) used for BACnet/IPv6 * communications. Default is 47808 (0xBAC0). * - BACNET_BIP6_BROADCAST - FF05::BAC0 or FF02::BAC0 or ... + * - BACDL_BSC: (BACnet Secure Connect) + * - BACNET_SC_PRIMARY_HUB_URI + * - BACNET_SC_FAILOVER_HUB_URI + * - BACNET_SC_ISSUER_1_CERTIFICATE_FILE + * - BACNET_SC_ISSUER_2_CERTIFICATE_FILE + * - BACNET_SC_OPERATIONAL_CERTIFICATE_FILE + * - BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE + * - BACNET_SC_DIRECT_CONNECT_BINDING - pair: interface name (optional) and + * TCP/IP port number (0..65534), like "50000" (port only) or + * "eth0:50000"(both) + * - BACNET_SC_HUB_FUNCTION_BINDING - pair: interface name (optional) and + * TCP/IP port number (0..65534), like "50000" (port only) or + * "eth0:50000"(both) + * - BACNET_SC_DIRECT_CONNECT_INITIATE - if true equal "1", "y", "Y", + * otherwise false + * - BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS - list of direct connect accept URLs + * separated by a space character, e.g. + * "wss://192.0.0.1:40000 wss://192.0.0.2:6666" */ void dlenv_init(void) { @@ -574,6 +773,8 @@ void dlenv_init(void) datalink_set("ethernet"); #elif defined(BACDL_ARCNET) datalink_set("arcnet"); +#elif defined(BACDL_BSC) + datalink_set("bsc"); #else datalink_set("none"); #endif @@ -665,6 +866,35 @@ void dlenv_init(void) } else { dlmstp_set_mac_address(127); } +#elif defined(BACDL_BSC) + char *primary_hub_uri; + char *failover_hub_uri; + char *filename_ca_1_cert; + char *filename_ca_2_cert; + char *filename_cert; + char *filename_key; + char *direct_binding; + char *hub_binding; + char *direct_connect_initiate; + char *direct_connect_accept_urls; + + primary_hub_uri = getenv("BACNET_SC_PRIMARY_HUB_URI"); + failover_hub_uri = getenv("BACNET_SC_FAILOVER_HUB_URI"); + filename_ca_1_cert = getenv("BACNET_SC_ISSUER_1_CERTIFICATE_FILE"); + filename_ca_2_cert = getenv("BACNET_SC_ISSUER_2_CERTIFICATE_FILE"); + filename_cert = getenv("BACNET_SC_OPERATIONAL_CERTIFICATE_FILE"); + filename_key = getenv("BACNET_SC_OPERATIONAL_CERTIFICATE_PRIVATE_KEY_FILE"); + direct_binding = getenv("BACNET_SC_DIRECT_CONNECT_BINDING"); + hub_binding = getenv("BACNET_SC_HUB_FUNCTION_BINDING"); + direct_connect_initiate = getenv("BACNET_SC_DIRECT_CONNECT_INITIATE"); + direct_connect_accept_urls = getenv("BACNET_SC_DIRECT_CONNECT_ACCEPT_URLS"); + bacnet_secure_connect_network_port_init( + primary_hub_uri, failover_hub_uri, filename_ca_1_cert, + filename_ca_2_cert, filename_cert, filename_key, direct_binding, + hub_binding, direct_connect_initiate, direct_connect_accept_urls); + if (!bsc_cert_files_check()) { + exit(1); + } #endif pEnv = getenv("BACNET_APDU_TIMEOUT"); if (pEnv) { diff --git a/src/bacnet/proplist.c b/src/bacnet/proplist.c index db844c3e..ed6524ab 100644 --- a/src/bacnet/proplist.c +++ b/src/bacnet/proplist.c @@ -391,6 +391,8 @@ static const int Properties_BACnetARRAY[] = { PROP_WEEKLY_SCHEDULE, PROP_EXCEPTION_SCHEDULE, PROP_TAGS, + PROP_ISSUER_CERTIFICATE_FILES, + PROP_SC_HUB_FUNCTION_ACCEPT_URIS, -1 }; diff --git a/src/bacnet/secure_connect.c b/src/bacnet/secure_connect.c new file mode 100644 index 00000000..d6c8a4e4 --- /dev/null +++ b/src/bacnet/secure_connect.c @@ -0,0 +1,1191 @@ +/** + * @file + * @brief BACnet Secure Connect Encoding and Decoding + * @author Mikhail Antropov + * @author Steve Karg + * @date 2023 + * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/bacapp.h" +#include "bacnet/bacint.h" +#include "bacnet/datetime.h" +#include "bacnet/secure_connect.h" + +/** + * @brief convert a BACNET_HOST_N_PORT to a BACNET_HOST_N_PORT_DATA + * @param peer - the structure that holds the data to be converted + * @param peer_data - the structure to hold the converted data + */ +static void host_n_port_to_data( + BACNET_HOST_N_PORT *peer, BACNET_HOST_N_PORT_DATA *peer_data) +{ + peer_data->type = (peer->host_ip_address ? BACNET_HOST_N_PORT_IP : 0) + + (peer->host_name ? BACNET_HOST_N_PORT_HOST : 0); + + if (peer->host_ip_address) { + octetstring_copy_value( + (uint8_t *)peer_data->host, 6, &peer->host.ip_address); + } else if (peer->host_name) { + characterstring_ansi_copy( + peer_data->host, sizeof(peer_data->host), &peer->host.name); + } else { + peer_data->host[0] = 0; + } + + peer_data->port = peer->port; +} + +/** + * @brief Convert BACNET_HOST_N_PORT_DATA to BACNET_HOST_N_PORT + * @param peer_data - the structure that holds the data to be converted + * @param peer - the structure to hold the converted data + */ +static void host_n_port_from_data( + const BACNET_HOST_N_PORT_DATA *peer_data, BACNET_HOST_N_PORT *peer) +{ + peer->host_ip_address = peer_data->type & BACNET_HOST_N_PORT_IP; + peer->host_name = peer_data->type & BACNET_HOST_N_PORT_HOST; + + if (peer->host_ip_address) { + octetstring_init(&peer->host.ip_address, (uint8_t *)peer_data->host, 6); + } else if (peer->host_name) { + characterstring_init_ansi(&peer->host.name, peer_data->host); + } + + peer->port = peer_data->port; +} + +/** + * @brief Encode a SCHubConnection complex data type + * + * BACnetSCHubConnection ::= SEQUENCE { + * connection-state [0] BACnetSCConnectionState, + * connect-timestamp [1] BACnetDateTime, + * disconnect-timestamp [2] BACnetDateTime, + * error [3] Error OPTIONAL, + * error-details [4] CharacterString OPTIONAL + * } + * + * @param apdu - the APDU buffer, or NULL for length + * @param address - IP address and port number + * @return length of the encoded APDU buffer + */ +int bacapp_encode_SCHubConnection( + uint8_t *apdu, const BACNET_SC_HUB_CONNECTION_STATUS *value) +{ + int apdu_len = 0, len = 0; + BACNET_CHARACTER_STRING str; + + if (value) { + len = encode_context_enumerated(apdu, 0, value->State); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = + bacapp_encode_context_datetime(apdu, 1, &value->Connect_Timestamp); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = bacapp_encode_context_datetime( + apdu, 2, &value->Disconnect_Timestamp); + apdu_len += len; + if (apdu) { + apdu += len; + } + if (value->State == + BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS || + value->State == BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT) { + len = encode_context_enumerated(apdu, 3, value->Error); + apdu_len += len; + if (apdu) { + apdu += len; + } + if (characterstring_init_ansi(&str, value->Error_Details)) { + len = encode_context_character_string(apdu, 4, &str); + apdu_len += len; + } + } + } + + return apdu_len; +} + +/** + * @brief Decode a SCHubConnection complex data type + * BACnetSCHubConnection ::= SEQUENCE { + * connection-state [0] BACnetSCConnectionState, + * connect-timestamp [1] BACnetDateTime, + * disconnect-timestamp [2] BACnetDateTime, + * error [3] Error OPTIONAL, + * error-details [4] CharacterString OPTIONAL + * @param apdu - the APDU buffer + * @param apdu_size - the length of the APDU buffer + * @param value - the structure to hold the decoded data + * @return number of bytes decoded of the APDU buffer, or -1 on error + */ +int bacapp_decode_SCHubConnection( + const uint8_t *apdu, + size_t apdu_size, + BACNET_SC_HUB_CONNECTION_STATUS *value) +{ + int apdu_len = 0; + int len; + BACNET_CHARACTER_STRING str; + BACNET_DATE_TIME datetime; + uint32_t ui32; + + /* connection-state [0] BACnetSCConnectionState */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &ui32); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (ui32 > BACNET_SC_CONNECTION_STATE_MAX) { + return BACNET_STATUS_ERROR; + } else { + if (value) { + value->State = (BACNET_SC_CONNECTION_STATE)ui32; + } + } + /* connect-timestamp [1] BACnetDateTime */ + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &datetime); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + datetime_copy(&value->Connect_Timestamp, &datetime); + } + /* disconnect-timestamp [2] BACnetDateTime */ + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &datetime); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + datetime_copy(&value->Disconnect_Timestamp, &datetime); + } + /* error [3] Error OPTIONAL */ + /* error-details [4] CharacterString OPTIONAL */ + if (value) { + /* default values when omitted */ + value->Error = ERROR_CODE_DEFAULT; + value->Error_Details[0] = 0; + } + if (apdu_size > apdu_len) { + /* error [3] Error OPTIONAL */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &ui32); + if (len > 0) { + apdu_len += len; + if (ui32 > ERROR_CODE_PROPRIETARY_LAST) { + return BACNET_STATUS_ERROR; + } else { + if (value) { + value->Error = (BACNET_ERROR_CODE)ui32; + } + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* OPTIONAL - skip and do not increment apdu_len */ + } + /* error-details [4] CharacterString OPTIONAL */ + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 4, &str); + if (len > 0) { + apdu_len += len; + if (value) { + characterstring_ansi_copy( + value->Error_Details, sizeof(value->Error_Details), &str); + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* OPTIONAL - skip and do not increment apdu_len */ + } + } + + return apdu_len; +} + +/** + * @brief Encode a SCHubFunctionConnection complex data type + * + * BACnetSCHubFunctionConnection ::= SEQUENCE { + * connection-state [0] BACnetSCConnectionState, + * connect-timestamp [1] BACnetDateTime, + * disconnect-timestamp [2] BACnetDateTime, + * peer-address [3] BACnetHostNPort, + * peer-vmac [4] OCTET STRING (SIZE(6)), + * peer-uuid [5] OCTET STRING (SIZE(16)), + * error [6] Error OPTIONAL, + * error-details [7] CharacterString OPTIONAL + * } + * + * @param apdu - the APDU buffer, or NULL for length + * @param address - IP address and port number + * @return length of the encoded APDU buffer + */ +int bacapp_encode_SCHubFunctionConnection( + uint8_t *apdu, const BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *value) +{ + int apdu_len = 0, len = 0; + BACNET_CHARACTER_STRING str; + BACNET_OCTET_STRING octet; + BACNET_HOST_N_PORT hp; + + if (value) { + len = encode_context_enumerated(apdu, 0, value->State); + if (apdu) { + apdu += len; + } + apdu_len += len; + len = + bacapp_encode_context_datetime(apdu, 1, &value->Connect_Timestamp); + if (apdu) { + apdu += len; + } + apdu_len += len; + len = bacapp_encode_context_datetime( + apdu, 2, &value->Disconnect_Timestamp); + if (apdu) { + apdu += len; + } + apdu_len += len; + host_n_port_from_data(&value->Peer_Address, &hp); + len = host_n_port_context_encode(apdu, 3, &hp); + if (apdu) { + apdu += len; + } + apdu_len += len; + if (octetstring_init( + &octet, value->Peer_VMAC, sizeof(value->Peer_VMAC))) { + len = encode_context_octet_string(apdu, 4, &octet); + } else { + return BACNET_STATUS_ERROR; + } + if (apdu) { + apdu += len; + } + apdu_len += len; + if (octetstring_init( + &octet, value->Peer_UUID.uuid.uuid128, + sizeof(value->Peer_UUID.uuid.uuid128))) { + len = encode_context_octet_string(apdu, 5, &octet); + } else { + return BACNET_STATUS_ERROR; + } + if (apdu) { + apdu += len; + } + apdu_len += len; + if (value->State == + BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS || + value->State == BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT) { + len = encode_context_enumerated(apdu, 6, value->Error); + if (apdu) { + apdu += len; + } + apdu_len += len; + if (characterstring_init_ansi(&str, value->Error_Details)) { + len = encode_context_character_string(apdu, 7, &str); + apdu_len += len; + } + } + } + + return apdu_len; +} + +/** + * @brief Decode a SCHubFunctionConnection complex data type + * + * BACnetSCHubFunctionConnection ::= SEQUENCE { + * connection-state [0] BACnetSCConnectionState, + * connect-timestamp [1] BACnetDateTime, + * disconnect-timestamp [2] BACnetDateTime, + * peer-address [3] BACnetHostNPort, + * peer-vmac [4] OCTET STRING (SIZE(6)), + * peer-uuid [5] OCTET STRING (SIZE(16)), + * error [6] Error OPTIONAL, + * error-details [7] CharacterString OPTIONAL + * } + * + * @param apdu - the APDU buffer + * @param apdu_size - the length of the APDU buffer + * @param value - the structure to hold the decoded data + * @return number of bytes decoded of the APDU buffer, or -1 on error + */ +int bacapp_decode_SCHubFunctionConnection( + const uint8_t *apdu, + size_t apdu_size, + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *value) +{ + int apdu_len = 0; + int len; + BACNET_CHARACTER_STRING str; + BACNET_OCTET_STRING octet; + BACNET_HOST_N_PORT hp; + BACNET_DATE_TIME datetime; + uint32_t ui32; + + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &ui32); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + value->State = (BACNET_SC_CONNECTION_STATE)ui32; + } + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &datetime); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + datetime_copy(&value->Connect_Timestamp, &datetime); + } + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &datetime); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + datetime_copy(&value->Disconnect_Timestamp, &datetime); + } + len = host_n_port_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, NULL, &hp); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + host_n_port_to_data(&hp, &value->Peer_Address); + } + len = bacnet_octet_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 4, &octet); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + octetstring_copy_value( + value->Peer_VMAC, sizeof(value->Peer_VMAC), &octet); + } + len = bacnet_octet_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 5, &octet); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + octetstring_copy_value( + value->Peer_UUID.uuid.uuid128, + sizeof(value->Peer_UUID.uuid.uuid128), &octet); + } + /* error [6] Error OPTIONAL */ + /* error-details [7] CharacterString OPTIONAL */ + if (value) { + /* default values when omitted */ + value->Error = ERROR_CODE_DEFAULT; + value->Error_Details[0] = 0; + } + if (apdu_size > apdu_len) { + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 6, &ui32); + if (len > 0) { + apdu_len += len; + if (value) { + value->Error = (BACNET_ERROR_CODE)ui32; + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* OPTIONAL - skip and do not increment apdu_len */ + } + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 7, &str); + if (len > 0) { + apdu_len += len; + if (value) { + characterstring_ansi_copy( + value->Error_Details, sizeof(value->Error_Details), &str); + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* OPTIONAL - skip and do not increment apdu_len */ + } + } + + return (apdu_len <= apdu_size) ? apdu_len : -1; +} + +/** + * @brief Encode a SCFailedConnectionRequest complex data type + * + * BACnetSCFailedConnectionRequest ::= SEQUENCE { + * timestamp [0] BACnetDateTime, + * peer-address [1] BACnetHostNPort, + * peer-vmac [2] OCTET STRING (SIZE(6)) OPTIONAL, + * peer-uuid [3] OCTET STRING (SIZE(16)) OPTIONAL, + * error [4] Error, + * error-details [5] CharacterString OPTIONAL + * } + * + * @param apdu - the APDU buffer, or NULL for length + * @param address - IP address and port number + * @return length of the encoded APDU buffer + */ +int bacapp_encode_SCFailedConnectionRequest( + uint8_t *apdu, const BACNET_SC_FAILED_CONNECTION_REQUEST *value) +{ + int apdu_len = 0, len = 0; + BACNET_CHARACTER_STRING str; + BACNET_OCTET_STRING octet; + BACNET_HOST_N_PORT hp; + + if (value) { + len = bacapp_encode_context_datetime(apdu, 0, &value->Timestamp); + if (apdu) { + apdu += len; + } + apdu_len += len; + host_n_port_from_data(&value->Peer_Address, &hp); + len = host_n_port_context_encode(apdu, 1, &hp); + if (apdu) { + apdu += len; + } + apdu_len += len; + if (octetstring_init( + &octet, value->Peer_VMAC, sizeof(value->Peer_VMAC))) { + len = encode_context_octet_string(apdu, 2, &octet); + } else { + return BACNET_STATUS_ERROR; + } + if (apdu) { + apdu += len; + } + apdu_len += len; + if (octetstring_init( + &octet, value->Peer_UUID.uuid.uuid128, + sizeof(value->Peer_UUID.uuid.uuid128))) { + len = encode_context_octet_string(apdu, 3, &octet); + } else { + return BACNET_STATUS_ERROR; + } + if (apdu) { + apdu += len; + } + apdu_len += len; + if (value->Error != ERROR_CODE_DEFAULT) { + len = encode_context_enumerated(apdu, 4, value->Error); + if (apdu) { + apdu += len; + } + apdu_len += len; + + if (characterstring_init_ansi(&str, value->Error_Details)) { + len = encode_context_character_string(apdu, 5, &str); + apdu_len += len; + } + } + } + + return apdu_len; +} + +/** + * @brief Decode a SCFailedConnectionRequest complex data type + * @param apdu - the APDU buffer + * @param apdu_size - the length of the APDU buffer + * @param value - the structure to hold the decoded data + * @return number of bytes decoded of the APDU buffer, or -1 on error + */ +int bacapp_decode_SCFailedConnectionRequest( + const uint8_t *apdu, + size_t apdu_size, + BACNET_SC_FAILED_CONNECTION_REQUEST *value) +{ + int apdu_len = 0; + int len; + BACNET_CHARACTER_STRING str; + BACNET_OCTET_STRING octet; + BACNET_HOST_N_PORT hp; + BACNET_DATE_TIME bdatetime; + uint32_t ui32; + + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &bdatetime); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + datetime_copy(&value->Timestamp, &bdatetime); + } + len = host_n_port_context_decode(&apdu[apdu_len], apdu_size, 1, NULL, &hp); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + host_n_port_to_data(&hp, &value->Peer_Address); + } + len = bacnet_octet_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &octet); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + octetstring_copy_value( + value->Peer_VMAC, sizeof(value->Peer_VMAC), &octet); + } + len = bacnet_octet_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &octet); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + octetstring_copy_value( + value->Peer_UUID.uuid.uuid128, + sizeof(value->Peer_UUID.uuid.uuid128), &octet); + } + if (apdu_size > apdu_len) { + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 4, &ui32); + if (len > 0) { + apdu_len += len; + if (value) { + value->Error = (BACNET_ERROR_CODE)ui32; + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* OPTIONAL - skip and do not increment apdu_len */ + } + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 5, &str); + if (len > 0) { + apdu_len += len; + if (value) { + characterstring_ansi_copy( + value->Error_Details, sizeof(value->Error_Details), &str); + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* OPTIONAL - skip and do not increment apdu_len */ + } + } + + return apdu_len; +} + +/** + * @brief Encode a SCFailedConnectionRequest complex data type + * + * BACnetRouterEntry ::= SEQUENCE { + * network-number [0] Unsigned16, + * mac-address [1] OCTET STRING, + * status [2] ENUMERATED { + * available (0), + * busy (1), + * disconnected (2) + * }, + * performance-index [3] Unsigned8 OPTIONAL + * } + * + * @param apdu - the APDU buffer, or NULL for length + * @param address - IP address and port number + * @return length of the encoded APDU buffer + */ +int bacapp_encode_RouterEntry(uint8_t *apdu, const BACNET_ROUTER_ENTRY *value) +{ + int apdu_len = 0, len = 0; + BACNET_OCTET_STRING octet; + BACNET_UNSIGNED_INTEGER v; + + if (value) { + v = value->Network_Number; + len = encode_context_unsigned(apdu, 0, v); + if (apdu) { + apdu += len; + } + apdu_len += len; + if (octetstring_init( + &octet, value->Mac_Address, sizeof(value->Mac_Address))) { + len = encode_context_octet_string(apdu, 1, &octet); + } else { + return BACNET_STATUS_ERROR; + } + if (apdu) { + apdu += len; + } + apdu_len += len; + len = encode_context_enumerated(apdu, 2, value->Status); + if (apdu) { + apdu += len; + } + apdu_len += len; + if (value->Performance_Index != 0) { + v = value->Performance_Index; + len = encode_context_unsigned(apdu, 3, v); + apdu_len += len; + } + } + + return apdu_len; +} + +/** + * @brief Decode a BACnetRouterEntry complex data type + * @param apdu - the APDU buffer + * @param apdu_size - the length of the APDU buffer + * @param value - the structure to hold the decoded data + * @return number of bytes decoded of the APDU buffer, or -1 on error + */ +int bacapp_decode_RouterEntry( + const uint8_t *apdu, size_t apdu_size, BACNET_ROUTER_ENTRY *value) +{ + int apdu_len = 0; + int len; + BACNET_OCTET_STRING octet; + BACNET_UNSIGNED_INTEGER v; + uint32_t ui32; + + len = bacnet_unsigned_context_decode(&apdu[0], apdu_size, 0, &v); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + value->Network_Number = (uint16_t)v; + } + len = bacnet_octet_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &octet); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + octetstring_copy_value( + value->Mac_Address, sizeof(value->Mac_Address), &octet); + } + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &ui32); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (ui32 > BACNET_ROUTER_STATUS_MAX) { + return BACNET_STATUS_ERROR; + } else { + if (value) { + value->Status = (BACNET_ROUTER_STATUS)ui32; + } + } + /* performance-index [3] Unsigned8 OPTIONAL */ + if (value) { + value->Performance_Index = 0; + } + if (apdu_size > apdu_len) { + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &v); + if (len > 0) { + apdu_len += len; + if (value) { + value->Performance_Index = (uint8_t)v; + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* OPTIONAL - skip and do not increment apdu_len */ + } + } + + return apdu_len; +} + +/** + * @brief Encode a SCDirectConnection complex data type + * + * * BACnetSCDirectConnection ::= SEQUENCE { + * uri [0] CharacterString, + * connection-state [1] BACnetSCConnectionState, + * connect-timestamp [2] BACnetDateTime, + * disconnect-timestamp [3] BACnetDateTime, + * peer-address [4] BACnetHostNPort OPTIONAL, + * peer-vmac [5] OCTET STRING (SIZE(6)) OPTIONAL, + * peer-uuid [6] OCTET STRING (SIZE(16)) OPTIONAL, + * error [7] Error OPTIONAL, + * error-details [8] CharacterString OPTIONAL + * } + * + * @param apdu - the APDU buffer, or NULL for length + * @param address - IP address and port number + * @return length of the encoded APDU buffer + */ +int bacapp_encode_SCDirectConnection( + uint8_t *apdu, const BACNET_SC_DIRECT_CONNECTION_STATUS *value) +{ + int apdu_len = 0, len = 0; + BACNET_CHARACTER_STRING str; + BACNET_OCTET_STRING octet; + BACNET_HOST_N_PORT hp; + + if (value) { + if (characterstring_init_ansi(&str, value->URI)) { + len = encode_context_character_string(apdu, 0, &str); + } else { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_enumerated(apdu, 1, value->State); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = + bacapp_encode_context_datetime(apdu, 2, &value->Connect_Timestamp); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = bacapp_encode_context_datetime( + apdu, 3, &value->Disconnect_Timestamp); + apdu_len += len; + if (apdu) { + apdu += len; + } + host_n_port_from_data(&value->Peer_Address, &hp); + len = host_n_port_context_encode(apdu, 4, &hp); + apdu_len += len; + if (apdu) { + apdu += len; + } + if (octetstring_init( + &octet, value->Peer_VMAC, sizeof(value->Peer_VMAC))) { + len = encode_context_octet_string(apdu, 5, &octet); + } else { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (apdu) { + apdu += len; + } + if (octetstring_init( + &octet, value->Peer_UUID.uuid.uuid128, + sizeof(value->Peer_UUID.uuid.uuid128))) { + len = encode_context_octet_string(apdu, 6, &octet); + } else { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (apdu) { + apdu += len; + } + if (value->State == + BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS || + value->State == BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT) { + len = encode_context_enumerated(apdu, 7, value->Error); + apdu_len += len; + if (apdu) { + apdu += len; + } + if (characterstring_init_ansi(&str, value->Error_Details)) { + len = encode_context_character_string(apdu, 8, &str); + apdu_len += len; + } + } + } + + return apdu_len; +} + +/** + * @brief Decode a SCDirectConnection complex data type + * @param apdu - the APDU buffer + * @param apdu_size - the length of the APDU buffer + * @param value - the structure to hold the decoded data + * @return number of bytes decoded of the APDU buffer, or -1 on error + */ +int bacapp_decode_SCDirectConnection( + const uint8_t *apdu, + size_t apdu_size, + BACNET_SC_DIRECT_CONNECTION_STATUS *value) +{ + int apdu_len = 0; + int len; + BACNET_CHARACTER_STRING str; + BACNET_OCTET_STRING octet; + BACNET_HOST_N_PORT hp; + BACNET_DATE_TIME datetime; + uint32_t ui32; + + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &str); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + characterstring_ansi_copy(value->URI, sizeof(value->URI), &str); + } + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &ui32); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (ui32 > BACNET_SC_CONNECTION_STATE_MAX) { + return BACNET_STATUS_ERROR; + } else { + if (value) { + value->State = (BACNET_SC_CONNECTION_STATE)ui32; + } + } + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &datetime); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + datetime_copy(&value->Connect_Timestamp, &datetime); + } + + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &datetime); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + datetime_copy(&value->Disconnect_Timestamp, &datetime); + } + len = host_n_port_context_decode(&apdu[apdu_len], apdu_size, 4, NULL, &hp); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + host_n_port_to_data(&hp, &value->Peer_Address); + } + len = bacnet_octet_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 5, &octet); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + octetstring_copy_value( + value->Peer_VMAC, sizeof(value->Peer_VMAC), &octet); + } + len = bacnet_octet_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 6, &octet); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + octetstring_copy_value( + value->Peer_UUID.uuid.uuid128, + sizeof(value->Peer_UUID.uuid.uuid128), &octet); + } + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 7, &ui32); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (ui32 > ERROR_CODE_PROPRIETARY_LAST) { + return BACNET_STATUS_ERROR; + } else { + if (value) { + value->Error = (BACNET_ERROR_CODE)ui32; + } + } + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 8, &str); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (value) { + characterstring_ansi_copy( + value->Error_Details, sizeof(value->Error_Details), &str); + } + + return apdu_len; +} + +/** + * @brief Stringify a BACnetHostNPort complex data type + * @param str - the string buffer + * @param str_len - the length of the string buffer + * @param host_port - the structure holding the data + * @return number of bytes written to the string buffer + */ +static int bacapp_snprintf_host_n_port( + char *str, size_t str_len, const BACNET_HOST_N_PORT_DATA *host_port) +{ + int len; + + len = bacapp_snprintf( + str, str_len, "%u.%u.%u.%u:%u, ", (uint8_t)host_port->host[0], + (uint8_t)host_port->host[1], (uint8_t)host_port->host[2], + (uint8_t)host_port->host[3], host_port->port); + + return len; +} + +/** + * @brief stringify a VMAC + * @param str - the string buffer + * @param str_len - the length of the string buffer + * @param vmac - the VMAC + * @return number of bytes written to the string buffer + */ +static int bacapp_snprintf_vmac(char *str, size_t str_len, const uint8_t *vmac) +{ + return bacapp_snprintf( + str, str_len, "%u.%u.%u.%u.%u.%u, ", vmac[0], vmac[1], vmac[2], vmac[3], + vmac[4], vmac[5]); +} + +/** + * @brief stringify a BACnet_UUID + * @param str - the string buffer + * @param str_len - the length of the string buffer + * @param uuid - the UUID + * @return number of bytes written to the string buffer + */ +static int +bacapp_snprintf_uuid(char *str, size_t str_len, const BACNET_UUID *uuid) +{ + char *uuid_format[2] = { + "%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%" + "2.2x%2.2x%2.2x%2.2x, ", + "%8.8lx-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x," + " " + }; + + return bacapp_snprintf( + str, str_len, uuid_format[sizeof(unsigned int) == 4 ? 0 : 1], + uuid->uuid.guid.time_low, uuid->uuid.guid.time_mid, + uuid->uuid.guid.time_hi_and_version, + uuid->uuid.guid.clock_seq_and_node[0], + uuid->uuid.guid.clock_seq_and_node[1], + uuid->uuid.guid.clock_seq_and_node[2], + uuid->uuid.guid.clock_seq_and_node[3], + uuid->uuid.guid.clock_seq_and_node[4], + uuid->uuid.guid.clock_seq_and_node[5], + uuid->uuid.guid.clock_seq_and_node[6], + uuid->uuid.guid.clock_seq_and_node[7]); +} + +/** + * @brief stringify an error code + * @param str - the string buffer + * @param str_len - the length of the string buffer + * @param error - the error code + * @param error_details - the error details + * @return number of bytes written to the string buffer + */ +static int snprintf_error_code( + char *str, size_t str_len, int error, const char *error_details) +{ + return error_details[0] + ? bacapp_snprintf(str, str_len, "%d, \"%s\"", error, error_details) + : bacapp_snprintf(str, str_len, "%d", error); +} + +/** + * @brief stringify a SCFailedConnectionRequest complex data type + * @param str - the string buffer + * @param str_size - the length of the string buffer + * @param req - the structure to hold the data + * @return number of bytes written to the string buffer + */ +int bacapp_snprintf_SCFailedConnectionRequest( + char *str, size_t str_size, const BACNET_SC_FAILED_CONNECTION_REQUEST *req) +{ + int str_len = 0; + int slen; + + slen = bacapp_snprintf(str, str_size, "{"); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = datetime_to_ascii(&req->Timestamp, str, str_size); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, ", "); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_host_n_port(str, str_size, &req->Peer_Address); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_vmac(str, str_size, req->Peer_VMAC); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_uuid(str, str_size, &req->Peer_UUID); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = snprintf_error_code(str, str_size, req->Error, req->Error_Details); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, "}"); + str_len += slen; + + return str_len; +} + +/** + * @brief stringify a SCHubFunctionConnection complex data type + * @param str - the string buffer + * @param str_size - the length of the string buffer + * @param st - the structure to hold the data + * @return number of bytes written to the string buffer + */ +int bacapp_snprintf_SCHubFunctionConnection( + char *str, + size_t str_size, + const BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *st) +{ + int str_len = 0; + int slen; + + slen = bacapp_snprintf(str, str_size, "{%d, ", st->State); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = datetime_to_ascii(&st->Connect_Timestamp, str, str_size); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, ", "); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = datetime_to_ascii(&st->Disconnect_Timestamp, str, str_size); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, ", "); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_host_n_port(str, str_size, &st->Peer_Address); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_vmac(str, str_size, st->Peer_VMAC); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_uuid(str, str_size, &st->Peer_UUID); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = snprintf_error_code(str, str_size, st->Error, st->Error_Details); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, "}"); + str_len += slen; + + return str_len; +} + +/** + * @brief stringify a SCHubConnection complex data type + * @param str - the string buffer, or NULL for length + * @param str_size - the length of the string buffer + * @param st - the structure to hold the data + * @return number of bytes written to the string buffer + */ +int bacapp_snprintf_SCDirectConnection( + char *str, size_t str_size, const BACNET_SC_DIRECT_CONNECTION_STATUS *st) +{ + int str_len = 0; + int slen; + + slen = bacapp_snprintf( + str, str_size, "{%s, %d, ", st->URI[0] ? st->URI : "NULL", st->State); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = datetime_to_ascii(&st->Connect_Timestamp, str, str_size); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, ", "); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = datetime_to_ascii(&st->Disconnect_Timestamp, str, str_size); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, ", "); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_host_n_port(str, str_size, &st->Peer_Address); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_vmac(str, str_size, st->Peer_VMAC); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf_uuid(str, str_size, &st->Peer_UUID); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = snprintf_error_code(str, str_size, st->Error, st->Error_Details); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, "}"); + str_len += slen; + + return str_len; +} + +/** + * @brief stringify a SCHubConnection complex data type + * @param str - the string buffer + * @param str_size - the size of the string buffer + * @param st - the structure to hold the data + * @return number of bytes written to the string buffer + */ +int bacapp_snprintf_SCHubConnection( + char *str, size_t str_size, const BACNET_SC_HUB_CONNECTION_STATUS *st) +{ + int str_len = 0; + int slen; + + slen = bacapp_snprintf(str, str_size, "{%d, ", st->State); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = datetime_to_ascii(&st->Connect_Timestamp, str, str_size); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, ", "); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = datetime_to_ascii(&st->Disconnect_Timestamp, str, str_size); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, ", "); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = snprintf_error_code(str, str_size, st->Error, st->Error_Details); + str_len += bacapp_snprintf_shift(slen, &str, &str_size); + slen = bacapp_snprintf(str, str_size, "}"); + str_len += slen; + + return str_len; +} diff --git a/src/bacnet/secure_connect.h b/src/bacnet/secure_connect.h new file mode 100644 index 00000000..34d67ac9 --- /dev/null +++ b/src/bacnet/secure_connect.h @@ -0,0 +1,175 @@ +/** + * @file + * @brief BACnet Secure Connect encode and decode functions. + * @author Mikhail Antropov + * @date 2023 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_SECURE_CONNECT_H +#define BACNET_SECURE_CONNECT_H +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +/* BACnet Stack API */ +#include "bacnet/datetime.h" +#include "bacnet/datalink/bsc/bsc-conf.h" +#include "bacnet/datalink/bsc/bvlc-sc.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Support + - SC_Hub_Function_Connection_Status + - SC_Direct_Connect_Connection_Status + - SC_FailedConnectionRequests +*/ + +typedef struct BACnetUUID_T_ { + union { + struct guid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_and_node[8]; + } guid; + uint8_t uuid128[16]; + } uuid; +} BACNET_UUID; + +#define BACNET_ERROR_STRING_LENGTH BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN +#define BACNET_URI_LENGTH BSC_CONF_WSURL_MAX_LEN +#define BACNET_PEER_VMAC_LENGTH BVLC_SC_VMAC_SIZE + +enum BACNET_HOST_N_PORT_TYPE { + BACNET_HOST_N_PORT_IP = 1, + BACNET_HOST_N_PORT_HOST = 2 +}; + +typedef struct BACnetHostNPort_data_T { + uint8_t type; + char host[BACNET_URI_LENGTH]; + uint16_t port; +} BACNET_HOST_N_PORT_DATA; + +typedef struct BACnetSCHubConnectionStatus_T { + BACNET_SC_CONNECTION_STATE State; /*index = 0 */ + BACNET_DATE_TIME Connect_Timestamp; /*index = 1 */ + BACNET_DATE_TIME Disconnect_Timestamp; /*index = 2 */ + /* optionals - use ERROR_CODE_DEFAULT for default value */ + BACNET_ERROR_CODE Error; /* index = 3 */ + char Error_Details[BACNET_ERROR_STRING_LENGTH]; /* index = 4 */ +} BACNET_SC_HUB_CONNECTION_STATUS; + +typedef struct BACnetSCHubFunctionConnectionStatus_T { + BACNET_SC_CONNECTION_STATE State; + BACNET_DATE_TIME Connect_Timestamp; + BACNET_DATE_TIME Disconnect_Timestamp; + BACNET_HOST_N_PORT_DATA Peer_Address; + uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH]; + BACNET_UUID Peer_UUID; + BACNET_ERROR_CODE Error; + char Error_Details[BACNET_ERROR_STRING_LENGTH]; +} BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS; + +typedef struct BACnetSCFailedConnectionRequest_T { + BACNET_DATE_TIME Timestamp; + BACNET_HOST_N_PORT_DATA Peer_Address; + uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH]; + BACNET_UUID Peer_UUID; + BACNET_ERROR_CODE Error; + char Error_Details[BACNET_ERROR_STRING_LENGTH]; +} BACNET_SC_FAILED_CONNECTION_REQUEST; + +typedef enum BACnetRouterStatus { + BACNET_ROUTER_STATUS_AVAILABLE = 0, + BACNET_ROUTER_STATUS_BUSY = 1, + BACNET_ROUTER_STATUS_DISCONNECTED = 2, + BACNET_ROUTER_STATUS_MAX = 2 +} BACNET_ROUTER_STATUS; + +typedef struct BACnetRouterEntry_T { + uint16_t Network_Number; + uint8_t Mac_Address[6]; + BACNET_ROUTER_STATUS Status; + uint8_t Performance_Index; +} BACNET_ROUTER_ENTRY; + +typedef struct BACnetSCDirectConnectionStatus_T { + char URI[BACNET_URI_LENGTH]; + BACNET_SC_CONNECTION_STATE State; + BACNET_DATE_TIME Connect_Timestamp; + BACNET_DATE_TIME Disconnect_Timestamp; + BACNET_HOST_N_PORT_DATA Peer_Address; + uint8_t Peer_VMAC[BACNET_PEER_VMAC_LENGTH]; + BACNET_UUID Peer_UUID; + BACNET_ERROR_CODE Error; + char Error_Details[BACNET_ERROR_STRING_LENGTH]; +} BACNET_SC_DIRECT_CONNECTION_STATUS; + +/* */ +/* Encode / decode */ +/* */ + +BACNET_STACK_EXPORT +int bacapp_encode_SCHubConnection( + uint8_t *apdu, const BACNET_SC_HUB_CONNECTION_STATUS *value); +BACNET_STACK_EXPORT +int bacapp_decode_SCHubConnection( + const uint8_t *apdu, + size_t apdu_size, + BACNET_SC_HUB_CONNECTION_STATUS *value); + +BACNET_STACK_EXPORT +int bacapp_encode_SCHubFunctionConnection( + uint8_t *apdu, const BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *value); +BACNET_STACK_EXPORT +int bacapp_decode_SCHubFunctionConnection( + const uint8_t *apdu, + size_t apdu_size, + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *value); + +BACNET_STACK_EXPORT +int bacapp_encode_SCFailedConnectionRequest( + uint8_t *apdu, const BACNET_SC_FAILED_CONNECTION_REQUEST *value); +BACNET_STACK_EXPORT +int bacapp_decode_SCFailedConnectionRequest( + const uint8_t *apdu, + size_t apdu_size, + BACNET_SC_FAILED_CONNECTION_REQUEST *value); + +BACNET_STACK_EXPORT +int bacapp_encode_RouterEntry(uint8_t *apdu, const BACNET_ROUTER_ENTRY *value); +BACNET_STACK_EXPORT +int bacapp_decode_RouterEntry( + const uint8_t *apdu, size_t apdu_size, BACNET_ROUTER_ENTRY *value); + +BACNET_STACK_EXPORT +int bacapp_encode_SCDirectConnection( + uint8_t *apdu, const BACNET_SC_DIRECT_CONNECTION_STATUS *value); +BACNET_STACK_EXPORT +int bacapp_decode_SCDirectConnection( + const uint8_t *apdu, + size_t apdu_size, + BACNET_SC_DIRECT_CONNECTION_STATUS *value); + +BACNET_STACK_EXPORT +int bacapp_snprintf_SCFailedConnectionRequest( + char *str, size_t str_len, const BACNET_SC_FAILED_CONNECTION_REQUEST *req); +BACNET_STACK_EXPORT +int bacapp_snprintf_SCHubFunctionConnection( + char *str, + size_t str_len, + const BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *st); +BACNET_STACK_EXPORT +int bacapp_snprintf_SCDirectConnection( + char *str, size_t str_len, const BACNET_SC_DIRECT_CONNECTION_STATUS *st); +BACNET_STACK_EXPORT +int bacapp_snprintf_SCHubConnection( + char *str, size_t str_len, const BACNET_SC_HUB_CONNECTION_STATUS *st); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c895f37a..fd7a6b5a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -109,6 +109,7 @@ list(APPEND testdirs bacnet/reject bacnet/rp bacnet/rpm + bacnet/secure_connect bacnet/specialevent bacnet/timestamp bacnet/timesync @@ -187,6 +188,30 @@ list(APPEND testdirs bacnet/datalink/mstp ) +# ports tests +if(ZEPHYR_BASE) + message(FATAL_ERROR "ZEPHYR_BASE env variable defined. Use zephyr/CMakeLists.txt for Zephyr build") +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(STATUS "Added ports specific tests for linux") + + list(APPEND testdirs + ports/linux/bsc_event + ) + +elseif(WIN32) + message(STATUS "Added ports specific tests for win32") + + list(APPEND testdirs + ports/win32/bsc_event + ) + +elseif(APPLE) + message(STATUS "Added ports specific tests for APPLE") + list(APPEND testdirs + ports/bsd/bsc_event + ) +endif() + enable_testing() foreach(testdir IN ITEMS ${testdirs}) get_filename_component(basename ${testdir} NAME) diff --git a/test/bacnet/bacapp/CMakeLists.txt b/test/bacnet/bacapp/CMakeLists.txt index 160b6add..4d2e16d7 100644 --- a/test/bacnet/bacapp/CMakeLists.txt +++ b/test/bacnet/bacapp/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/bacdest/CMakeLists.txt b/test/bacnet/bacdest/CMakeLists.txt index 74ca6b06..7c3f14f5 100644 --- a/test/bacnet/bacdest/CMakeLists.txt +++ b/test/bacnet/bacdest/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/bacdevobjpropref/CMakeLists.txt b/test/bacnet/bacdevobjpropref/CMakeLists.txt index f0592ef4..f8b6207b 100644 --- a/test/bacnet/bacdevobjpropref/CMakeLists.txt +++ b/test/bacnet/bacdevobjpropref/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/bactimevalue/CMakeLists.txt b/test/bacnet/bactimevalue/CMakeLists.txt index d1a4be5e..630f027c 100644 --- a/test/bacnet/bactimevalue/CMakeLists.txt +++ b/test/bacnet/bactimevalue/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/binding/address/CMakeLists.txt b/test/bacnet/basic/binding/address/CMakeLists.txt index baf34ef7..a509af4c 100644 --- a/test/bacnet/basic/binding/address/CMakeLists.txt +++ b/test/bacnet/basic/binding/address/CMakeLists.txt @@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/acc/CMakeLists.txt b/test/bacnet/basic/object/acc/CMakeLists.txt index d884f21e..1fb46a89 100644 --- a/test/bacnet/basic/object/acc/CMakeLists.txt +++ b/test/bacnet/basic/object/acc/CMakeLists.txt @@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_credential/CMakeLists.txt b/test/bacnet/basic/object/access_credential/CMakeLists.txt index 2c80ee11..0bfadb74 100644 --- a/test/bacnet/basic/object/access_credential/CMakeLists.txt +++ b/test/bacnet/basic/object/access_credential/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_door/CMakeLists.txt b/test/bacnet/basic/object/access_door/CMakeLists.txt index 3661ab21..ec00d581 100644 --- a/test/bacnet/basic/object/access_door/CMakeLists.txt +++ b/test/bacnet/basic/object/access_door/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_point/CMakeLists.txt b/test/bacnet/basic/object/access_point/CMakeLists.txt index 464a828e..97aa0d8b 100644 --- a/test/bacnet/basic/object/access_point/CMakeLists.txt +++ b/test/bacnet/basic/object/access_point/CMakeLists.txt @@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_rights/CMakeLists.txt b/test/bacnet/basic/object/access_rights/CMakeLists.txt index b46450a7..08d25c40 100644 --- a/test/bacnet/basic/object/access_rights/CMakeLists.txt +++ b/test/bacnet/basic/object/access_rights/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/property_test.c diff --git a/test/bacnet/basic/object/access_user/CMakeLists.txt b/test/bacnet/basic/object/access_user/CMakeLists.txt index 1be1197d..8826bfeb 100644 --- a/test/bacnet/basic/object/access_user/CMakeLists.txt +++ b/test/bacnet/basic/object/access_user/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_zone/CMakeLists.txt b/test/bacnet/basic/object/access_zone/CMakeLists.txt index 2a5e76b0..bd42a5aa 100644 --- a/test/bacnet/basic/object/access_zone/CMakeLists.txt +++ b/test/bacnet/basic/object/access_zone/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/ai/CMakeLists.txt b/test/bacnet/basic/object/ai/CMakeLists.txt index 0a377049..2ce7b6af 100644 --- a/test/bacnet/basic/object/ai/CMakeLists.txt +++ b/test/bacnet/basic/object/ai/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/ao/CMakeLists.txt b/test/bacnet/basic/object/ao/CMakeLists.txt index eacb151e..1f208221 100644 --- a/test/bacnet/basic/object/ao/CMakeLists.txt +++ b/test/bacnet/basic/object/ao/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/keylist.c diff --git a/test/bacnet/basic/object/av/CMakeLists.txt b/test/bacnet/basic/object/av/CMakeLists.txt index 1dcb4015..9f134ec5 100644 --- a/test/bacnet/basic/object/av/CMakeLists.txt +++ b/test/bacnet/basic/object/av/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/bacfile/CMakeLists.txt b/test/bacnet/basic/object/bacfile/CMakeLists.txt index be9a0a14..085b23cc 100644 --- a/test/bacnet/basic/object/bacfile/CMakeLists.txt +++ b/test/bacnet/basic/object/bacfile/CMakeLists.txt @@ -66,6 +66,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/apdu_mock.c diff --git a/test/bacnet/basic/object/bacfile/src/main.c b/test/bacnet/basic/object/bacfile/src/main.c index c1f67343..996df25d 100644 --- a/test/bacnet/basic/object/bacfile/src/main.c +++ b/test/bacnet/basic/object/bacfile/src/main.c @@ -65,6 +65,8 @@ static void test_BACnet_File_Object(void) required_property++; } + bacfile_cleanup(); + return; } /** diff --git a/test/bacnet/basic/object/bi/CMakeLists.txt b/test/bacnet/basic/object/bi/CMakeLists.txt index 6a59ecf1..47f9f1d2 100644 --- a/test/bacnet/basic/object/bi/CMakeLists.txt +++ b/test/bacnet/basic/object/bi/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/bitstring_value/CMakeLists.txt b/test/bacnet/basic/object/bitstring_value/CMakeLists.txt index bd88a8b9..9163cf44 100644 --- a/test/bacnet/basic/object/bitstring_value/CMakeLists.txt +++ b/test/bacnet/basic/object/bitstring_value/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/property_test.c diff --git a/test/bacnet/basic/object/blo/CMakeLists.txt b/test/bacnet/basic/object/blo/CMakeLists.txt index 57b3927b..4f17c9b8 100644 --- a/test/bacnet/basic/object/blo/CMakeLists.txt +++ b/test/bacnet/basic/object/blo/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/device_mock.c diff --git a/test/bacnet/basic/object/bo/CMakeLists.txt b/test/bacnet/basic/object/bo/CMakeLists.txt index f47a324a..588ce6dd 100644 --- a/test/bacnet/basic/object/bo/CMakeLists.txt +++ b/test/bacnet/basic/object/bo/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/bv/CMakeLists.txt b/test/bacnet/basic/object/bv/CMakeLists.txt index b35e3650..661c3854 100644 --- a/test/bacnet/basic/object/bv/CMakeLists.txt +++ b/test/bacnet/basic/object/bv/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/calendar/CMakeLists.txt b/test/bacnet/basic/object/calendar/CMakeLists.txt index 94961e4d..49a7e204 100644 --- a/test/bacnet/basic/object/calendar/CMakeLists.txt +++ b/test/bacnet/basic/object/calendar/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/bactimevalue.c ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files diff --git a/test/bacnet/basic/object/channel/CMakeLists.txt b/test/bacnet/basic/object/channel/CMakeLists.txt index 725e1ded..136c149a 100644 --- a/test/bacnet/basic/object/channel/CMakeLists.txt +++ b/test/bacnet/basic/object/channel/CMakeLists.txt @@ -64,6 +64,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/property_test.c diff --git a/test/bacnet/basic/object/color_object/CMakeLists.txt b/test/bacnet/basic/object/color_object/CMakeLists.txt index 50839e1e..beda6112 100644 --- a/test/bacnet/basic/object/color_object/CMakeLists.txt +++ b/test/bacnet/basic/object/color_object/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/device_mock.c diff --git a/test/bacnet/basic/object/color_temperature/CMakeLists.txt b/test/bacnet/basic/object/color_temperature/CMakeLists.txt index c0c4dc31..8055be16 100644 --- a/test/bacnet/basic/object/color_temperature/CMakeLists.txt +++ b/test/bacnet/basic/object/color_temperature/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/device_mock.c diff --git a/test/bacnet/basic/object/command/CMakeLists.txt b/test/bacnet/basic/object/command/CMakeLists.txt index 91fa93ec..0a36a933 100644 --- a/test/bacnet/basic/object/command/CMakeLists.txt +++ b/test/bacnet/basic/object/command/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/property_test.c diff --git a/test/bacnet/basic/object/credential_data_input/CMakeLists.txt b/test/bacnet/basic/object/credential_data_input/CMakeLists.txt index 0f2a212e..386dd01d 100644 --- a/test/bacnet/basic/object/credential_data_input/CMakeLists.txt +++ b/test/bacnet/basic/object/credential_data_input/CMakeLists.txt @@ -64,6 +64,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/property_test.c diff --git a/test/bacnet/basic/object/csv/CMakeLists.txt b/test/bacnet/basic/object/csv/CMakeLists.txt index a84d5f7b..77037937 100644 --- a/test/bacnet/basic/object/csv/CMakeLists.txt +++ b/test/bacnet/basic/object/csv/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/memcopy.c # Test and test library files ./src/main.c diff --git a/test/bacnet/basic/object/device/CMakeLists.txt b/test/bacnet/basic/object/device/CMakeLists.txt index a451772b..a576b21d 100644 --- a/test/bacnet/basic/object/device/CMakeLists.txt +++ b/test/bacnet/basic/object/device/CMakeLists.txt @@ -107,6 +107,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ./stubs.c # Test and test library files ./src/main.c diff --git a/test/bacnet/basic/object/iv/CMakeLists.txt b/test/bacnet/basic/object/iv/CMakeLists.txt index c2303dbc..a4e14828 100644 --- a/test/bacnet/basic/object/iv/CMakeLists.txt +++ b/test/bacnet/basic/object/iv/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/lc/CMakeLists.txt b/test/bacnet/basic/object/lc/CMakeLists.txt index def47599..8d10b2a4 100644 --- a/test/bacnet/basic/object/lc/CMakeLists.txt +++ b/test/bacnet/basic/object/lc/CMakeLists.txt @@ -70,6 +70,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/datetime_local.c diff --git a/test/bacnet/basic/object/lo/CMakeLists.txt b/test/bacnet/basic/object/lo/CMakeLists.txt index cebbe47d..5f45b3fe 100644 --- a/test/bacnet/basic/object/lo/CMakeLists.txt +++ b/test/bacnet/basic/object/lo/CMakeLists.txt @@ -64,6 +64,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/device_mock.c diff --git a/test/bacnet/basic/object/lsp/CMakeLists.txt b/test/bacnet/basic/object/lsp/CMakeLists.txt index 614bf8b8..3afe4eed 100644 --- a/test/bacnet/basic/object/lsp/CMakeLists.txt +++ b/test/bacnet/basic/object/lsp/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/lsz/CMakeLists.txt b/test/bacnet/basic/object/lsz/CMakeLists.txt index b00cdd25..de3727af 100644 --- a/test/bacnet/basic/object/lsz/CMakeLists.txt +++ b/test/bacnet/basic/object/lsz/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/property_test.c diff --git a/test/bacnet/basic/object/ms-input/CMakeLists.txt b/test/bacnet/basic/object/ms-input/CMakeLists.txt index 3213b829..ecda1ceb 100644 --- a/test/bacnet/basic/object/ms-input/CMakeLists.txt +++ b/test/bacnet/basic/object/ms-input/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/mso/CMakeLists.txt b/test/bacnet/basic/object/mso/CMakeLists.txt index a5ee4440..f3475e31 100644 --- a/test/bacnet/basic/object/mso/CMakeLists.txt +++ b/test/bacnet/basic/object/mso/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/msv/CMakeLists.txt b/test/bacnet/basic/object/msv/CMakeLists.txt index 76175ecf..b485b1f2 100644 --- a/test/bacnet/basic/object/msv/CMakeLists.txt +++ b/test/bacnet/basic/object/msv/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/basic/sys/debug.c diff --git a/test/bacnet/basic/object/nc/CMakeLists.txt b/test/bacnet/basic/object/nc/CMakeLists.txt index 57df9fc6..121c5fd6 100644 --- a/test/bacnet/basic/object/nc/CMakeLists.txt +++ b/test/bacnet/basic/object/nc/CMakeLists.txt @@ -64,6 +64,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./stubs.c ./src/main.c diff --git a/test/bacnet/basic/object/netport/CMakeLists.txt b/test/bacnet/basic/object/netport/CMakeLists.txt index 78fd8707..a6b04637 100644 --- a/test/bacnet/basic/object/netport/CMakeLists.txt +++ b/test/bacnet/basic/object/netport/CMakeLists.txt @@ -13,6 +13,11 @@ string(REGEX REPLACE "/src" SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) string(REGEX REPLACE "/test/bacnet/[a-zA-Z_/-]*$" "/test" @@ -23,6 +28,13 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 + BACDL_BSC=1 + BACNET_SECURE_CONNECT_ROUTING_TABLE=1 + BSC_CONF_HUB_FUNCTIONS_NUM=1 + BSC_CONF_HUB_CONNECTORS_NUM=1 + BACNET_SC_DIRECT_ACCEPT_URI_MAX=6 + MAX_TSM_TRANSACTIONS=0 + BSC_CONF_TX_PRE=0 ) include_directories( @@ -31,11 +43,21 @@ include_directories( ${TST_DIR}/ztest/include ) -add_executable(${PROJECT_NAME} +if(ZEPHYR_BASE) + message(FATAL_ERROR "ZEPHYR_BASE env variable defined. Use zephyr/CMakeLists.txt for Zephyr build") +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(STATUS "Netport test: building for linux") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/linux/datetime-init.c # File(s) under test + ${SRC_DIR}/bacnet/basic/object/bacfile.c ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c # Support files and stubs (pathname alphabetical) ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c ${SRC_DIR}/bacnet/bacaction.c ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c @@ -47,10 +69,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c - ${SRC_DIR}/bacnet/datalink/bvlc.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c @@ -64,9 +83,69 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/datalink/bvlc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c # Test and test library files - ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c ${TST_DIR}/bacnet/basic/object/test/property_test.c + ./src/main.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) + +elseif(APPLE) + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/bsd) + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/bsd/datetime-init.c + # File(s) under test + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/property.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/datalink/bvlc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + # Test and test library files + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${TST_DIR}/bacnet/basic/object/test/property_test.c + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) +endif() diff --git a/test/bacnet/basic/object/netport/src/main.c b/test/bacnet/basic/object/netport/src/main.c index d1fd16e2..33443649 100644 --- a/test/bacnet/basic/object/netport/src/main.c +++ b/test/bacnet/basic/object/netport/src/main.c @@ -9,8 +9,12 @@ #include #include #include +#include +#include #include - +#ifndef BACDL_BSC +#define BACDL_BSC +#endif /** * @addtogroup bacnet_tests * @{ @@ -34,23 +38,27 @@ static void test_network_port(void) PORT_TYPE_LONTALK, PORT_TYPE_BIP, PORT_TYPE_ZIGBEE, PORT_TYPE_VIRTUAL, PORT_TYPE_NON_BACNET, PORT_TYPE_BIP6, - PORT_TYPE_MAX }; + PORT_TYPE_BSC, PORT_TYPE_MAX }; const int known_fail_property_list[] = { PROP_IP_DNS_SERVER, PROP_BBMD_BROADCAST_DISTRIBUTION_TABLE, PROP_BBMD_FOREIGN_DEVICE_TABLE, PROP_FD_BBMD_ADDRESS, PROP_IPV6_DNS_SERVER, + PROP_ISSUER_CERTIFICATE_FILES, + PROP_SC_HUB_FUNCTION_CONNECTION_STATUS, + PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS, + PROP_SC_FAILED_CONNECTION_REQUESTS, -1 }; while (port_type[port] != PORT_TYPE_MAX) { + Network_Port_Init(); object_instance = 1234; status = Network_Port_Object_Instance_Number_Set(0, object_instance); zassert_true(status, NULL); status = Network_Port_Type_Set(object_instance, port_type[port]); zassert_true(status, NULL); - Network_Port_Init(); count = Network_Port_Count(); zassert_true(count > 0, NULL); bacnet_object_properties_read_write_test( @@ -61,6 +69,7 @@ static void test_network_port(void) object_instance, Network_Port_Name_Set, Network_Port_Object_Name_ASCII); port++; + Network_Port_Cleanup(); } return; @@ -69,12 +78,680 @@ static void test_network_port(void) * @} */ +static void test_network_port_pending_param(void) +{ +#ifdef BACDL_BSC + /* for decode value data */ + unsigned count = 0; + uint32_t object_instance = 0; + bool status = false; +#if (BSC_CONF_HUB_FUNCTIONS_NUM != 0) || (BSC_CONF_HUB_CONNECTORS_NUM != 0) + bool val; + BACNET_CHARACTER_STRING str; +#endif +#define PRIMARY_HUB_URL1 "SC_Primary_Hub_URI_test" +#define PRIMARY_HUB_URL2 "SC_Primary_Hub_URI_test2" +#define FAILOVER_HUB_URL1 "SC_Failover_Hub_URI_test" +#define FAILOVER_HUB_URL2 "SC_Failover_Hub_URI_test2" +#define HUB_BINDING1 "SC_Hub_Function_Binding_test:12345" +#define HUB_BINDING2 "SC_Hub_Function_Binding_test2:11111" +#define DIRECT_BINDING1 "SC_Direct_Connect_Binding_test:54321" +#define DIRECT_BINDING2 "SC_Direct_Connect_Binding_test2:22222" +#define DIRECT_BINDING1 "SC_Direct_Connect_Binding_test:54321" +#define DIRECT_BINDING2 "SC_Direct_Connect_Binding_test2:22222" +#define URL1 "SC_Direct_Connect_Accept_URI1" +#define URL2 "SC_Direct_Connect_Accept_URI2" + + Network_Port_Init(); + object_instance = 1234; + status = Network_Port_Object_Instance_Number_Set(0, object_instance); + zassert_true(status, NULL); + status = Network_Port_Type_Set(object_instance, PORT_TYPE_BSC); + zassert_true(status, NULL); + count = Network_Port_Count(); + zassert_true(count > 0, NULL); + + Network_Port_Max_BVLC_Length_Accepted_Set(object_instance, 1200); + Network_Port_Max_NPDU_Length_Accepted_Set(object_instance, 1100); + Network_Port_SC_Minimum_Reconnect_Time_Set(object_instance, 2); + Network_Port_SC_Maximum_Reconnect_Time_Set(object_instance, 5); + Network_Port_SC_Connect_Wait_Timeout_Set(object_instance, 40); + Network_Port_SC_Disconnect_Wait_Timeout_Set(object_instance, 10); + Network_Port_SC_Heartbeat_Timeout_Set(object_instance, 3); + + // write properties +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + Network_Port_SC_Primary_Hub_URI_Set(object_instance, PRIMARY_HUB_URL1); + Network_Port_SC_Failover_Hub_URI_Set(object_instance, FAILOVER_HUB_URL1); + Network_Port_SC_Hub_Function_Enable_Set(object_instance, false); + Network_Port_SC_Hub_Function_Binding_Set(object_instance, HUB_BINDING1); + Network_Port_SC_Hub_Function_Accept_URI_Set(object_instance, 0, URL1); +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + Network_Port_SC_Direct_Connect_Initiate_Enable_Set(object_instance, false); + Network_Port_SC_Direct_Connect_Accept_Enable_Set(object_instance, false); + Network_Port_SC_Direct_Connect_Binding_Set( + object_instance, DIRECT_BINDING1); + Network_Port_SC_Direct_Connect_Accept_URI_Set(object_instance, 0, URL1); +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + + // write dirty properties + Network_Port_Max_BVLC_Length_Accepted_Dirty_Set(object_instance, 1250); + Network_Port_Max_NPDU_Length_Accepted_Dirty_Set(object_instance, 1150); + Network_Port_SC_Minimum_Reconnect_Time_Dirty_Set(object_instance, 3); + Network_Port_SC_Maximum_Reconnect_Time_Dirty_Set(object_instance, 4); + Network_Port_SC_Connect_Wait_Timeout_Dirty_Set(object_instance, 50); + Network_Port_SC_Disconnect_Wait_Timeout_Dirty_Set(object_instance, 15); + Network_Port_SC_Heartbeat_Timeout_Dirty_Set(object_instance, 6); + +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + Network_Port_SC_Primary_Hub_URI_Dirty_Set( + object_instance, PRIMARY_HUB_URL2); + Network_Port_SC_Failover_Hub_URI_Dirty_Set( + object_instance, FAILOVER_HUB_URL2); + Network_Port_SC_Hub_Function_Enable_Dirty_Set(object_instance, true); + Network_Port_SC_Hub_Function_Binding_Dirty_Set( + object_instance, HUB_BINDING2); + Network_Port_SC_Hub_Function_Accept_URI_Dirty_Set(object_instance, 0, URL2); +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + Network_Port_SC_Direct_Connect_Initiate_Enable_Dirty_Set( + object_instance, true); + Network_Port_SC_Direct_Connect_Accept_Enable_Dirty_Set( + object_instance, true); + Network_Port_SC_Direct_Connect_Binding_Dirty_Set( + object_instance, DIRECT_BINDING2); + Network_Port_SC_Direct_Connect_Accept_URI_Dirty_Set( + object_instance, 0, URL2); +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + + // check old value + zassert_true( + Network_Port_Max_BVLC_Length_Accepted(object_instance) == 1200, NULL); + zassert_true( + Network_Port_Max_NPDU_Length_Accepted(object_instance) == 1100, NULL); + zassert_true( + Network_Port_SC_Minimum_Reconnect_Time(object_instance) == 2, NULL); + zassert_true( + Network_Port_SC_Maximum_Reconnect_Time(object_instance) == 5, NULL); + zassert_true( + Network_Port_SC_Connect_Wait_Timeout(object_instance) == 40, NULL); + zassert_true( + Network_Port_SC_Disconnect_Wait_Timeout(object_instance) == 10, NULL); + zassert_true(Network_Port_SC_Heartbeat_Timeout(object_instance) == 3, NULL); + +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + Network_Port_SC_Primary_Hub_URI(object_instance, &str); + zassert_true( + strncmp( + characterstring_value(&str), PRIMARY_HUB_URL1, + characterstring_length(&str)) == 0, + NULL); + Network_Port_SC_Failover_Hub_URI(object_instance, &str); + zassert_true( + strncmp( + characterstring_value(&str), FAILOVER_HUB_URL1, + characterstring_length(&str)) == 0, + NULL); + val = Network_Port_SC_Hub_Function_Enable(object_instance); + zassert_true(val == false, NULL); + Network_Port_SC_Hub_Function_Binding(object_instance, &str); + zassert_true( + strncmp( + characterstring_value(&str), HUB_BINDING1, + characterstring_length(&str)) == 0, + NULL); + Network_Port_SC_Hub_Function_Accept_URI(object_instance, 0, &str); + zassert_true( + strncmp( + characterstring_value(&str), URL1, characterstring_length(&str)) == + 0, + NULL); +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + val = Network_Port_SC_Direct_Connect_Initiate_Enable(object_instance); + zassert_true(val == false, NULL); + val = Network_Port_SC_Direct_Connect_Accept_Enable(object_instance); + zassert_true(val == false, NULL); + Network_Port_SC_Direct_Connect_Binding(object_instance, &str); + zassert_true( + strncmp( + characterstring_value(&str), DIRECT_BINDING1, + characterstring_length(&str)) == 0, + NULL); + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 0, &str); + zassert_true( + strncmp( + characterstring_value(&str), URL1, characterstring_length(&str)) == + 0, + NULL); +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + + // apply + Network_Port_Changes_Pending_Activate(object_instance); + + // check new value + zassert_true( + Network_Port_Max_BVLC_Length_Accepted(object_instance) == 1250, NULL); + zassert_true( + Network_Port_Max_NPDU_Length_Accepted(object_instance) == 1150, NULL); + zassert_true( + Network_Port_SC_Minimum_Reconnect_Time(object_instance) == 3, NULL); + zassert_true( + Network_Port_SC_Maximum_Reconnect_Time(object_instance) == 4, NULL); + zassert_true( + Network_Port_SC_Connect_Wait_Timeout(object_instance) == 50, NULL); + zassert_true( + Network_Port_SC_Disconnect_Wait_Timeout(object_instance) == 15, NULL); + zassert_true(Network_Port_SC_Heartbeat_Timeout(object_instance) == 6, NULL); + +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + Network_Port_SC_Primary_Hub_URI(object_instance, &str); + zassert_true( + strncmp( + characterstring_value(&str), PRIMARY_HUB_URL2, + characterstring_length(&str)) == 0, + NULL); + Network_Port_SC_Failover_Hub_URI(object_instance, &str); + zassert_true( + strncmp( + characterstring_value(&str), FAILOVER_HUB_URL2, + characterstring_length(&str)) == 0, + NULL); + val = Network_Port_SC_Hub_Function_Enable(object_instance); + zassert_true(val == true, NULL); + Network_Port_SC_Hub_Function_Binding(object_instance, &str); + zassert_true( + strncmp( + characterstring_value(&str), HUB_BINDING2, + characterstring_length(&str)) == 0, + NULL); + Network_Port_SC_Hub_Function_Accept_URI(object_instance, 0, &str); + zassert_true( + strncmp( + characterstring_value(&str), URL2, characterstring_length(&str)) == + 0, + NULL); +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + val = Network_Port_SC_Direct_Connect_Initiate_Enable(object_instance); + zassert_true(val == true, NULL); + val = Network_Port_SC_Direct_Connect_Accept_Enable(object_instance); + zassert_true(val == true, NULL); + Network_Port_SC_Direct_Connect_Binding(object_instance, &str); + zassert_true( + strncmp( + characterstring_value(&str), DIRECT_BINDING2, + characterstring_length(&str)) == 0, + NULL); + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 0, &str); + zassert_true( + strncmp( + characterstring_value(&str), URL2, characterstring_length(&str)) == + 0, + NULL); +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + +#endif /* BACDL_BSC */ + return; +} + +static void test_network_port_sc_direct_connect_accept_uri(void) +{ +#if defined(BACDL_BSC) && (BSC_CONF_HUB_CONNECTORS_NUM != 0) +#define URL1 "SC_Direct_Connect_Accept_URI1" +#define URL2 "SC_Direct_Connect_Accept_URI2" +#define URL3 "SC_Direct_Connect_Accept_URI3" +#define URL_SMALL "bla-bla-bla" +#define URL_BIG "big-bla-big-bla-big-bla-big-bla-big-bla" + char urls0[] = URL1 " " URL2 " " URL3; + char urls1[] = URL1 " " URL_SMALL " " URL3; + char urls2[] = URL1 " " URL_BIG " " URL3; + char urls3[] = URL1 " " URL_BIG " " URL3 " " URL_SMALL; + char urls4[] = URL1 " " URL_BIG " " URL3 " " URL_SMALL " " URL_BIG; + + unsigned count = 0; + uint32_t object_instance = 0; + bool status = false; + BACNET_CHARACTER_STRING str = { 0 }; + + Network_Port_Init(); + object_instance = 1234; + status = Network_Port_Object_Instance_Number_Set(0, object_instance); + zassert_true(status, NULL); + count = Network_Port_Count(); + zassert_true(count > 0, NULL); + + // init + Network_Port_SC_Direct_Connect_Accept_URIs_Set(object_instance, urls0); + zassert_true( + strcmp( + Network_Port_SC_Direct_Connect_Accept_URIs_char(object_instance), + urls0) == 0, + NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 0, &str), + NULL); + zassert_true( + strncmp( + characterstring_value(&str), URL1, characterstring_length(&str)) == + 0, + NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 1, &str), + NULL); + zassert_true( + strncmp( + characterstring_value(&str), URL2, characterstring_length(&str)) == + 0, + NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 2, &str), + NULL); + zassert_true( + strncmp( + characterstring_value(&str), URL3, characterstring_length(&str)) == + 0, + NULL); + + // change + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI_Set( + object_instance, 1, URL_SMALL), + NULL); + zassert_true( + strcmp( + Network_Port_SC_Direct_Connect_Accept_URIs_char(object_instance), + urls1) == 0, + NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI_Set( + object_instance, 1, URL_BIG), + NULL); + zassert_true( + strcmp( + Network_Port_SC_Direct_Connect_Accept_URIs_char(object_instance), + urls2) == 0, + NULL); + + // append + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI_Set( + object_instance, 4, URL_SMALL), + NULL); + zassert_true( + strcmp( + Network_Port_SC_Direct_Connect_Accept_URIs_char(object_instance), + urls3) == 0, + NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI_Set( + object_instance, 4, URL_BIG), + NULL); + zassert_true( + strcmp( + Network_Port_SC_Direct_Connect_Accept_URIs_char(object_instance), + urls4) == 0, + NULL); + + // check last + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 0, &str), + NULL); + zassert_true(strcmp(characterstring_value(&str), URL1) == 0, NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 1, &str), + NULL); + zassert_true(strcmp(characterstring_value(&str), URL_BIG) == 0, NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 2, &str), + NULL); + zassert_true(strcmp(characterstring_value(&str), URL3) == 0, NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 3, &str), + NULL); + zassert_true(strcmp(characterstring_value(&str), URL_SMALL) == 0, NULL); + + zassert_true( + Network_Port_SC_Direct_Connect_Accept_URI(object_instance, 4, &str), + NULL); + zassert_true( + strncmp( + characterstring_value(&str), URL_BIG, + characterstring_length(&str)) == 0, + NULL); + +#endif /* BACDL_BSC && BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + + return; +} + +#define BACFILE_START 0 + +static void test_network_port_sc_certificates(void) +{ +#ifdef BACDL_BSC + unsigned count = 0; + uint32_t instance = 0; + uint32_t file_instance = 0; + bool status = false; + char *filename_ca_cert = "ca_cert.pem"; + char *filename_cert = "cert.pem"; + char *filename_key = "key.pem"; + + Network_Port_Init(); + instance = 1234; + status = Network_Port_Object_Instance_Number_Set(0, instance); + zassert_true(status, NULL); + count = Network_Port_Count(); + zassert_true(count > 0, NULL); + + bacfile_init(); + // CA certificate + status = bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE); + zassert_true(status, NULL); + bacfile_pathname_set( + BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE, filename_ca_cert); + status = Network_Port_Issuer_Certificate_File_Set( + instance, 0, BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE); + zassert_true(status, NULL); + file_instance = Network_Port_Issuer_Certificate_File(instance, 0); + zassert_equal(file_instance, BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE, NULL); + + status = bacfile_create(BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE); + zassert_true(status, NULL); + bacfile_pathname_set( + BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE, filename_cert); + status = Network_Port_Operational_Certificate_File_Set( + instance, BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE); + zassert_true(status, NULL); + file_instance = Network_Port_Operational_Certificate_File(instance); + zassert_equal( + file_instance, BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE, NULL); + + status = bacfile_create(BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE); + zassert_true(status, NULL); + bacfile_pathname_set( + BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE, filename_key); + status = Network_Port_Certificate_Key_File_Set( + instance, BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE); + zassert_true(status, NULL); + file_instance = Network_Port_Certificate_Key_File(instance); + zassert_equal( + file_instance, BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE, NULL); + + // reset + + // check bacfile after reset + +#endif /* BACDL_BSC */ + + return; +} + +static void test_network_port_sc_status_encode_decode(void) +{ +#ifdef BACDL_BSC + unsigned count = 0; + uint32_t instance = 0; + bool status = false; + BACNET_DATE_TIME ts = { { 2202, 5, 7, 3 }, { 12, 34, 22, 10 } }; + BACNET_HOST_N_PORT_DATA peer_address = { 1, "\xef\x00\x00\x10", 50001 }; + uint8_t peer_VMAC[BACNET_PEER_VMAC_LENGTH] = { 1, 2, 3, 4, 5, 6 }; + uint8_t peer_UUID[16] = { 0 }; + BACNET_HOST_N_PORT_DATA peer_address2 = { 1, "\xef\x00\x00\x10", 50002 }; + + BACNET_READ_PROPERTY_DATA rpdata; + BACNET_OBJECT_PROPERTY_VALUE object_value; + BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */ + uint8_t apdu[MAX_APDU]; + int len; + int len2; + char str[512]; + + const char REQ_STR[] = + "{1946/05/07-12:34:22.10, 239.0.0.16:50001, 1.2.3.4.5.6, " + "00000000-0000-0000-0000-000000000000, 38, \"error details\"}"; + + const char HUB_STATUS_STR[] = "{1946/05/07-12:34:22.10, " + "239.0.0.16:50001, 1.2.3.4.5.6, " + "00000000-0000-0000-0000-000000000000, 38, " + "\"error details\"}"; + + const char DIRECT_STATUS_STR[] = + "{connect url, 3, 1946/05/07-12:34:22.10, 1946/05/07-12:34:22.10, " + "239.0.0.16:50001, 1.2.3.4.5.6, 00000000-0000-0000-0000-000000000000, " + "38, \"error message of direct status\"}"; + + const char PRIMARY_HUB_STATUS[] = + "{2, 1946/05/07-12:34:22.10, " + "1946/05/07-12:34:22.10, 38, \"error message\"}"; + + const char FAILOVER_HUB_STATUS[] = + "{3, 1946/05/07-12:34:22.10, " + "1946/05/07-12:34:22.10, 14, \"again error message\"}"; + + Network_Port_Init(); + instance = 1234; + status = Network_Port_Object_Instance_Number_Set(0, instance); + zassert_true(status, NULL); + count = Network_Port_Count(); + zassert_true(count > 0, NULL); + + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_NETWORK_PORT; + rpdata.object_instance = instance; + + object_value.object_type = rpdata.object_type; + object_value.object_instance = rpdata.object_instance; + object_value.value = &value; + + /* SC_Failed_Connection_Requests */ + count = Network_Port_SC_Failed_Connection_Requests_Count(instance); + zassert_equal(count, 0, NULL); + status = Network_Port_SC_Failed_Connection_Requests_Add( + instance, &ts, &peer_address, peer_VMAC, peer_UUID, + ERROR_CODE_VT_SESSION_ALREADY_CLOSED, "error details"); + zassert_true(status, NULL); + count = Network_Port_SC_Failed_Connection_Requests_Count(instance); + zassert_equal(count, 1, NULL); + + object_value.object_property = rpdata.object_property = + PROP_SC_FAILED_CONNECTION_REQUESTS; + + // count (error: property is not BacArray) + object_value.array_index = rpdata.array_index = 0; + len = Network_Port_Read_Property(&rpdata); + zassert_true(len == -1, NULL); + + // context (error: property is not BacArray) + object_value.array_index = rpdata.array_index = 1; + len = Network_Port_Read_Property(&rpdata); + zassert_true(len == -1, NULL); + + // all context + object_value.array_index = rpdata.array_index = BACNET_ARRAY_ALL; + len = Network_Port_Read_Property(&rpdata); + zassert_true(len > 0, NULL); + + len2 = bacapp_decode_known_property( + apdu, len, &value, rpdata.object_type, rpdata.object_property); + zassert_equal(len2, len, NULL); + + len = bacapp_snprintf_value(NULL, 0, &object_value); + zassert_true((len > 0) && (len < sizeof(str) - 1), NULL); + len2 = bacapp_snprintf_value(str, len + 1, &object_value); + zassert_equal(len2, len, NULL); + + zassert_true(strncmp(str, REQ_STR, strlen(REQ_STR)) == 0, NULL); + +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + + /* SC_Hub_Function_Connection_Status */ + count = Network_Port_SC_Hub_Function_Connection_Status_Count(instance); + zassert_equal(count, 0, NULL); + + status = Network_Port_SC_Hub_Function_Connection_Status_Add( + instance, BACNET_SC_CONNECTION_STATE_CONNECTED, &ts, &ts, &peer_address, + peer_VMAC, peer_UUID, ERROR_CODE_VT_SESSION_ALREADY_CLOSED, + "hided error message"); + zassert_true(status, NULL); + + status = Network_Port_SC_Hub_Function_Connection_Status_Add( + instance, BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, &ts, &ts, + &peer_address2, peer_VMAC, peer_UUID, + ERROR_CODE_VT_SESSION_ALREADY_CLOSED, "error message of hub status"); + zassert_true(status, NULL); + + count = Network_Port_SC_Hub_Function_Connection_Status_Count(instance); + zassert_equal(count, 2, NULL); + + object_value.object_property = rpdata.object_property = + PROP_SC_HUB_FUNCTION_CONNECTION_STATUS; + + // count (error: property is not BacArray) + object_value.array_index = rpdata.array_index = 0; + len = Network_Port_Read_Property(&rpdata); + zassert_true(len == -1, NULL); + + // context (error: property is not BacArray) + object_value.array_index = rpdata.array_index = 1; + len = Network_Port_Read_Property(&rpdata); + zassert_true(len == -1, NULL); + + len = bacapp_snprintf_value(NULL, 0, &object_value); + zassert_true((len > 0) && (len < sizeof(str) - 1), NULL); + len2 = bacapp_snprintf_value(str, len + 1, &object_value); + zassert_equal(len2, len, NULL); + + zassert_true( + strncmp(str, HUB_STATUS_STR, strlen(HUB_STATUS_STR)) == 0, NULL); + + /* SC_Primary_Hub_Connection_Status */ + status = Network_Port_SC_Primary_Hub_Connection_Status_Set( + instance, BACNET_SC_CONNECTION_STATE_DISCONNECTED_WITH_ERRORS, &ts, &ts, + ERROR_CODE_VT_SESSION_ALREADY_CLOSED, "error message"); + zassert_true(status, NULL); + + rpdata.array_index = BACNET_ARRAY_ALL; + object_value.object_property = rpdata.object_property = + PROP_SC_PRIMARY_HUB_CONNECTION_STATUS; + + // context + len = Network_Port_Read_Property(&rpdata); + zassert_true(len > 0, NULL); + + len2 = bacapp_decode_known_property( + apdu, len, &value, rpdata.object_type, rpdata.object_property); + zassert_equal(len2, len, NULL); + + len = bacapp_snprintf_value(NULL, 0, &object_value); + zassert_true((len > 0) && (len < sizeof(str) - 1), NULL); + len2 = bacapp_snprintf_value(str, len + 1, &object_value); + zassert_equal(len2, len, NULL); + zassert_true( + strncmp(str, PRIMARY_HUB_STATUS, strlen(PRIMARY_HUB_STATUS)) == 0, + NULL); + + /* SC_Failover_Hub_Connection_Status */ + status = Network_Port_SC_Failover_Hub_Connection_Status_Set( + instance, BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, &ts, &ts, + ERROR_CODE_INVALID_TIME_STAMP, "again error message"); + zassert_true(status, NULL); + + object_value.object_property = rpdata.object_property = + PROP_SC_FAILOVER_HUB_CONNECTION_STATUS; + + // context + len = Network_Port_Read_Property(&rpdata); + zassert_true(len > 0, NULL); + + len2 = bacapp_decode_known_property( + apdu, len, &value, rpdata.object_type, rpdata.object_property); + zassert_equal(len2, len, NULL); + + len = bacapp_snprintf_value(NULL, 0, &object_value); + zassert_true((len > 0) && (len < sizeof(str) - 1), NULL); + len2 = bacapp_snprintf_value(str, len + 1, &object_value); + zassert_equal(len2, len, NULL); + zassert_true( + strncmp(str, FAILOVER_HUB_STATUS, strlen(FAILOVER_HUB_STATUS)) == 0, + NULL); + +#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */ + +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + + /* SC_Hub_Function_Connection_Status */ + count = Network_Port_SC_Direct_Connect_Connection_Status_Count(instance); + zassert_equal(count, 0, NULL); + + status = Network_Port_SC_Direct_Connect_Connection_Status_Add( + instance, "connect url", BACNET_SC_CONNECTION_STATE_FAILED_TO_CONNECT, + &ts, &ts, &peer_address, peer_VMAC, peer_UUID, + ERROR_CODE_VT_SESSION_ALREADY_CLOSED, "error message of direct status"); + zassert_true(status, NULL); + + count = Network_Port_SC_Direct_Connect_Connection_Status_Count(instance); + zassert_equal(count, 1, NULL); + + object_value.object_property = rpdata.object_property = + PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS; + + // count (error: property is not BacArray) + object_value.array_index = rpdata.array_index = 0; + len = Network_Port_Read_Property(&rpdata); + zassert_true(len == -1, NULL); + + // context (error: property is not BacArray) + object_value.array_index = rpdata.array_index = 1; + len = Network_Port_Read_Property(&rpdata); + zassert_true(len == -1, NULL); + + // all context + object_value.array_index = rpdata.array_index = BACNET_ARRAY_ALL; + len = Network_Port_Read_Property(&rpdata); + zassert_true(len > 0, NULL); + + len2 = bacapp_decode_known_property( + apdu, len, &value, rpdata.object_type, rpdata.object_property); + zassert_equal(len2, len, NULL); + + len = bacapp_snprintf_value(NULL, 0, &object_value); + zassert_true((len > 0) && (len < sizeof(str) - 1), NULL); + len2 = bacapp_snprintf_value(str, len + 1, &object_value); + zassert_equal(len2, len, NULL); + + zassert_true( + strncmp(str, DIRECT_STATUS_STR, strlen(DIRECT_STATUS_STR)) == 0, NULL); + +#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */ + +#endif /* BACDL_BSC */ + + return; +} + #if defined(CONFIG_ZTEST_NEW_API) ZTEST_SUITE(netport_tests, NULL, NULL, NULL, NULL, NULL); #else void test_main(void) { - ztest_test_suite(netport_tests, ztest_unit_test(test_network_port)); + ztest_test_suite( + netport_tests, ztest_unit_test(test_network_port), + ztest_unit_test(test_network_port_pending_param), + ztest_unit_test(test_network_port_sc_direct_connect_accept_uri), + ztest_unit_test(test_network_port_sc_certificates), + ztest_unit_test(test_network_port_sc_status_encode_decode)); ztest_run_test_suite(netport_tests); } diff --git a/test/bacnet/basic/object/osv/CMakeLists.txt b/test/bacnet/basic/object/osv/CMakeLists.txt index 77b3982b..1ae7b80c 100644 --- a/test/bacnet/basic/object/osv/CMakeLists.txt +++ b/test/bacnet/basic/object/osv/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/piv/CMakeLists.txt b/test/bacnet/basic/object/piv/CMakeLists.txt index dadc50f7..9dad4962 100644 --- a/test/bacnet/basic/object/piv/CMakeLists.txt +++ b/test/bacnet/basic/object/piv/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/schedule/CMakeLists.txt b/test/bacnet/basic/object/schedule/CMakeLists.txt index 8f014795..67abf42f 100644 --- a/test/bacnet/basic/object/schedule/CMakeLists.txt +++ b/test/bacnet/basic/object/schedule/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/property_test.c diff --git a/test/bacnet/basic/object/structured_view/CMakeLists.txt b/test/bacnet/basic/object/structured_view/CMakeLists.txt index 3c818261..4a8b551d 100644 --- a/test/bacnet/basic/object/structured_view/CMakeLists.txt +++ b/test/bacnet/basic/object/structured_view/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/sys/debug.c ${SRC_DIR}/bacnet/basic/sys/keylist.c diff --git a/test/bacnet/basic/object/time_value/CMakeLists.txt b/test/bacnet/basic/object/time_value/CMakeLists.txt index a9ef0ae0..44a11f00 100644 --- a/test/bacnet/basic/object/time_value/CMakeLists.txt +++ b/test/bacnet/basic/object/time_value/CMakeLists.txt @@ -64,6 +64,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/datetime_local.c diff --git a/test/bacnet/basic/object/trendlog/CMakeLists.txt b/test/bacnet/basic/object/trendlog/CMakeLists.txt index 6900778c..4be8a788 100644 --- a/test/bacnet/basic/object/trendlog/CMakeLists.txt +++ b/test/bacnet/basic/object/trendlog/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/device_mock.c diff --git a/test/bacnet/channel_value/CMakeLists.txt b/test/bacnet/channel_value/CMakeLists.txt index fc71c7ef..375b9217 100644 --- a/test/bacnet/channel_value/CMakeLists.txt +++ b/test/bacnet/channel_value/CMakeLists.txt @@ -41,6 +41,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/lighting.c # Test and test library files diff --git a/test/bacnet/cov/CMakeLists.txt b/test/bacnet/cov/CMakeLists.txt index d8b083fc..0c2606f0 100644 --- a/test/bacnet/cov/CMakeLists.txt +++ b/test/bacnet/cov/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/create_object/CMakeLists.txt b/test/bacnet/create_object/CMakeLists.txt index 939027b2..43d5d6a1 100644 --- a/test/bacnet/create_object/CMakeLists.txt +++ b/test/bacnet/create_object/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/datalink/bsc-datalink/CMakeLists.txt b/test/bacnet/datalink/bsc-datalink/CMakeLists.txt new file mode 100644 index 00000000..91105539 --- /dev/null +++ b/test/bacnet/datalink/bsc-datalink/CMakeLists.txt @@ -0,0 +1,303 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BACDL_BSC + MAX_BACFILES=4 + BSC_CONF_WSURL_MAX_LEN=128 + BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN=128 + BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM=3 + BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM=3 + BSC_CONF_WEBSOCKET_SERVERS_NUM=6 + BSC_CONF_HUB_CONNECTORS_NUM=4 + BSC_CONF_HUB_FUNCTIONS_NUM=4 + BSC_CONF_NODES_NUM=4 + BSC_CONF_NODE_SWITCHES_NUM=4 + BSC_CONF_FAILED_CONNECTION_STATUS_MAX_NUM=1 + BSC_CONF_HUB_FUNCTION_CONNECTION_STATUS_MAX_NUM=2 + BSC_CONF_NODE_SWITCH_CONNECTION_STATUS_MAX_NUM=2 + MAX_TSM_TRANSACTIONS=0 + BACDL_BSC + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +if(ZEPHYR_BASE) + message(FATAL_ERROR "ZEPHYR_BASE env variable defined. Use zephyr/CMakeLists.txt for Zephyr build") +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(STATUS "BACNet/SC datalink test: building for linux") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux) + add_compile_definitions(BACNET_PORT=linux) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/linux/websocket-cli.c + ${PORTS_DIR}/linux/websocket-srv.c + ${PORTS_DIR}/linux/websocket-global.c + ${PORTS_DIR}/linux/bsc-event.c + ${PORTS_DIR}/linux/mstimer-init.c + ${PORTS_DIR}/linux/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-datalink.c + ${SRC_DIR}/bacnet/basic/service/h_apdu.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) +elseif(WIN32) + message(STATUS "BACNet/SC datalink test: building for win32") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/win32) + add_compile_definitions(BACNET_PORT=win32) + add_compile_definitions(BACNET_STACK_STATIC_DEFINE) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Zc:preprocessor") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:preprocessor") + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/win32/websocket-cli.c + ${PORTS_DIR}/win32/websocket-srv.c + ${PORTS_DIR}/win32/websocket-global.c + ${PORTS_DIR}/win32/bsc-event.c + ${PORTS_DIR}/win32/mstimer-init.c + ${PORTS_DIR}/win32/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-datalink.c + ${SRC_DIR}/bacnet/basic/service/h_apdu.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + + # basically if you use vcpkg you should just add ${LIBWEBSOCKETS_LIBRARIES} + # into target_link_libraries() but for some reason it does not work as expected + # so that's why libs have to be hardcoded as workaround + target_link_libraries(${PROJECT_NAME} + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\websockets.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libssl.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libcrypto.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\pthreadVC3.lib + ws2_32.lib + userenv.lib + psapi.lib + iphlpapi.lib + crypt32.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\zlib.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\uv.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + comdlg32.lib + advapi32.lib + ) + +elseif(APPLE) + message(STATUS "BACNet/SC datalink test: building for APPLE") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/bsd) + execute_process ( + COMMAND bash -c "brew --prefix openssl" + OUTPUT_VARIABLE OPEN_SSL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE + ) + include_directories(${OPEN_SSL_DIR}/include) + add_compile_definitions(BACNET_PORT=bsd) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/bsd/websocket-cli.c + ${PORTS_DIR}/bsd/websocket-srv.c + ${PORTS_DIR}/bsd/websocket-global.c + ${PORTS_DIR}/bsd/bsc-event.c + ${PORTS_DIR}/bsd/mstimer-init.c + ${PORTS_DIR}/bsd/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-datalink.c + ${SRC_DIR}/bacnet/basic/service/h_apdu.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) +endif() diff --git a/test/bacnet/datalink/bsc-datalink/src/main.c b/test/bacnet/datalink/bsc-datalink/src/main.c new file mode 100644 index 00000000..9959dfc1 --- /dev/null +++ b/test/bacnet/datalink/bsc-datalink/src/main.c @@ -0,0 +1,10838 @@ +/* + * Copyright (c) 2020 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +/* @file + * @brief test of bsc-socket interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bacnet/basic/sys/debug.h" + +unsigned char ca_key[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x6f, + 0x67, + 0x49, + 0x42, + 0x41, + 0x41, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x45, + 0x41, + 0x31, + 0x66, + 0x7a, + 0x73, + 0x2b, + 0x56, + 0x62, + 0x46, + 0x39, + 0x31, + 0x65, + 0x45, + 0x66, + 0x4c, + 0x32, + 0x67, + 0x44, + 0x63, + 0x6a, + 0x79, + 0x44, + 0x47, + 0x68, + 0x62, + 0x4d, + 0x6f, + 0x44, + 0x48, + 0x6c, + 0x71, + 0x53, + 0x6d, + 0x70, + 0x78, + 0x7a, + 0x45, + 0x2b, + 0x4a, + 0x48, + 0x34, + 0x6f, + 0x46, + 0x47, + 0x46, + 0x42, + 0x31, + 0x47, + 0x48, + 0x0a, + 0x69, + 0x35, + 0x47, + 0x7a, + 0x33, + 0x73, + 0x47, + 0x51, + 0x4d, + 0x6e, + 0x41, + 0x6e, + 0x32, + 0x4c, + 0x41, + 0x6e, + 0x51, + 0x53, + 0x5a, + 0x72, + 0x46, + 0x77, + 0x6c, + 0x77, + 0x72, + 0x48, + 0x34, + 0x68, + 0x43, + 0x36, + 0x62, + 0x6d, + 0x4a, + 0x33, + 0x71, + 0x55, + 0x58, + 0x72, + 0x61, + 0x32, + 0x64, + 0x76, + 0x63, + 0x45, + 0x34, + 0x2f, + 0x32, + 0x2f, + 0x70, + 0x74, + 0x6b, + 0x34, + 0x31, + 0x47, + 0x39, + 0x68, + 0x75, + 0x45, + 0x4f, + 0x75, + 0x77, + 0x69, + 0x6b, + 0x68, + 0x0a, + 0x7a, + 0x5a, + 0x59, + 0x65, + 0x67, + 0x6b, + 0x4b, + 0x68, + 0x62, + 0x78, + 0x46, + 0x70, + 0x77, + 0x56, + 0x2f, + 0x76, + 0x48, + 0x32, + 0x48, + 0x6f, + 0x55, + 0x45, + 0x39, + 0x30, + 0x4d, + 0x43, + 0x56, + 0x7a, + 0x66, + 0x59, + 0x56, + 0x51, + 0x62, + 0x6a, + 0x38, + 0x51, + 0x2f, + 0x73, + 0x51, + 0x50, + 0x6d, + 0x79, + 0x4f, + 0x6d, + 0x64, + 0x76, + 0x46, + 0x74, + 0x4a, + 0x41, + 0x4e, + 0x47, + 0x53, + 0x2f, + 0x52, + 0x58, + 0x31, + 0x67, + 0x6b, + 0x2b, + 0x6e, + 0x7a, + 0x67, + 0x38, + 0x0a, + 0x54, + 0x4f, + 0x33, + 0x6f, + 0x4a, + 0x45, + 0x6b, + 0x70, + 0x69, + 0x6e, + 0x67, + 0x34, + 0x45, + 0x31, + 0x4c, + 0x4a, + 0x2b, + 0x63, + 0x48, + 0x66, + 0x45, + 0x66, + 0x4a, + 0x45, + 0x49, + 0x5a, + 0x4f, + 0x4d, + 0x69, + 0x4e, + 0x69, + 0x6a, + 0x31, + 0x4d, + 0x72, + 0x50, + 0x70, + 0x32, + 0x4d, + 0x63, + 0x36, + 0x4d, + 0x49, + 0x7a, + 0x54, + 0x6f, + 0x76, + 0x4c, + 0x6f, + 0x51, + 0x31, + 0x38, + 0x36, + 0x2f, + 0x68, + 0x63, + 0x59, + 0x50, + 0x36, + 0x62, + 0x4f, + 0x76, + 0x46, + 0x59, + 0x0a, + 0x6d, + 0x59, + 0x78, + 0x68, + 0x59, + 0x46, + 0x30, + 0x68, + 0x52, + 0x38, + 0x4f, + 0x57, + 0x53, + 0x4c, + 0x30, + 0x55, + 0x45, + 0x78, + 0x69, + 0x4e, + 0x6f, + 0x64, + 0x45, + 0x2f, + 0x61, + 0x70, + 0x68, + 0x7a, + 0x39, + 0x41, + 0x56, + 0x52, + 0x63, + 0x6d, + 0x79, + 0x62, + 0x76, + 0x62, + 0x4f, + 0x6c, + 0x77, + 0x32, + 0x4c, + 0x71, + 0x44, + 0x50, + 0x42, + 0x43, + 0x62, + 0x46, + 0x6b, + 0x72, + 0x50, + 0x75, + 0x4c, + 0x7a, + 0x6d, + 0x6e, + 0x47, + 0x6b, + 0x78, + 0x46, + 0x57, + 0x41, + 0x0a, + 0x45, + 0x42, + 0x65, + 0x52, + 0x38, + 0x51, + 0x43, + 0x33, + 0x77, + 0x72, + 0x57, + 0x6a, + 0x6e, + 0x6a, + 0x34, + 0x30, + 0x71, + 0x66, + 0x6e, + 0x33, + 0x78, + 0x50, + 0x6c, + 0x4e, + 0x41, + 0x73, + 0x57, + 0x42, + 0x64, + 0x7a, + 0x52, + 0x2f, + 0x64, + 0x32, + 0x43, + 0x6d, + 0x63, + 0x51, + 0x49, + 0x44, + 0x41, + 0x51, + 0x41, + 0x42, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x43, + 0x58, + 0x49, + 0x78, + 0x7a, + 0x38, + 0x76, + 0x70, + 0x7a, + 0x30, + 0x4a, + 0x59, + 0x77, + 0x64, + 0x7a, + 0x0a, + 0x70, + 0x44, + 0x4e, + 0x4b, + 0x37, + 0x42, + 0x4a, + 0x73, + 0x79, + 0x73, + 0x32, + 0x63, + 0x46, + 0x36, + 0x48, + 0x74, + 0x36, + 0x4d, + 0x39, + 0x52, + 0x4d, + 0x53, + 0x61, + 0x43, + 0x2f, + 0x39, + 0x65, + 0x76, + 0x44, + 0x55, + 0x4a, + 0x42, + 0x6a, + 0x79, + 0x47, + 0x42, + 0x31, + 0x4c, + 0x54, + 0x63, + 0x6b, + 0x4d, + 0x32, + 0x58, + 0x4b, + 0x44, + 0x49, + 0x47, + 0x79, + 0x4b, + 0x65, + 0x6b, + 0x56, + 0x50, + 0x78, + 0x34, + 0x57, + 0x6b, + 0x44, + 0x61, + 0x39, + 0x4a, + 0x4c, + 0x4f, + 0x0a, + 0x49, + 0x59, + 0x32, + 0x50, + 0x41, + 0x32, + 0x76, + 0x46, + 0x37, + 0x32, + 0x6f, + 0x4b, + 0x4b, + 0x2f, + 0x37, + 0x6c, + 0x36, + 0x31, + 0x56, + 0x57, + 0x76, + 0x63, + 0x59, + 0x6b, + 0x39, + 0x4b, + 0x68, + 0x49, + 0x71, + 0x79, + 0x37, + 0x31, + 0x66, + 0x46, + 0x61, + 0x45, + 0x7a, + 0x31, + 0x5a, + 0x49, + 0x31, + 0x61, + 0x42, + 0x36, + 0x2f, + 0x71, + 0x56, + 0x36, + 0x66, + 0x77, + 0x71, + 0x58, + 0x69, + 0x79, + 0x48, + 0x44, + 0x4a, + 0x63, + 0x7a, + 0x71, + 0x6a, + 0x2f, + 0x33, + 0x31, + 0x0a, + 0x38, + 0x45, + 0x48, + 0x48, + 0x4f, + 0x51, + 0x55, + 0x44, + 0x4d, + 0x59, + 0x34, + 0x2f, + 0x4f, + 0x55, + 0x46, + 0x2f, + 0x56, + 0x37, + 0x6f, + 0x6f, + 0x4b, + 0x64, + 0x31, + 0x33, + 0x67, + 0x35, + 0x72, + 0x7a, + 0x4c, + 0x41, + 0x50, + 0x47, + 0x4e, + 0x74, + 0x43, + 0x37, + 0x6e, + 0x2b, + 0x56, + 0x6b, + 0x6a, + 0x41, + 0x31, + 0x31, + 0x78, + 0x38, + 0x4f, + 0x6c, + 0x32, + 0x57, + 0x62, + 0x48, + 0x33, + 0x64, + 0x6a, + 0x31, + 0x78, + 0x4f, + 0x47, + 0x2f, + 0x66, + 0x50, + 0x52, + 0x57, + 0x0a, + 0x72, + 0x63, + 0x69, + 0x57, + 0x46, + 0x61, + 0x79, + 0x4a, + 0x72, + 0x33, + 0x71, + 0x56, + 0x6c, + 0x5a, + 0x2b, + 0x42, + 0x79, + 0x31, + 0x66, + 0x44, + 0x43, + 0x59, + 0x38, + 0x31, + 0x4d, + 0x73, + 0x55, + 0x56, + 0x41, + 0x65, + 0x55, + 0x57, + 0x2f, + 0x54, + 0x33, + 0x6b, + 0x39, + 0x42, + 0x6e, + 0x4b, + 0x67, + 0x36, + 0x68, + 0x71, + 0x61, + 0x33, + 0x4a, + 0x47, + 0x36, + 0x37, + 0x4e, + 0x41, + 0x36, + 0x42, + 0x72, + 0x63, + 0x7a, + 0x4e, + 0x4b, + 0x53, + 0x4b, + 0x47, + 0x79, + 0x37, + 0x0a, + 0x70, + 0x70, + 0x56, + 0x56, + 0x41, + 0x65, + 0x61, + 0x47, + 0x44, + 0x49, + 0x73, + 0x37, + 0x62, + 0x73, + 0x43, + 0x7a, + 0x52, + 0x70, + 0x45, + 0x42, + 0x43, + 0x67, + 0x4e, + 0x68, + 0x52, + 0x42, + 0x5a, + 0x45, + 0x2f, + 0x41, + 0x79, + 0x65, + 0x77, + 0x4d, + 0x56, + 0x6e, + 0x42, + 0x4a, + 0x62, + 0x4b, + 0x41, + 0x72, + 0x6e, + 0x72, + 0x79, + 0x69, + 0x69, + 0x38, + 0x2f, + 0x5a, + 0x62, + 0x56, + 0x51, + 0x55, + 0x4d, + 0x4c, + 0x56, + 0x48, + 0x32, + 0x70, + 0x68, + 0x34, + 0x46, + 0x69, + 0x0a, + 0x6b, + 0x2b, + 0x30, + 0x56, + 0x66, + 0x56, + 0x45, + 0x43, + 0x67, + 0x59, + 0x45, + 0x41, + 0x2f, + 0x5a, + 0x45, + 0x39, + 0x35, + 0x71, + 0x4b, + 0x46, + 0x6d, + 0x71, + 0x51, + 0x65, + 0x4f, + 0x36, + 0x6d, + 0x76, + 0x42, + 0x37, + 0x6b, + 0x56, + 0x6b, + 0x70, + 0x56, + 0x4b, + 0x53, + 0x58, + 0x47, + 0x75, + 0x2f, + 0x59, + 0x35, + 0x6b, + 0x46, + 0x63, + 0x70, + 0x4c, + 0x57, + 0x4f, + 0x63, + 0x35, + 0x45, + 0x56, + 0x48, + 0x4c, + 0x36, + 0x46, + 0x6d, + 0x66, + 0x7a, + 0x62, + 0x71, + 0x42, + 0x0a, + 0x41, + 0x78, + 0x6f, + 0x71, + 0x39, + 0x48, + 0x68, + 0x66, + 0x4c, + 0x79, + 0x39, + 0x61, + 0x37, + 0x4e, + 0x7a, + 0x73, + 0x61, + 0x43, + 0x77, + 0x30, + 0x66, + 0x34, + 0x56, + 0x79, + 0x5a, + 0x72, + 0x73, + 0x4f, + 0x61, + 0x73, + 0x67, + 0x42, + 0x31, + 0x43, + 0x42, + 0x5a, + 0x50, + 0x36, + 0x30, + 0x32, + 0x43, + 0x47, + 0x69, + 0x38, + 0x68, + 0x62, + 0x48, + 0x61, + 0x6a, + 0x4a, + 0x4d, + 0x77, + 0x4a, + 0x67, + 0x37, + 0x42, + 0x39, + 0x64, + 0x38, + 0x52, + 0x71, + 0x62, + 0x67, + 0x35, + 0x0a, + 0x76, + 0x61, + 0x6b, + 0x45, + 0x75, + 0x64, + 0x70, + 0x42, + 0x31, + 0x68, + 0x39, + 0x6a, + 0x6d, + 0x39, + 0x4e, + 0x4a, + 0x35, + 0x61, + 0x63, + 0x52, + 0x37, + 0x2f, + 0x76, + 0x63, + 0x64, + 0x56, + 0x70, + 0x45, + 0x67, + 0x70, + 0x6e, + 0x32, + 0x6e, + 0x50, + 0x38, + 0x34, + 0x52, + 0x6e, + 0x2f, + 0x4e, + 0x36, + 0x72, + 0x65, + 0x75, + 0x77, + 0x52, + 0x56, + 0x61, + 0x38, + 0x2f, + 0x75, + 0x2b, + 0x59, + 0x65, + 0x63, + 0x43, + 0x67, + 0x59, + 0x45, + 0x41, + 0x32, + 0x41, + 0x70, + 0x36, + 0x0a, + 0x4f, + 0x36, + 0x78, + 0x4d, + 0x32, + 0x32, + 0x70, + 0x4e, + 0x44, + 0x64, + 0x33, + 0x70, + 0x35, + 0x79, + 0x50, + 0x65, + 0x38, + 0x36, + 0x38, + 0x66, + 0x79, + 0x66, + 0x35, + 0x56, + 0x35, + 0x2f, + 0x32, + 0x4b, + 0x6f, + 0x76, + 0x34, + 0x76, + 0x46, + 0x38, + 0x77, + 0x66, + 0x69, + 0x73, + 0x61, + 0x45, + 0x63, + 0x66, + 0x62, + 0x2f, + 0x57, + 0x62, + 0x4a, + 0x52, + 0x67, + 0x77, + 0x63, + 0x36, + 0x2f, + 0x6b, + 0x55, + 0x55, + 0x6b, + 0x31, + 0x6c, + 0x4a, + 0x6e, + 0x78, + 0x6e, + 0x47, + 0x0a, + 0x50, + 0x4b, + 0x39, + 0x38, + 0x48, + 0x6d, + 0x6a, + 0x69, + 0x64, + 0x35, + 0x59, + 0x33, + 0x39, + 0x64, + 0x7a, + 0x69, + 0x6c, + 0x58, + 0x67, + 0x74, + 0x43, + 0x36, + 0x36, + 0x58, + 0x56, + 0x41, + 0x75, + 0x30, + 0x4f, + 0x4a, + 0x30, + 0x34, + 0x6c, + 0x71, + 0x69, + 0x6c, + 0x73, + 0x4d, + 0x34, + 0x42, + 0x53, + 0x43, + 0x64, + 0x6b, + 0x6c, + 0x69, + 0x6d, + 0x70, + 0x68, + 0x75, + 0x46, + 0x73, + 0x7a, + 0x4a, + 0x33, + 0x50, + 0x4f, + 0x74, + 0x36, + 0x55, + 0x37, + 0x68, + 0x67, + 0x42, + 0x0a, + 0x31, + 0x71, + 0x73, + 0x4b, + 0x4d, + 0x4b, + 0x61, + 0x75, + 0x74, + 0x6a, + 0x33, + 0x2f, + 0x38, + 0x76, + 0x32, + 0x53, + 0x77, + 0x63, + 0x71, + 0x36, + 0x46, + 0x49, + 0x78, + 0x36, + 0x6c, + 0x36, + 0x46, + 0x6f, + 0x37, + 0x71, + 0x2b, + 0x73, + 0x70, + 0x74, + 0x56, + 0x72, + 0x57, + 0x65, + 0x63, + 0x43, + 0x67, + 0x59, + 0x42, + 0x2b, + 0x36, + 0x63, + 0x31, + 0x54, + 0x76, + 0x4a, + 0x43, + 0x6d, + 0x66, + 0x2f, + 0x4a, + 0x70, + 0x35, + 0x6c, + 0x6f, + 0x6d, + 0x77, + 0x57, + 0x71, + 0x63, + 0x0a, + 0x76, + 0x59, + 0x41, + 0x37, + 0x46, + 0x6c, + 0x32, + 0x42, + 0x70, + 0x31, + 0x31, + 0x4d, + 0x30, + 0x72, + 0x32, + 0x33, + 0x74, + 0x37, + 0x4f, + 0x47, + 0x69, + 0x61, + 0x78, + 0x48, + 0x6c, + 0x57, + 0x51, + 0x34, + 0x73, + 0x6c, + 0x71, + 0x55, + 0x56, + 0x4f, + 0x71, + 0x66, + 0x42, + 0x67, + 0x69, + 0x4f, + 0x4d, + 0x32, + 0x4f, + 0x4e, + 0x48, + 0x6c, + 0x35, + 0x74, + 0x48, + 0x59, + 0x4d, + 0x42, + 0x4f, + 0x4b, + 0x65, + 0x7a, + 0x35, + 0x33, + 0x67, + 0x6c, + 0x31, + 0x67, + 0x6d, + 0x6b, + 0x0a, + 0x52, + 0x4c, + 0x53, + 0x6d, + 0x2f, + 0x47, + 0x6b, + 0x49, + 0x2b, + 0x48, + 0x4d, + 0x7a, + 0x62, + 0x33, + 0x74, + 0x31, + 0x31, + 0x4d, + 0x33, + 0x4b, + 0x6e, + 0x71, + 0x52, + 0x53, + 0x44, + 0x64, + 0x35, + 0x6e, + 0x56, + 0x6b, + 0x41, + 0x41, + 0x50, + 0x37, + 0x4b, + 0x50, + 0x32, + 0x30, + 0x41, + 0x4d, + 0x6a, + 0x68, + 0x56, + 0x72, + 0x44, + 0x75, + 0x76, + 0x7a, + 0x75, + 0x42, + 0x56, + 0x77, + 0x53, + 0x6c, + 0x31, + 0x6a, + 0x6c, + 0x31, + 0x53, + 0x6e, + 0x45, + 0x61, + 0x79, + 0x76, + 0x0a, + 0x6b, + 0x38, + 0x5a, + 0x30, + 0x38, + 0x73, + 0x37, + 0x37, + 0x35, + 0x67, + 0x66, + 0x66, + 0x75, + 0x48, + 0x4b, + 0x58, + 0x6e, + 0x36, + 0x38, + 0x41, + 0x6a, + 0x51, + 0x4b, + 0x42, + 0x67, + 0x46, + 0x58, + 0x73, + 0x4c, + 0x4e, + 0x73, + 0x6f, + 0x31, + 0x74, + 0x52, + 0x35, + 0x50, + 0x62, + 0x59, + 0x6a, + 0x4b, + 0x56, + 0x44, + 0x39, + 0x69, + 0x6b, + 0x47, + 0x65, + 0x78, + 0x2b, + 0x54, + 0x64, + 0x57, + 0x36, + 0x74, + 0x4e, + 0x77, + 0x6d, + 0x4b, + 0x36, + 0x39, + 0x31, + 0x33, + 0x65, + 0x0a, + 0x6d, + 0x44, + 0x6a, + 0x6f, + 0x5a, + 0x57, + 0x71, + 0x79, + 0x45, + 0x72, + 0x4c, + 0x49, + 0x34, + 0x66, + 0x52, + 0x62, + 0x33, + 0x74, + 0x47, + 0x63, + 0x42, + 0x65, + 0x66, + 0x6f, + 0x6e, + 0x67, + 0x68, + 0x43, + 0x42, + 0x76, + 0x37, + 0x42, + 0x79, + 0x48, + 0x71, + 0x4c, + 0x75, + 0x6d, + 0x35, + 0x58, + 0x64, + 0x32, + 0x41, + 0x34, + 0x66, + 0x6f, + 0x46, + 0x31, + 0x37, + 0x32, + 0x78, + 0x79, + 0x2f, + 0x73, + 0x71, + 0x31, + 0x63, + 0x50, + 0x4d, + 0x48, + 0x54, + 0x4b, + 0x64, + 0x57, + 0x0a, + 0x34, + 0x62, + 0x63, + 0x6b, + 0x35, + 0x34, + 0x75, + 0x62, + 0x35, + 0x7a, + 0x78, + 0x31, + 0x79, + 0x32, + 0x2f, + 0x53, + 0x6e, + 0x69, + 0x50, + 0x76, + 0x4b, + 0x36, + 0x6b, + 0x39, + 0x4e, + 0x7a, + 0x78, + 0x4f, + 0x6e, + 0x67, + 0x53, + 0x54, + 0x75, + 0x42, + 0x54, + 0x4c, + 0x5a, + 0x6a, + 0x63, + 0x6a, + 0x42, + 0x33, + 0x58, + 0x4c, + 0x39, + 0x68, + 0x39, + 0x50, + 0x45, + 0x70, + 0x7a, + 0x7a, + 0x6c, + 0x68, + 0x70, + 0x53, + 0x58, + 0x74, + 0x70, + 0x33, + 0x55, + 0x68, + 0x4a, + 0x30, + 0x0a, + 0x56, + 0x53, + 0x4c, + 0x48, + 0x41, + 0x6f, + 0x47, + 0x41, + 0x44, + 0x4e, + 0x79, + 0x6b, + 0x52, + 0x50, + 0x32, + 0x55, + 0x38, + 0x34, + 0x78, + 0x70, + 0x50, + 0x6d, + 0x36, + 0x77, + 0x31, + 0x69, + 0x36, + 0x72, + 0x47, + 0x48, + 0x71, + 0x50, + 0x38, + 0x67, + 0x65, + 0x2b, + 0x4f, + 0x6f, + 0x6f, + 0x77, + 0x69, + 0x47, + 0x47, + 0x4b, + 0x48, + 0x54, + 0x45, + 0x48, + 0x72, + 0x56, + 0x39, + 0x53, + 0x79, + 0x66, + 0x6c, + 0x2f, + 0x63, + 0x42, + 0x39, + 0x55, + 0x4d, + 0x65, + 0x79, + 0x43, + 0x0a, + 0x45, + 0x49, + 0x68, + 0x46, + 0x37, + 0x4e, + 0x71, + 0x6b, + 0x35, + 0x76, + 0x2b, + 0x4d, + 0x68, + 0x2b, + 0x50, + 0x31, + 0x52, + 0x49, + 0x52, + 0x71, + 0x35, + 0x39, + 0x66, + 0x4d, + 0x41, + 0x34, + 0x72, + 0x4e, + 0x7a, + 0x6d, + 0x61, + 0x78, + 0x65, + 0x6b, + 0x35, + 0x72, + 0x4b, + 0x36, + 0x45, + 0x50, + 0x67, + 0x31, + 0x62, + 0x2f, + 0x67, + 0x78, + 0x7a, + 0x45, + 0x73, + 0x4c, + 0x56, + 0x45, + 0x45, + 0x42, + 0x58, + 0x49, + 0x51, + 0x35, + 0x57, + 0x57, + 0x6e, + 0x58, + 0x6a, + 0x52, + 0x0a, + 0x75, + 0x2b, + 0x5a, + 0x56, + 0x49, + 0x37, + 0x33, + 0x68, + 0x62, + 0x36, + 0x79, + 0x56, + 0x56, + 0x70, + 0x75, + 0x6f, + 0x4e, + 0x68, + 0x52, + 0x43, + 0x67, + 0x59, + 0x67, + 0x45, + 0x70, + 0x48, + 0x6b, + 0x6e, + 0x47, + 0x43, + 0x45, + 0x76, + 0x62, + 0x55, + 0x52, + 0x4b, + 0x4f, + 0x68, + 0x79, + 0x44, + 0x4d, + 0x4e, + 0x69, + 0x6c, + 0x49, + 0x6a, + 0x31, + 0x6d, + 0x71, + 0x44, + 0x4d, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char ca_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x44, + 0x48, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x51, + 0x43, + 0x43, + 0x51, + 0x44, + 0x43, + 0x44, + 0x35, + 0x33, + 0x59, + 0x5a, + 0x4a, + 0x4a, + 0x37, + 0x6c, + 0x6a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x6b, + 0x71, + 0x68, + 0x6b, + 0x69, + 0x47, + 0x39, + 0x77, + 0x30, + 0x42, + 0x41, + 0x51, + 0x73, + 0x46, + 0x41, + 0x44, + 0x42, + 0x50, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x63, + 0x54, + 0x43, + 0x56, + 0x68, + 0x70, + 0x59, + 0x57, + 0x39, + 0x69, + 0x61, + 0x58, + 0x52, + 0x68, + 0x62, + 0x6a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x0a, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x41, + 0x67, + 0x46, + 0x77, + 0x30, + 0x79, + 0x4d, + 0x6a, + 0x41, + 0x33, + 0x4d, + 0x44, + 0x59, + 0x78, + 0x4d, + 0x54, + 0x49, + 0x30, + 0x4d, + 0x6a, + 0x42, + 0x61, + 0x47, + 0x41, + 0x38, + 0x79, + 0x4d, + 0x44, + 0x55, + 0x77, + 0x4d, + 0x44, + 0x63, + 0x78, + 0x4f, + 0x54, + 0x45, + 0x78, + 0x0a, + 0x4d, + 0x6a, + 0x51, + 0x79, + 0x4d, + 0x46, + 0x6f, + 0x77, + 0x54, + 0x7a, + 0x45, + 0x62, + 0x4d, + 0x42, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x68, + 0x4d, + 0x53, + 0x62, + 0x47, + 0x6c, + 0x69, + 0x64, + 0x32, + 0x56, + 0x69, + 0x63, + 0x32, + 0x39, + 0x6a, + 0x61, + 0x32, + 0x56, + 0x30, + 0x63, + 0x79, + 0x31, + 0x30, + 0x5a, + 0x58, + 0x4e, + 0x30, + 0x4d, + 0x52, + 0x49, + 0x77, + 0x45, + 0x41, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x48, + 0x45, + 0x77, + 0x6c, + 0x59, + 0x0a, + 0x61, + 0x57, + 0x46, + 0x76, + 0x59, + 0x6d, + 0x6c, + 0x30, + 0x59, + 0x57, + 0x34, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x67, + 0x54, + 0x42, + 0x6c, + 0x52, + 0x68, + 0x61, + 0x58, + 0x42, + 0x6c, + 0x61, + 0x54, + 0x45, + 0x4c, + 0x4d, + 0x41, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x68, + 0x4d, + 0x43, + 0x56, + 0x46, + 0x63, + 0x77, + 0x67, + 0x67, + 0x45, + 0x69, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x0a, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x41, + 0x51, + 0x55, + 0x41, + 0x41, + 0x34, + 0x49, + 0x42, + 0x44, + 0x77, + 0x41, + 0x77, + 0x67, + 0x67, + 0x45, + 0x4b, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x51, + 0x44, + 0x56, + 0x2f, + 0x4f, + 0x7a, + 0x35, + 0x56, + 0x73, + 0x58, + 0x33, + 0x56, + 0x34, + 0x52, + 0x38, + 0x76, + 0x61, + 0x41, + 0x4e, + 0x79, + 0x50, + 0x49, + 0x4d, + 0x61, + 0x46, + 0x73, + 0x79, + 0x67, + 0x4d, + 0x65, + 0x57, + 0x70, + 0x4b, + 0x61, + 0x6e, + 0x0a, + 0x48, + 0x4d, + 0x54, + 0x34, + 0x6b, + 0x66, + 0x69, + 0x67, + 0x55, + 0x59, + 0x55, + 0x48, + 0x55, + 0x59, + 0x65, + 0x4c, + 0x6b, + 0x62, + 0x50, + 0x65, + 0x77, + 0x5a, + 0x41, + 0x79, + 0x63, + 0x43, + 0x66, + 0x59, + 0x73, + 0x43, + 0x64, + 0x42, + 0x4a, + 0x6d, + 0x73, + 0x58, + 0x43, + 0x58, + 0x43, + 0x73, + 0x66, + 0x69, + 0x45, + 0x4c, + 0x70, + 0x75, + 0x59, + 0x6e, + 0x65, + 0x70, + 0x52, + 0x65, + 0x74, + 0x72, + 0x5a, + 0x32, + 0x39, + 0x77, + 0x54, + 0x6a, + 0x2f, + 0x62, + 0x2b, + 0x6d, + 0x0a, + 0x32, + 0x54, + 0x6a, + 0x55, + 0x62, + 0x32, + 0x47, + 0x34, + 0x51, + 0x36, + 0x37, + 0x43, + 0x4b, + 0x53, + 0x48, + 0x4e, + 0x6c, + 0x68, + 0x36, + 0x43, + 0x51, + 0x71, + 0x46, + 0x76, + 0x45, + 0x57, + 0x6e, + 0x42, + 0x58, + 0x2b, + 0x38, + 0x66, + 0x59, + 0x65, + 0x68, + 0x51, + 0x54, + 0x33, + 0x51, + 0x77, + 0x4a, + 0x58, + 0x4e, + 0x39, + 0x68, + 0x56, + 0x42, + 0x75, + 0x50, + 0x78, + 0x44, + 0x2b, + 0x78, + 0x41, + 0x2b, + 0x62, + 0x49, + 0x36, + 0x5a, + 0x32, + 0x38, + 0x57, + 0x30, + 0x6b, + 0x0a, + 0x41, + 0x30, + 0x5a, + 0x4c, + 0x39, + 0x46, + 0x66, + 0x57, + 0x43, + 0x54, + 0x36, + 0x66, + 0x4f, + 0x44, + 0x78, + 0x4d, + 0x37, + 0x65, + 0x67, + 0x6b, + 0x53, + 0x53, + 0x6d, + 0x4b, + 0x65, + 0x44, + 0x67, + 0x54, + 0x55, + 0x73, + 0x6e, + 0x35, + 0x77, + 0x64, + 0x38, + 0x52, + 0x38, + 0x6b, + 0x51, + 0x68, + 0x6b, + 0x34, + 0x79, + 0x49, + 0x32, + 0x4b, + 0x50, + 0x55, + 0x79, + 0x73, + 0x2b, + 0x6e, + 0x59, + 0x78, + 0x7a, + 0x6f, + 0x77, + 0x6a, + 0x4e, + 0x4f, + 0x69, + 0x38, + 0x75, + 0x68, + 0x0a, + 0x44, + 0x58, + 0x7a, + 0x72, + 0x2b, + 0x46, + 0x78, + 0x67, + 0x2f, + 0x70, + 0x73, + 0x36, + 0x38, + 0x56, + 0x69, + 0x5a, + 0x6a, + 0x47, + 0x46, + 0x67, + 0x58, + 0x53, + 0x46, + 0x48, + 0x77, + 0x35, + 0x5a, + 0x49, + 0x76, + 0x52, + 0x51, + 0x54, + 0x47, + 0x49, + 0x32, + 0x68, + 0x30, + 0x54, + 0x39, + 0x71, + 0x6d, + 0x48, + 0x50, + 0x30, + 0x42, + 0x56, + 0x46, + 0x79, + 0x62, + 0x4a, + 0x75, + 0x39, + 0x73, + 0x36, + 0x58, + 0x44, + 0x59, + 0x75, + 0x6f, + 0x4d, + 0x38, + 0x45, + 0x4a, + 0x73, + 0x0a, + 0x57, + 0x53, + 0x73, + 0x2b, + 0x34, + 0x76, + 0x4f, + 0x61, + 0x63, + 0x61, + 0x54, + 0x45, + 0x56, + 0x59, + 0x41, + 0x51, + 0x46, + 0x35, + 0x48, + 0x78, + 0x41, + 0x4c, + 0x66, + 0x43, + 0x74, + 0x61, + 0x4f, + 0x65, + 0x50, + 0x6a, + 0x53, + 0x70, + 0x2b, + 0x66, + 0x66, + 0x45, + 0x2b, + 0x55, + 0x30, + 0x43, + 0x78, + 0x59, + 0x46, + 0x33, + 0x4e, + 0x48, + 0x39, + 0x33, + 0x59, + 0x4b, + 0x5a, + 0x78, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x0a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x49, + 0x6d, + 0x4a, + 0x76, + 0x71, + 0x6e, + 0x70, + 0x50, + 0x64, + 0x53, + 0x75, + 0x46, + 0x4f, + 0x5a, + 0x54, + 0x36, + 0x76, + 0x74, + 0x48, + 0x31, + 0x70, + 0x4a, + 0x43, + 0x45, + 0x76, + 0x4a, + 0x39, + 0x62, + 0x53, + 0x78, + 0x31, + 0x43, + 0x41, + 0x76, + 0x36, + 0x46, + 0x34, + 0x46, + 0x44, + 0x6f, + 0x77, + 0x4b, + 0x77, + 0x0a, + 0x43, + 0x71, + 0x4b, + 0x53, + 0x6c, + 0x59, + 0x45, + 0x6a, + 0x72, + 0x49, + 0x4c, + 0x64, + 0x6c, + 0x42, + 0x30, + 0x39, + 0x32, + 0x31, + 0x4f, + 0x54, + 0x30, + 0x76, + 0x61, + 0x68, + 0x33, + 0x6c, + 0x55, + 0x76, + 0x2f, + 0x6b, + 0x47, + 0x4e, + 0x4c, + 0x76, + 0x58, + 0x55, + 0x71, + 0x54, + 0x69, + 0x42, + 0x61, + 0x6b, + 0x77, + 0x66, + 0x52, + 0x47, + 0x30, + 0x39, + 0x61, + 0x49, + 0x45, + 0x6e, + 0x53, + 0x68, + 0x6d, + 0x79, + 0x6f, + 0x30, + 0x68, + 0x63, + 0x65, + 0x4f, + 0x68, + 0x33, + 0x0a, + 0x4f, + 0x31, + 0x4b, + 0x4b, + 0x59, + 0x76, + 0x4a, + 0x32, + 0x6a, + 0x4a, + 0x47, + 0x6b, + 0x36, + 0x50, + 0x6c, + 0x52, + 0x78, + 0x65, + 0x53, + 0x67, + 0x37, + 0x64, + 0x35, + 0x4d, + 0x69, + 0x37, + 0x58, + 0x67, + 0x6e, + 0x41, + 0x64, + 0x65, + 0x61, + 0x78, + 0x77, + 0x68, + 0x75, + 0x76, + 0x5a, + 0x5a, + 0x6d, + 0x61, + 0x49, + 0x7a, + 0x62, + 0x68, + 0x41, + 0x57, + 0x50, + 0x38, + 0x71, + 0x67, + 0x49, + 0x30, + 0x36, + 0x50, + 0x32, + 0x52, + 0x42, + 0x53, + 0x35, + 0x42, + 0x4a, + 0x76, + 0x0a, + 0x72, + 0x44, + 0x44, + 0x33, + 0x44, + 0x68, + 0x77, + 0x38, + 0x4e, + 0x38, + 0x47, + 0x77, + 0x42, + 0x44, + 0x31, + 0x52, + 0x59, + 0x32, + 0x79, + 0x4b, + 0x72, + 0x79, + 0x46, + 0x51, + 0x2b, + 0x34, + 0x55, + 0x32, + 0x31, + 0x45, + 0x72, + 0x73, + 0x77, + 0x2f, + 0x33, + 0x38, + 0x63, + 0x59, + 0x38, + 0x55, + 0x41, + 0x46, + 0x54, + 0x6b, + 0x67, + 0x33, + 0x72, + 0x57, + 0x72, + 0x34, + 0x44, + 0x57, + 0x78, + 0x36, + 0x74, + 0x6e, + 0x49, + 0x66, + 0x64, + 0x72, + 0x4e, + 0x31, + 0x49, + 0x49, + 0x0a, + 0x70, + 0x52, + 0x71, + 0x53, + 0x78, + 0x48, + 0x51, + 0x57, + 0x34, + 0x6b, + 0x5a, + 0x61, + 0x67, + 0x31, + 0x75, + 0x5a, + 0x64, + 0x46, + 0x63, + 0x53, + 0x69, + 0x61, + 0x59, + 0x54, + 0x2b, + 0x65, + 0x62, + 0x4c, + 0x57, + 0x56, + 0x58, + 0x41, + 0x7a, + 0x62, + 0x66, + 0x4a, + 0x4c, + 0x4f, + 0x56, + 0x38, + 0x45, + 0x46, + 0x46, + 0x33, + 0x52, + 0x44, + 0x43, + 0x2f, + 0x41, + 0x33, + 0x55, + 0x33, + 0x56, + 0x63, + 0x4f, + 0x52, + 0x44, + 0x42, + 0x44, + 0x57, + 0x44, + 0x68, + 0x6d, + 0x68, + 0x0a, + 0x38, + 0x72, + 0x34, + 0x54, + 0x2b, + 0x36, + 0x46, + 0x67, + 0x67, + 0x59, + 0x4c, + 0x32, + 0x65, + 0x6a, + 0x4d, + 0x6d, + 0x66, + 0x77, + 0x62, + 0x65, + 0x77, + 0x7a, + 0x49, + 0x2b, + 0x48, + 0x6d, + 0x34, + 0x53, + 0x69, + 0x64, + 0x67, + 0x4f, + 0x78, + 0x4c, + 0x66, + 0x31, + 0x46, + 0x78, + 0x31, + 0x64, + 0x32, + 0x76, + 0x34, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char client_key[] = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x52, + 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, + 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x4a, + 0x4b, 0x51, 0x49, 0x42, 0x41, 0x41, 0x4b, 0x43, 0x41, 0x67, 0x45, 0x41, + 0x72, 0x77, 0x53, 0x79, 0x4e, 0x6e, 0x68, 0x66, 0x76, 0x44, 0x69, 0x63, + 0x47, 0x4a, 0x4f, 0x42, 0x66, 0x55, 0x51, 0x4c, 0x47, 0x45, 0x70, 0x4b, + 0x43, 0x32, 0x2f, 0x4e, 0x32, 0x35, 0x54, 0x6f, 0x32, 0x4b, 0x66, 0x36, + 0x36, 0x7a, 0x6b, 0x44, 0x62, 0x33, 0x6d, 0x34, 0x49, 0x50, 0x77, 0x4c, + 0x0a, 0x63, 0x63, 0x78, 0x47, 0x57, 0x48, 0x57, 0x75, 0x65, 0x78, 0x69, + 0x70, 0x6d, 0x76, 0x41, 0x77, 0x46, 0x2b, 0x58, 0x32, 0x36, 0x46, 0x47, + 0x7a, 0x35, 0x66, 0x4a, 0x64, 0x71, 0x38, 0x46, 0x68, 0x77, 0x6c, 0x71, + 0x2b, 0x6e, 0x71, 0x6a, 0x34, 0x50, 0x68, 0x6c, 0x2f, 0x51, 0x2b, 0x77, + 0x55, 0x42, 0x6c, 0x51, 0x2b, 0x58, 0x4d, 0x6a, 0x71, 0x49, 0x70, 0x77, + 0x79, 0x7a, 0x70, 0x5a, 0x6d, 0x0a, 0x34, 0x48, 0x6d, 0x2b, 0x4f, 0x4f, + 0x43, 0x4b, 0x66, 0x73, 0x79, 0x58, 0x64, 0x6d, 0x4c, 0x4c, 0x70, 0x46, + 0x79, 0x56, 0x6b, 0x34, 0x72, 0x72, 0x32, 0x47, 0x51, 0x34, 0x4b, 0x46, + 0x53, 0x51, 0x58, 0x51, 0x58, 0x79, 0x5a, 0x78, 0x78, 0x62, 0x4e, 0x46, + 0x64, 0x67, 0x6e, 0x43, 0x72, 0x76, 0x6d, 0x74, 0x6b, 0x49, 0x42, 0x51, + 0x62, 0x76, 0x6d, 0x6e, 0x42, 0x79, 0x50, 0x2b, 0x77, 0x6e, 0x0a, 0x65, + 0x7a, 0x68, 0x55, 0x51, 0x4b, 0x4c, 0x6b, 0x6d, 0x51, 0x50, 0x32, 0x53, + 0x6a, 0x37, 0x65, 0x52, 0x7a, 0x4c, 0x6f, 0x79, 0x58, 0x32, 0x71, 0x41, + 0x49, 0x75, 0x43, 0x46, 0x59, 0x62, 0x57, 0x41, 0x36, 0x44, 0x57, 0x53, + 0x39, 0x50, 0x4c, 0x56, 0x75, 0x67, 0x71, 0x53, 0x76, 0x4c, 0x65, 0x51, + 0x71, 0x7a, 0x31, 0x74, 0x70, 0x32, 0x66, 0x5a, 0x70, 0x68, 0x42, 0x64, + 0x6a, 0x66, 0x49, 0x0a, 0x6c, 0x65, 0x44, 0x42, 0x75, 0x7a, 0x59, 0x67, + 0x42, 0x30, 0x61, 0x63, 0x39, 0x79, 0x44, 0x48, 0x31, 0x6d, 0x65, 0x65, + 0x51, 0x4f, 0x43, 0x4e, 0x50, 0x61, 0x7a, 0x52, 0x41, 0x63, 0x57, 0x57, + 0x77, 0x75, 0x2b, 0x6f, 0x56, 0x57, 0x6d, 0x45, 0x42, 0x45, 0x6b, 0x50, + 0x44, 0x49, 0x31, 0x70, 0x68, 0x75, 0x6e, 0x44, 0x39, 0x33, 0x33, 0x47, + 0x31, 0x52, 0x63, 0x57, 0x64, 0x6d, 0x51, 0x55, 0x0a, 0x72, 0x37, 0x6b, + 0x48, 0x65, 0x50, 0x4d, 0x50, 0x77, 0x35, 0x70, 0x33, 0x42, 0x7a, 0x2b, + 0x71, 0x6a, 0x71, 0x61, 0x48, 0x2b, 0x36, 0x6f, 0x43, 0x54, 0x68, 0x5a, + 0x43, 0x6f, 0x2b, 0x59, 0x55, 0x39, 0x68, 0x51, 0x2f, 0x34, 0x2b, 0x48, + 0x77, 0x4d, 0x4e, 0x79, 0x38, 0x51, 0x73, 0x79, 0x6a, 0x61, 0x6d, 0x66, + 0x37, 0x31, 0x7a, 0x35, 0x67, 0x34, 0x43, 0x68, 0x65, 0x32, 0x34, 0x51, + 0x50, 0x0a, 0x66, 0x63, 0x6e, 0x30, 0x33, 0x57, 0x79, 0x2f, 0x5a, 0x52, + 0x61, 0x75, 0x53, 0x67, 0x73, 0x51, 0x6e, 0x79, 0x7a, 0x68, 0x72, 0x5a, + 0x7a, 0x39, 0x57, 0x4a, 0x35, 0x2b, 0x38, 0x77, 0x55, 0x71, 0x6a, 0x34, + 0x34, 0x51, 0x38, 0x45, 0x36, 0x70, 0x59, 0x52, 0x4c, 0x30, 0x68, 0x34, + 0x78, 0x65, 0x49, 0x73, 0x45, 0x4f, 0x76, 0x2b, 0x49, 0x77, 0x4e, 0x62, + 0x56, 0x59, 0x6d, 0x38, 0x57, 0x31, 0x0a, 0x62, 0x48, 0x5a, 0x62, 0x64, + 0x57, 0x31, 0x53, 0x65, 0x4d, 0x77, 0x70, 0x74, 0x45, 0x62, 0x37, 0x47, + 0x50, 0x67, 0x55, 0x61, 0x4f, 0x2f, 0x79, 0x4a, 0x7a, 0x2f, 0x68, 0x4b, + 0x71, 0x63, 0x72, 0x70, 0x49, 0x4a, 0x37, 0x2f, 0x49, 0x6f, 0x61, 0x41, + 0x54, 0x4b, 0x2b, 0x6a, 0x39, 0x70, 0x38, 0x32, 0x62, 0x33, 0x51, 0x69, + 0x32, 0x2f, 0x55, 0x54, 0x70, 0x71, 0x49, 0x44, 0x49, 0x67, 0x79, 0x0a, + 0x79, 0x5a, 0x41, 0x4c, 0x55, 0x6b, 0x36, 0x68, 0x57, 0x48, 0x39, 0x37, + 0x53, 0x6f, 0x4d, 0x61, 0x71, 0x6b, 0x68, 0x55, 0x6a, 0x73, 0x44, 0x38, + 0x36, 0x7a, 0x50, 0x32, 0x31, 0x4b, 0x48, 0x45, 0x4e, 0x35, 0x47, 0x35, + 0x6b, 0x58, 0x33, 0x36, 0x41, 0x77, 0x79, 0x66, 0x53, 0x45, 0x41, 0x6d, + 0x52, 0x76, 0x6c, 0x48, 0x63, 0x7a, 0x47, 0x6a, 0x78, 0x66, 0x71, 0x51, + 0x50, 0x42, 0x71, 0x7a, 0x0a, 0x4d, 0x63, 0x65, 0x39, 0x46, 0x53, 0x78, + 0x39, 0x4e, 0x2b, 0x52, 0x39, 0x62, 0x77, 0x2f, 0x56, 0x42, 0x73, 0x66, + 0x43, 0x63, 0x7a, 0x46, 0x35, 0x4c, 0x4b, 0x6d, 0x37, 0x52, 0x6f, 0x6a, + 0x48, 0x44, 0x35, 0x7a, 0x77, 0x4f, 0x62, 0x4f, 0x33, 0x36, 0x57, 0x7a, + 0x74, 0x75, 0x50, 0x61, 0x62, 0x62, 0x6f, 0x66, 0x7a, 0x30, 0x6d, 0x46, + 0x69, 0x56, 0x79, 0x75, 0x46, 0x33, 0x63, 0x4f, 0x62, 0x0a, 0x2f, 0x74, + 0x71, 0x47, 0x56, 0x56, 0x39, 0x33, 0x6e, 0x49, 0x72, 0x41, 0x42, 0x62, + 0x79, 0x2f, 0x44, 0x62, 0x42, 0x62, 0x49, 0x51, 0x4a, 0x58, 0x4c, 0x73, + 0x73, 0x74, 0x6e, 0x70, 0x77, 0x70, 0x6c, 0x5a, 0x62, 0x61, 0x5a, 0x6b, + 0x59, 0x71, 0x50, 0x76, 0x4f, 0x37, 0x47, 0x52, 0x56, 0x37, 0x30, 0x48, + 0x38, 0x77, 0x4e, 0x4f, 0x35, 0x4f, 0x66, 0x33, 0x4d, 0x43, 0x41, 0x77, + 0x45, 0x41, 0x0a, 0x41, 0x51, 0x4b, 0x43, 0x41, 0x67, 0x42, 0x6c, 0x78, + 0x33, 0x56, 0x62, 0x39, 0x2b, 0x53, 0x30, 0x73, 0x4c, 0x63, 0x57, 0x45, + 0x37, 0x48, 0x61, 0x42, 0x78, 0x66, 0x73, 0x71, 0x45, 0x63, 0x6e, 0x48, + 0x33, 0x32, 0x33, 0x6c, 0x49, 0x46, 0x55, 0x66, 0x56, 0x75, 0x4f, 0x4c, + 0x7a, 0x6d, 0x77, 0x4f, 0x6a, 0x69, 0x35, 0x39, 0x64, 0x6b, 0x78, 0x39, + 0x48, 0x6c, 0x30, 0x4e, 0x2f, 0x75, 0x66, 0x0a, 0x32, 0x6c, 0x66, 0x48, + 0x6f, 0x71, 0x5a, 0x56, 0x50, 0x34, 0x61, 0x32, 0x30, 0x38, 0x79, 0x71, + 0x6a, 0x4b, 0x65, 0x73, 0x6d, 0x6d, 0x6b, 0x66, 0x66, 0x57, 0x59, 0x64, + 0x48, 0x6d, 0x59, 0x2b, 0x74, 0x74, 0x55, 0x72, 0x79, 0x72, 0x35, 0x61, + 0x62, 0x2b, 0x4e, 0x55, 0x55, 0x67, 0x4c, 0x57, 0x33, 0x62, 0x38, 0x75, + 0x4a, 0x49, 0x6a, 0x76, 0x51, 0x64, 0x30, 0x39, 0x64, 0x6c, 0x63, 0x55, + 0x0a, 0x4a, 0x69, 0x5a, 0x75, 0x30, 0x6b, 0x56, 0x61, 0x37, 0x2f, 0x79, + 0x4d, 0x4d, 0x4e, 0x32, 0x32, 0x6d, 0x5a, 0x47, 0x4f, 0x34, 0x70, 0x36, + 0x52, 0x65, 0x6b, 0x50, 0x64, 0x63, 0x73, 0x41, 0x58, 0x55, 0x44, 0x6a, + 0x2b, 0x6d, 0x48, 0x6c, 0x2b, 0x73, 0x33, 0x66, 0x57, 0x64, 0x4a, 0x49, + 0x69, 0x58, 0x67, 0x49, 0x53, 0x36, 0x6d, 0x4b, 0x4c, 0x5a, 0x64, 0x61, + 0x5a, 0x51, 0x43, 0x46, 0x77, 0x0a, 0x57, 0x72, 0x31, 0x2f, 0x72, 0x38, + 0x2f, 0x54, 0x31, 0x2b, 0x64, 0x49, 0x52, 0x61, 0x76, 0x33, 0x5a, 0x53, + 0x6e, 0x68, 0x47, 0x75, 0x69, 0x61, 0x63, 0x34, 0x34, 0x72, 0x79, 0x70, + 0x38, 0x56, 0x69, 0x79, 0x34, 0x4e, 0x4a, 0x2b, 0x2f, 0x5a, 0x46, 0x6e, + 0x78, 0x4f, 0x46, 0x70, 0x76, 0x38, 0x4c, 0x63, 0x37, 0x6a, 0x30, 0x4d, + 0x7a, 0x31, 0x58, 0x42, 0x39, 0x4e, 0x6e, 0x38, 0x78, 0x41, 0x0a, 0x62, + 0x76, 0x41, 0x5a, 0x52, 0x78, 0x62, 0x76, 0x75, 0x4a, 0x4a, 0x76, 0x61, + 0x43, 0x61, 0x37, 0x46, 0x79, 0x54, 0x30, 0x77, 0x74, 0x4e, 0x4a, 0x79, + 0x64, 0x55, 0x36, 0x31, 0x6f, 0x48, 0x50, 0x66, 0x43, 0x30, 0x6b, 0x50, + 0x35, 0x68, 0x2b, 0x76, 0x4c, 0x4d, 0x5a, 0x32, 0x69, 0x73, 0x6e, 0x41, + 0x4b, 0x59, 0x76, 0x63, 0x30, 0x51, 0x55, 0x62, 0x4b, 0x58, 0x4c, 0x30, + 0x49, 0x33, 0x36, 0x0a, 0x55, 0x6e, 0x6d, 0x6c, 0x33, 0x59, 0x42, 0x79, + 0x4e, 0x4b, 0x59, 0x66, 0x31, 0x46, 0x35, 0x43, 0x79, 0x75, 0x38, 0x32, + 0x49, 0x54, 0x64, 0x50, 0x59, 0x37, 0x64, 0x43, 0x39, 0x39, 0x55, 0x6f, + 0x6e, 0x2f, 0x47, 0x35, 0x66, 0x52, 0x55, 0x56, 0x6e, 0x61, 0x6d, 0x58, + 0x34, 0x41, 0x2b, 0x4e, 0x62, 0x66, 0x47, 0x45, 0x68, 0x6c, 0x4a, 0x32, + 0x58, 0x58, 0x55, 0x51, 0x58, 0x54, 0x69, 0x68, 0x0a, 0x41, 0x77, 0x46, + 0x75, 0x5a, 0x48, 0x72, 0x54, 0x48, 0x41, 0x4a, 0x58, 0x39, 0x64, 0x6f, + 0x77, 0x34, 0x4c, 0x51, 0x51, 0x75, 0x49, 0x68, 0x44, 0x50, 0x4f, 0x59, + 0x55, 0x4e, 0x7a, 0x5a, 0x6f, 0x34, 0x57, 0x30, 0x34, 0x44, 0x44, 0x4a, + 0x2b, 0x62, 0x75, 0x66, 0x34, 0x67, 0x7a, 0x70, 0x4e, 0x68, 0x31, 0x39, + 0x59, 0x52, 0x65, 0x42, 0x44, 0x75, 0x7a, 0x67, 0x33, 0x43, 0x54, 0x62, + 0x59, 0x0a, 0x4b, 0x38, 0x52, 0x45, 0x6e, 0x58, 0x76, 0x4d, 0x59, 0x34, + 0x4b, 0x44, 0x36, 0x63, 0x69, 0x4d, 0x4f, 0x72, 0x61, 0x78, 0x4a, 0x53, + 0x31, 0x43, 0x49, 0x6d, 0x67, 0x53, 0x70, 0x68, 0x56, 0x70, 0x50, 0x37, + 0x54, 0x77, 0x4f, 0x4f, 0x6c, 0x62, 0x75, 0x78, 0x6a, 0x39, 0x76, 0x31, + 0x45, 0x63, 0x54, 0x41, 0x78, 0x34, 0x49, 0x68, 0x31, 0x38, 0x71, 0x72, + 0x71, 0x6c, 0x36, 0x32, 0x32, 0x56, 0x0a, 0x34, 0x45, 0x70, 0x34, 0x6c, + 0x62, 0x2f, 0x4a, 0x78, 0x57, 0x6e, 0x58, 0x4e, 0x48, 0x77, 0x2f, 0x7a, + 0x32, 0x64, 0x70, 0x64, 0x4d, 0x55, 0x6b, 0x4a, 0x4a, 0x66, 0x33, 0x6c, + 0x6d, 0x6e, 0x2b, 0x2f, 0x34, 0x5a, 0x56, 0x2b, 0x67, 0x4e, 0x42, 0x74, + 0x42, 0x79, 0x71, 0x4c, 0x52, 0x49, 0x50, 0x54, 0x47, 0x32, 0x6c, 0x37, + 0x4f, 0x34, 0x55, 0x6e, 0x52, 0x2f, 0x32, 0x69, 0x75, 0x71, 0x78, 0x0a, + 0x30, 0x77, 0x4b, 0x70, 0x59, 0x37, 0x54, 0x68, 0x34, 0x49, 0x66, 0x58, + 0x6a, 0x53, 0x56, 0x57, 0x36, 0x73, 0x4f, 0x63, 0x2b, 0x4a, 0x77, 0x37, + 0x64, 0x35, 0x68, 0x73, 0x56, 0x2f, 0x7a, 0x37, 0x41, 0x55, 0x52, 0x45, + 0x45, 0x4a, 0x34, 0x57, 0x58, 0x36, 0x4e, 0x4e, 0x70, 0x42, 0x77, 0x51, + 0x5a, 0x4e, 0x32, 0x56, 0x78, 0x48, 0x30, 0x37, 0x49, 0x78, 0x79, 0x76, + 0x6b, 0x33, 0x5a, 0x51, 0x0a, 0x58, 0x67, 0x56, 0x51, 0x58, 0x4e, 0x68, + 0x72, 0x79, 0x4e, 0x43, 0x49, 0x57, 0x70, 0x74, 0x2f, 0x66, 0x57, 0x69, + 0x76, 0x75, 0x50, 0x43, 0x7a, 0x77, 0x74, 0x32, 0x55, 0x74, 0x4f, 0x72, + 0x70, 0x2f, 0x35, 0x57, 0x43, 0x35, 0x37, 0x77, 0x31, 0x65, 0x65, 0x4d, + 0x73, 0x58, 0x63, 0x68, 0x72, 0x41, 0x51, 0x4b, 0x43, 0x41, 0x51, 0x45, + 0x41, 0x35, 0x4b, 0x48, 0x7a, 0x4c, 0x34, 0x76, 0x57, 0x0a, 0x42, 0x58, + 0x65, 0x66, 0x6f, 0x36, 0x2f, 0x4b, 0x68, 0x77, 0x49, 0x4a, 0x41, 0x37, + 0x79, 0x35, 0x54, 0x53, 0x67, 0x41, 0x62, 0x44, 0x68, 0x59, 0x70, 0x51, + 0x69, 0x34, 0x32, 0x4c, 0x6c, 0x74, 0x33, 0x37, 0x4e, 0x4d, 0x6e, 0x4a, + 0x63, 0x79, 0x4b, 0x54, 0x56, 0x6d, 0x79, 0x78, 0x70, 0x66, 0x59, 0x47, + 0x38, 0x65, 0x6e, 0x70, 0x31, 0x46, 0x71, 0x7a, 0x54, 0x56, 0x59, 0x74, + 0x63, 0x65, 0x0a, 0x5a, 0x49, 0x77, 0x33, 0x65, 0x39, 0x54, 0x35, 0x48, + 0x47, 0x4e, 0x71, 0x55, 0x6a, 0x70, 0x6f, 0x78, 0x43, 0x70, 0x35, 0x33, + 0x4b, 0x63, 0x44, 0x75, 0x38, 0x69, 0x47, 0x46, 0x36, 0x43, 0x51, 0x71, + 0x4a, 0x4d, 0x46, 0x72, 0x6b, 0x38, 0x47, 0x6b, 0x56, 0x51, 0x55, 0x38, + 0x31, 0x71, 0x58, 0x75, 0x57, 0x6f, 0x41, 0x50, 0x32, 0x79, 0x4a, 0x66, + 0x51, 0x53, 0x6d, 0x42, 0x75, 0x68, 0x67, 0x0a, 0x50, 0x64, 0x48, 0x4b, + 0x55, 0x4b, 0x31, 0x68, 0x47, 0x31, 0x6f, 0x36, 0x32, 0x44, 0x48, 0x32, + 0x67, 0x57, 0x33, 0x65, 0x6e, 0x37, 0x6a, 0x33, 0x70, 0x78, 0x43, 0x70, + 0x34, 0x4e, 0x68, 0x70, 0x39, 0x49, 0x70, 0x2f, 0x35, 0x39, 0x67, 0x30, + 0x4c, 0x73, 0x50, 0x44, 0x42, 0x67, 0x36, 0x6e, 0x59, 0x69, 0x38, 0x78, + 0x35, 0x61, 0x44, 0x4e, 0x6f, 0x59, 0x6d, 0x76, 0x45, 0x56, 0x45, 0x41, + 0x0a, 0x35, 0x46, 0x52, 0x7a, 0x37, 0x75, 0x2f, 0x77, 0x77, 0x67, 0x79, + 0x6d, 0x47, 0x49, 0x50, 0x67, 0x57, 0x70, 0x59, 0x47, 0x33, 0x63, 0x35, + 0x6b, 0x73, 0x31, 0x6e, 0x51, 0x41, 0x5a, 0x47, 0x53, 0x61, 0x37, 0x64, + 0x4c, 0x46, 0x74, 0x44, 0x4d, 0x45, 0x59, 0x4f, 0x78, 0x34, 0x37, 0x36, + 0x77, 0x42, 0x75, 0x48, 0x41, 0x56, 0x53, 0x57, 0x53, 0x41, 0x70, 0x64, + 0x55, 0x4d, 0x38, 0x56, 0x77, 0x0a, 0x6b, 0x2f, 0x6b, 0x63, 0x66, 0x2f, + 0x76, 0x61, 0x6f, 0x33, 0x4f, 0x7a, 0x71, 0x65, 0x33, 0x6f, 0x73, 0x37, + 0x37, 0x43, 0x6f, 0x46, 0x52, 0x77, 0x5a, 0x4d, 0x4e, 0x61, 0x7a, 0x2f, + 0x46, 0x2f, 0x7a, 0x35, 0x53, 0x77, 0x63, 0x47, 0x77, 0x42, 0x6f, 0x65, + 0x34, 0x51, 0x67, 0x69, 0x42, 0x6f, 0x33, 0x39, 0x4b, 0x58, 0x52, 0x33, + 0x5a, 0x63, 0x57, 0x33, 0x48, 0x63, 0x68, 0x76, 0x7a, 0x7a, 0x0a, 0x46, + 0x38, 0x54, 0x36, 0x6d, 0x38, 0x30, 0x78, 0x61, 0x7a, 0x67, 0x54, 0x6a, + 0x51, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, 0x77, 0x2f, 0x66, 0x55, 0x62, + 0x53, 0x78, 0x69, 0x6b, 0x33, 0x78, 0x38, 0x5a, 0x70, 0x59, 0x50, 0x71, + 0x53, 0x4a, 0x5a, 0x6f, 0x62, 0x30, 0x58, 0x76, 0x4b, 0x6b, 0x74, 0x67, + 0x37, 0x52, 0x35, 0x44, 0x4e, 0x32, 0x57, 0x2f, 0x65, 0x4c, 0x6e, 0x46, + 0x34, 0x63, 0x55, 0x0a, 0x45, 0x33, 0x70, 0x2b, 0x38, 0x5a, 0x33, 0x38, + 0x4c, 0x57, 0x76, 0x37, 0x6c, 0x36, 0x72, 0x72, 0x4e, 0x47, 0x4d, 0x41, + 0x4e, 0x48, 0x6a, 0x59, 0x44, 0x47, 0x53, 0x5a, 0x2f, 0x67, 0x79, 0x2b, + 0x48, 0x31, 0x6f, 0x75, 0x38, 0x69, 0x72, 0x54, 0x2b, 0x55, 0x64, 0x42, + 0x6c, 0x6c, 0x58, 0x4b, 0x70, 0x71, 0x44, 0x35, 0x39, 0x5a, 0x56, 0x55, + 0x73, 0x2b, 0x6f, 0x76, 0x4e, 0x73, 0x7a, 0x52, 0x0a, 0x68, 0x50, 0x37, + 0x77, 0x6c, 0x4c, 0x4d, 0x30, 0x42, 0x67, 0x45, 0x44, 0x58, 0x33, 0x6b, + 0x6e, 0x36, 0x78, 0x52, 0x41, 0x63, 0x5a, 0x2b, 0x74, 0x6b, 0x59, 0x53, + 0x64, 0x57, 0x79, 0x63, 0x6f, 0x2b, 0x5a, 0x4b, 0x4f, 0x48, 0x63, 0x66, + 0x71, 0x75, 0x54, 0x2f, 0x72, 0x4a, 0x34, 0x4b, 0x35, 0x42, 0x43, 0x65, + 0x75, 0x33, 0x31, 0x48, 0x6f, 0x78, 0x74, 0x51, 0x6c, 0x78, 0x4a, 0x44, + 0x51, 0x0a, 0x52, 0x36, 0x4b, 0x6a, 0x42, 0x4e, 0x2b, 0x61, 0x63, 0x37, + 0x63, 0x33, 0x6c, 0x32, 0x57, 0x52, 0x47, 0x7a, 0x2f, 0x70, 0x4b, 0x6a, + 0x30, 0x47, 0x39, 0x35, 0x32, 0x71, 0x42, 0x43, 0x72, 0x58, 0x4c, 0x71, + 0x32, 0x32, 0x30, 0x54, 0x4e, 0x71, 0x44, 0x4b, 0x44, 0x44, 0x4c, 0x50, + 0x54, 0x32, 0x5a, 0x46, 0x41, 0x6b, 0x65, 0x48, 0x77, 0x7a, 0x50, 0x58, + 0x32, 0x65, 0x65, 0x71, 0x53, 0x75, 0x0a, 0x62, 0x34, 0x6b, 0x48, 0x45, + 0x49, 0x44, 0x7a, 0x72, 0x66, 0x4d, 0x55, 0x44, 0x35, 0x63, 0x6d, 0x54, + 0x73, 0x42, 0x50, 0x71, 0x30, 0x4c, 0x59, 0x75, 0x6c, 0x67, 0x75, 0x42, + 0x59, 0x78, 0x5a, 0x57, 0x73, 0x50, 0x63, 0x6d, 0x2b, 0x66, 0x57, 0x63, + 0x53, 0x6e, 0x77, 0x39, 0x73, 0x32, 0x4c, 0x2b, 0x45, 0x4c, 0x32, 0x37, + 0x52, 0x39, 0x39, 0x49, 0x67, 0x7a, 0x43, 0x76, 0x64, 0x47, 0x49, 0x0a, + 0x71, 0x52, 0x45, 0x75, 0x2f, 0x69, 0x65, 0x5a, 0x66, 0x79, 0x6d, 0x31, + 0x6e, 0x67, 0x65, 0x58, 0x33, 0x56, 0x70, 0x73, 0x50, 0x4e, 0x54, 0x36, + 0x77, 0x43, 0x55, 0x58, 0x4c, 0x61, 0x63, 0x70, 0x79, 0x4a, 0x46, 0x6d, + 0x59, 0x57, 0x71, 0x65, 0x2f, 0x77, 0x4b, 0x43, 0x41, 0x51, 0x42, 0x44, + 0x75, 0x49, 0x53, 0x6a, 0x7a, 0x4c, 0x4f, 0x30, 0x49, 0x74, 0x36, 0x79, + 0x53, 0x56, 0x75, 0x66, 0x0a, 0x36, 0x63, 0x5a, 0x70, 0x79, 0x50, 0x6a, + 0x4b, 0x46, 0x64, 0x4d, 0x71, 0x4f, 0x76, 0x5a, 0x6d, 0x79, 0x39, 0x4b, + 0x55, 0x76, 0x7a, 0x67, 0x41, 0x54, 0x73, 0x65, 0x65, 0x69, 0x6c, 0x70, + 0x64, 0x51, 0x6d, 0x67, 0x55, 0x4f, 0x4e, 0x65, 0x50, 0x5a, 0x4e, 0x71, + 0x59, 0x2b, 0x4e, 0x53, 0x75, 0x42, 0x5a, 0x51, 0x2f, 0x46, 0x71, 0x44, + 0x31, 0x2f, 0x32, 0x4a, 0x66, 0x31, 0x35, 0x47, 0x43, 0x0a, 0x43, 0x79, + 0x42, 0x76, 0x41, 0x73, 0x59, 0x4e, 0x64, 0x4e, 0x64, 0x72, 0x75, 0x44, + 0x79, 0x75, 0x33, 0x70, 0x4a, 0x35, 0x5a, 0x53, 0x48, 0x30, 0x44, 0x4c, + 0x68, 0x65, 0x44, 0x53, 0x4a, 0x51, 0x34, 0x61, 0x72, 0x69, 0x2b, 0x69, + 0x35, 0x2b, 0x79, 0x52, 0x73, 0x52, 0x72, 0x6b, 0x42, 0x37, 0x4f, 0x32, + 0x6c, 0x43, 0x47, 0x6f, 0x71, 0x48, 0x52, 0x53, 0x43, 0x38, 0x44, 0x4e, + 0x37, 0x36, 0x0a, 0x6a, 0x78, 0x74, 0x6d, 0x39, 0x6b, 0x57, 0x68, 0x79, + 0x4c, 0x31, 0x73, 0x61, 0x67, 0x6c, 0x51, 0x2f, 0x75, 0x71, 0x53, 0x6f, + 0x77, 0x65, 0x66, 0x57, 0x33, 0x62, 0x50, 0x59, 0x59, 0x62, 0x7a, 0x59, + 0x79, 0x44, 0x64, 0x4a, 0x6e, 0x49, 0x46, 0x37, 0x78, 0x7a, 0x45, 0x65, + 0x34, 0x44, 0x74, 0x48, 0x43, 0x38, 0x79, 0x33, 0x64, 0x39, 0x35, 0x77, + 0x6c, 0x6f, 0x6b, 0x71, 0x41, 0x57, 0x55, 0x0a, 0x4e, 0x4e, 0x4c, 0x74, + 0x36, 0x41, 0x49, 0x49, 0x55, 0x75, 0x2f, 0x74, 0x75, 0x7a, 0x69, 0x77, + 0x74, 0x79, 0x57, 0x5a, 0x6b, 0x56, 0x6a, 0x68, 0x64, 0x77, 0x56, 0x53, + 0x32, 0x4c, 0x33, 0x5a, 0x59, 0x6a, 0x7a, 0x4b, 0x7a, 0x4b, 0x79, 0x76, + 0x48, 0x53, 0x63, 0x76, 0x45, 0x56, 0x58, 0x53, 0x56, 0x71, 0x69, 0x6d, + 0x50, 0x52, 0x45, 0x2f, 0x67, 0x30, 0x59, 0x68, 0x30, 0x71, 0x50, 0x4d, + 0x0a, 0x54, 0x6e, 0x55, 0x6c, 0x48, 0x45, 0x63, 0x56, 0x46, 0x71, 0x66, + 0x6e, 0x66, 0x5a, 0x74, 0x63, 0x2f, 0x56, 0x42, 0x6b, 0x7a, 0x34, 0x2b, + 0x67, 0x47, 0x4e, 0x61, 0x36, 0x4e, 0x67, 0x4f, 0x31, 0x78, 0x79, 0x49, + 0x30, 0x71, 0x51, 0x7a, 0x6e, 0x58, 0x79, 0x56, 0x59, 0x45, 0x59, 0x4d, + 0x42, 0x77, 0x78, 0x51, 0x6a, 0x46, 0x38, 0x47, 0x5a, 0x68, 0x4d, 0x73, + 0x47, 0x59, 0x78, 0x30, 0x45, 0x0a, 0x4f, 0x54, 0x50, 0x78, 0x41, 0x6f, + 0x49, 0x42, 0x41, 0x51, 0x43, 0x36, 0x67, 0x31, 0x72, 0x4c, 0x32, 0x59, + 0x32, 0x73, 0x76, 0x37, 0x4f, 0x4b, 0x30, 0x39, 0x48, 0x74, 0x38, 0x51, + 0x4b, 0x4c, 0x2f, 0x48, 0x50, 0x6d, 0x48, 0x4d, 0x4a, 0x7a, 0x38, 0x73, + 0x57, 0x76, 0x61, 0x41, 0x74, 0x34, 0x63, 0x6f, 0x66, 0x74, 0x6a, 0x61, + 0x4f, 0x65, 0x38, 0x6c, 0x78, 0x31, 0x7a, 0x42, 0x36, 0x69, 0x0a, 0x67, + 0x48, 0x37, 0x42, 0x6d, 0x47, 0x77, 0x70, 0x76, 0x50, 0x77, 0x4a, 0x39, + 0x4f, 0x58, 0x7a, 0x7a, 0x5a, 0x2f, 0x2b, 0x2b, 0x74, 0x50, 0x4d, 0x39, + 0x55, 0x46, 0x76, 0x50, 0x54, 0x48, 0x6f, 0x74, 0x46, 0x67, 0x4a, 0x4a, + 0x48, 0x67, 0x75, 0x35, 0x56, 0x46, 0x68, 0x32, 0x70, 0x48, 0x32, 0x64, + 0x72, 0x49, 0x66, 0x48, 0x77, 0x74, 0x65, 0x34, 0x47, 0x48, 0x4c, 0x74, + 0x55, 0x2b, 0x54, 0x0a, 0x55, 0x30, 0x32, 0x4a, 0x68, 0x62, 0x39, 0x6e, + 0x62, 0x79, 0x76, 0x79, 0x75, 0x4c, 0x34, 0x79, 0x6e, 0x4a, 0x78, 0x44, + 0x6b, 0x46, 0x37, 0x67, 0x51, 0x67, 0x73, 0x2b, 0x37, 0x76, 0x4a, 0x4a, + 0x42, 0x44, 0x76, 0x6b, 0x71, 0x2f, 0x63, 0x35, 0x72, 0x43, 0x63, 0x30, + 0x35, 0x7a, 0x6c, 0x38, 0x57, 0x35, 0x66, 0x69, 0x6f, 0x4d, 0x32, 0x4c, + 0x45, 0x4a, 0x44, 0x46, 0x5a, 0x36, 0x67, 0x52, 0x0a, 0x38, 0x46, 0x52, + 0x5a, 0x70, 0x4e, 0x4a, 0x5a, 0x74, 0x42, 0x64, 0x51, 0x47, 0x79, 0x74, + 0x52, 0x61, 0x74, 0x37, 0x51, 0x2f, 0x70, 0x45, 0x31, 0x48, 0x53, 0x4b, + 0x39, 0x73, 0x34, 0x69, 0x41, 0x6f, 0x6c, 0x57, 0x41, 0x71, 0x56, 0x6b, + 0x45, 0x6d, 0x6e, 0x35, 0x4c, 0x71, 0x6a, 0x48, 0x2b, 0x6d, 0x56, 0x76, + 0x48, 0x63, 0x49, 0x75, 0x52, 0x48, 0x6b, 0x4c, 0x36, 0x41, 0x7a, 0x46, + 0x70, 0x0a, 0x34, 0x73, 0x75, 0x36, 0x46, 0x75, 0x72, 0x66, 0x30, 0x77, + 0x74, 0x41, 0x66, 0x65, 0x76, 0x57, 0x56, 0x32, 0x7a, 0x64, 0x33, 0x50, + 0x6d, 0x43, 0x65, 0x4a, 0x32, 0x30, 0x53, 0x52, 0x43, 0x42, 0x50, 0x69, + 0x44, 0x59, 0x56, 0x55, 0x4f, 0x64, 0x73, 0x75, 0x5a, 0x66, 0x51, 0x7a, + 0x51, 0x4f, 0x76, 0x30, 0x52, 0x74, 0x38, 0x30, 0x70, 0x31, 0x56, 0x79, + 0x52, 0x35, 0x77, 0x75, 0x6a, 0x46, 0x0a, 0x37, 0x63, 0x57, 0x73, 0x74, + 0x5a, 0x39, 0x6c, 0x6a, 0x74, 0x55, 0x72, 0x68, 0x4b, 0x31, 0x76, 0x53, + 0x52, 0x33, 0x70, 0x56, 0x6f, 0x74, 0x66, 0x47, 0x48, 0x76, 0x76, 0x78, + 0x64, 0x47, 0x7a, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x51, 0x43, 0x36, 0x59, + 0x70, 0x5a, 0x64, 0x71, 0x4d, 0x38, 0x7a, 0x68, 0x32, 0x45, 0x41, 0x30, + 0x75, 0x69, 0x4e, 0x55, 0x4f, 0x55, 0x2f, 0x5a, 0x63, 0x6d, 0x54, 0x0a, + 0x31, 0x51, 0x6c, 0x4b, 0x67, 0x2b, 0x69, 0x58, 0x4b, 0x66, 0x57, 0x5a, + 0x72, 0x2b, 0x61, 0x64, 0x50, 0x56, 0x50, 0x6b, 0x31, 0x64, 0x54, 0x39, + 0x79, 0x55, 0x78, 0x2f, 0x6e, 0x4e, 0x77, 0x6e, 0x66, 0x43, 0x62, 0x4b, + 0x63, 0x54, 0x45, 0x79, 0x56, 0x74, 0x37, 0x65, 0x41, 0x7a, 0x69, 0x69, + 0x4c, 0x73, 0x5a, 0x77, 0x36, 0x65, 0x58, 0x4f, 0x50, 0x4b, 0x39, 0x55, + 0x38, 0x7a, 0x76, 0x43, 0x0a, 0x4a, 0x50, 0x55, 0x71, 0x67, 0x67, 0x44, + 0x5a, 0x33, 0x2b, 0x6b, 0x62, 0x6b, 0x34, 0x6a, 0x50, 0x44, 0x52, 0x41, + 0x73, 0x30, 0x4a, 0x74, 0x49, 0x6b, 0x43, 0x30, 0x62, 0x75, 0x48, 0x7a, + 0x6b, 0x58, 0x46, 0x30, 0x72, 0x67, 0x41, 0x34, 0x62, 0x4a, 0x7a, 0x75, + 0x55, 0x47, 0x30, 0x44, 0x45, 0x73, 0x58, 0x4a, 0x58, 0x59, 0x71, 0x48, + 0x6f, 0x4f, 0x6d, 0x31, 0x4f, 0x56, 0x4a, 0x66, 0x73, 0x0a, 0x78, 0x6f, + 0x4c, 0x50, 0x74, 0x68, 0x71, 0x55, 0x44, 0x30, 0x44, 0x32, 0x73, 0x43, + 0x59, 0x37, 0x46, 0x62, 0x4a, 0x58, 0x67, 0x4e, 0x4d, 0x61, 0x33, 0x76, + 0x59, 0x62, 0x31, 0x39, 0x6b, 0x78, 0x65, 0x4a, 0x37, 0x76, 0x37, 0x70, + 0x69, 0x42, 0x72, 0x69, 0x68, 0x4c, 0x6b, 0x35, 0x4c, 0x57, 0x64, 0x41, + 0x78, 0x32, 0x6c, 0x53, 0x56, 0x30, 0x6c, 0x61, 0x46, 0x45, 0x39, 0x4f, + 0x38, 0x43, 0x0a, 0x68, 0x36, 0x48, 0x61, 0x5a, 0x65, 0x52, 0x39, 0x4a, + 0x49, 0x69, 0x42, 0x79, 0x63, 0x5a, 0x39, 0x71, 0x48, 0x49, 0x58, 0x50, + 0x50, 0x43, 0x79, 0x5a, 0x57, 0x51, 0x4f, 0x6c, 0x6c, 0x43, 0x49, 0x56, + 0x69, 0x31, 0x71, 0x53, 0x51, 0x6b, 0x70, 0x51, 0x4e, 0x34, 0x76, 0x59, + 0x4e, 0x68, 0x63, 0x2f, 0x49, 0x45, 0x37, 0x4d, 0x76, 0x51, 0x31, 0x4a, + 0x42, 0x61, 0x4f, 0x62, 0x6c, 0x4a, 0x48, 0x0a, 0x63, 0x73, 0x63, 0x49, + 0x4c, 0x43, 0x38, 0x61, 0x69, 0x4a, 0x56, 0x46, 0x4f, 0x53, 0x5a, 0x6e, + 0x4e, 0x6a, 0x4b, 0x2f, 0x66, 0x6c, 0x6f, 0x72, 0x78, 0x74, 0x54, 0x4f, + 0x44, 0x44, 0x39, 0x49, 0x47, 0x33, 0x67, 0x48, 0x66, 0x78, 0x54, 0x52, + 0x31, 0x73, 0x66, 0x61, 0x4c, 0x76, 0x35, 0x7a, 0x43, 0x51, 0x66, 0x39, + 0x77, 0x4f, 0x49, 0x35, 0x52, 0x49, 0x2f, 0x78, 0x0a, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x52, 0x53, 0x41, 0x20, 0x50, 0x52, + 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a +}; + +unsigned char client_key_der[] = { + 0x30, 0x82, 0x09, 0x29, 0x02, 0x01, 0x00, 0x02, 0x82, 0x02, 0x01, 0x00, + 0xAF, 0x04, 0xB2, 0x36, 0x78, 0x5F, 0xBC, 0x38, 0x9C, 0x18, 0x93, 0x81, + 0x7D, 0x44, 0x0B, 0x18, 0x4A, 0x4A, 0x0B, 0x6F, 0xCD, 0xDB, 0x94, 0xE8, + 0xD8, 0xA7, 0xFA, 0xEB, 0x39, 0x03, 0x6F, 0x79, 0xB8, 0x20, 0xFC, 0x0B, + 0x71, 0xCC, 0x46, 0x58, 0x75, 0xAE, 0x7B, 0x18, 0xA9, 0x9A, 0xF0, 0x30, + 0x17, 0xE5, 0xF6, 0xE8, 0x51, 0xB3, 0xE5, 0xF2, 0x5D, 0xAB, 0xC1, 0x61, + 0xC2, 0x5A, 0xBE, 0x9E, 0xA8, 0xF8, 0x3E, 0x19, 0x7F, 0x43, 0xEC, 0x14, + 0x06, 0x54, 0x3E, 0x5C, 0xC8, 0xEA, 0x22, 0x9C, 0x32, 0xCE, 0x96, 0x66, + 0xE0, 0x79, 0xBE, 0x38, 0xE0, 0x8A, 0x7E, 0xCC, 0x97, 0x76, 0x62, 0xCB, + 0xA4, 0x5C, 0x95, 0x93, 0x8A, 0xEB, 0xD8, 0x64, 0x38, 0x28, 0x54, 0x90, + 0x5D, 0x05, 0xF2, 0x67, 0x1C, 0x5B, 0x34, 0x57, 0x60, 0x9C, 0x2A, 0xEF, + 0x9A, 0xD9, 0x08, 0x05, 0x06, 0xEF, 0x9A, 0x70, 0x72, 0x3F, 0xEC, 0x27, + 0x7B, 0x38, 0x54, 0x40, 0xA2, 0xE4, 0x99, 0x03, 0xF6, 0x4A, 0x3E, 0xDE, + 0x47, 0x32, 0xE8, 0xC9, 0x7D, 0xAA, 0x00, 0x8B, 0x82, 0x15, 0x86, 0xD6, + 0x03, 0xA0, 0xD6, 0x4B, 0xD3, 0xCB, 0x56, 0xE8, 0x2A, 0x4A, 0xF2, 0xDE, + 0x42, 0xAC, 0xF5, 0xB6, 0x9D, 0x9F, 0x66, 0x98, 0x41, 0x76, 0x37, 0xC8, + 0x95, 0xE0, 0xC1, 0xBB, 0x36, 0x20, 0x07, 0x46, 0x9C, 0xF7, 0x20, 0xC7, + 0xD6, 0x67, 0x9E, 0x40, 0xE0, 0x8D, 0x3D, 0xAC, 0xD1, 0x01, 0xC5, 0x96, + 0xC2, 0xEF, 0xA8, 0x55, 0x69, 0x84, 0x04, 0x49, 0x0F, 0x0C, 0x8D, 0x69, + 0x86, 0xE9, 0xC3, 0xF7, 0x7D, 0xC6, 0xD5, 0x17, 0x16, 0x76, 0x64, 0x14, + 0xAF, 0xB9, 0x07, 0x78, 0xF3, 0x0F, 0xC3, 0x9A, 0x77, 0x07, 0x3F, 0xAA, + 0x8E, 0xA6, 0x87, 0xFB, 0xAA, 0x02, 0x4E, 0x16, 0x42, 0xA3, 0xE6, 0x14, + 0xF6, 0x14, 0x3F, 0xE3, 0xE1, 0xF0, 0x30, 0xDC, 0xBC, 0x42, 0xCC, 0xA3, + 0x6A, 0x67, 0xFB, 0xD7, 0x3E, 0x60, 0xE0, 0x28, 0x5E, 0xDB, 0x84, 0x0F, + 0x7D, 0xC9, 0xF4, 0xDD, 0x6C, 0xBF, 0x65, 0x16, 0xAE, 0x4A, 0x0B, 0x10, + 0x9F, 0x2C, 0xE1, 0xAD, 0x9C, 0xFD, 0x58, 0x9E, 0x7E, 0xF3, 0x05, 0x2A, + 0x8F, 0x8E, 0x10, 0xF0, 0x4E, 0xA9, 0x61, 0x12, 0xF4, 0x87, 0x8C, 0x5E, + 0x22, 0xC1, 0x0E, 0xBF, 0xE2, 0x30, 0x35, 0xB5, 0x58, 0x9B, 0xC5, 0xB5, + 0x6C, 0x76, 0x5B, 0x75, 0x6D, 0x52, 0x78, 0xCC, 0x29, 0xB4, 0x46, 0xFB, + 0x18, 0xF8, 0x14, 0x68, 0xEF, 0xF2, 0x27, 0x3F, 0xE1, 0x2A, 0xA7, 0x2B, + 0xA4, 0x82, 0x7B, 0xFC, 0x8A, 0x1A, 0x01, 0x32, 0xBE, 0x8F, 0xDA, 0x7C, + 0xD9, 0xBD, 0xD0, 0x8B, 0x6F, 0xD4, 0x4E, 0x9A, 0x88, 0x0C, 0x88, 0x32, + 0xC9, 0x90, 0x0B, 0x52, 0x4E, 0xA1, 0x58, 0x7F, 0x7B, 0x4A, 0x83, 0x1A, + 0xAA, 0x48, 0x54, 0x8E, 0xC0, 0xFC, 0xEB, 0x33, 0xF6, 0xD4, 0xA1, 0xC4, + 0x37, 0x91, 0xB9, 0x91, 0x7D, 0xFA, 0x03, 0x0C, 0x9F, 0x48, 0x40, 0x26, + 0x46, 0xF9, 0x47, 0x73, 0x31, 0xA3, 0xC5, 0xFA, 0x90, 0x3C, 0x1A, 0xB3, + 0x31, 0xC7, 0xBD, 0x15, 0x2C, 0x7D, 0x37, 0xE4, 0x7D, 0x6F, 0x0F, 0xD5, + 0x06, 0xC7, 0xC2, 0x73, 0x31, 0x79, 0x2C, 0xA9, 0xBB, 0x46, 0x88, 0xC7, + 0x0F, 0x9C, 0xF0, 0x39, 0xB3, 0xB7, 0xE9, 0x6C, 0xED, 0xB8, 0xF6, 0x9B, + 0x6E, 0x87, 0xF3, 0xD2, 0x61, 0x62, 0x57, 0x2B, 0x85, 0xDD, 0xC3, 0x9B, + 0xFE, 0xDA, 0x86, 0x55, 0x5F, 0x77, 0x9C, 0x8A, 0xC0, 0x05, 0xBC, 0xBF, + 0x0D, 0xB0, 0x5B, 0x21, 0x02, 0x57, 0x2E, 0xCB, 0x2D, 0x9E, 0x9C, 0x29, + 0x95, 0x96, 0xDA, 0x66, 0x46, 0x2A, 0x3E, 0xF3, 0xBB, 0x19, 0x15, 0x7B, + 0xD0, 0x7F, 0x30, 0x34, 0xEE, 0x4E, 0x7F, 0x73, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x02, 0x00, 0x65, 0xC7, 0x75, 0x5B, 0xF7, 0xE4, 0xB4, + 0xB0, 0xB7, 0x16, 0x13, 0xB1, 0xDA, 0x07, 0x17, 0xEC, 0xA8, 0x47, 0x27, + 0x1F, 0x7D, 0xB7, 0x94, 0x81, 0x54, 0x7D, 0x5B, 0x8E, 0x2F, 0x39, 0xB0, + 0x3A, 0x38, 0xB9, 0xF5, 0xD9, 0x31, 0xF4, 0x79, 0x74, 0x37, 0xFB, 0x9F, + 0xDA, 0x57, 0xC7, 0xA2, 0xA6, 0x55, 0x3F, 0x86, 0xB6, 0xD3, 0xCC, 0xAA, + 0x8C, 0xA7, 0xAC, 0x9A, 0x69, 0x1F, 0x7D, 0x66, 0x1D, 0x1E, 0x66, 0x3E, + 0xB6, 0xD5, 0x2B, 0xCA, 0xBE, 0x5A, 0x6F, 0xE3, 0x54, 0x52, 0x02, 0xD6, + 0xDD, 0xBF, 0x2E, 0x24, 0x88, 0xEF, 0x41, 0xDD, 0x3D, 0x76, 0x57, 0x14, + 0x26, 0x26, 0x6E, 0xD2, 0x45, 0x5A, 0xEF, 0xFC, 0x8C, 0x30, 0xDD, 0xB6, + 0x99, 0x91, 0x8E, 0xE2, 0x9E, 0x91, 0x7A, 0x43, 0xDD, 0x72, 0xC0, 0x17, + 0x50, 0x38, 0xFE, 0x98, 0x79, 0x7E, 0xB3, 0x77, 0xD6, 0x74, 0x92, 0x22, + 0x5E, 0x02, 0x12, 0xEA, 0x62, 0x8B, 0x65, 0xD6, 0x99, 0x40, 0x21, 0x70, + 0x5A, 0xBD, 0x7F, 0xAF, 0xCF, 0xD3, 0xD7, 0xE7, 0x48, 0x45, 0xAB, 0xF7, + 0x65, 0x29, 0xE1, 0x1A, 0xE8, 0x9A, 0x73, 0x8E, 0x2B, 0xCA, 0x9F, 0x15, + 0x8B, 0x2E, 0x0D, 0x27, 0xEF, 0xD9, 0x16, 0x7C, 0x4E, 0x16, 0x9B, 0xFC, + 0x2D, 0xCE, 0xE3, 0xD0, 0xCC, 0xF5, 0x5C, 0x1F, 0x4D, 0x9F, 0xCC, 0x40, + 0x6E, 0xF0, 0x19, 0x47, 0x16, 0xEF, 0xB8, 0x92, 0x6F, 0x68, 0x26, 0xBB, + 0x17, 0x24, 0xF4, 0xC2, 0xD3, 0x49, 0xC9, 0xD5, 0x3A, 0xD6, 0x81, 0xCF, + 0x7C, 0x2D, 0x24, 0x3F, 0x98, 0x7E, 0xBC, 0xB3, 0x19, 0xDA, 0x2B, 0x27, + 0x00, 0xA6, 0x2F, 0x73, 0x44, 0x14, 0x6C, 0xA5, 0xCB, 0xD0, 0x8D, 0xFA, + 0x52, 0x79, 0xA5, 0xDD, 0x80, 0x72, 0x34, 0xA6, 0x1F, 0xD4, 0x5E, 0x42, + 0xCA, 0xEF, 0x36, 0x21, 0x37, 0x4F, 0x63, 0xB7, 0x42, 0xF7, 0xD5, 0x28, + 0x9F, 0xF1, 0xB9, 0x7D, 0x15, 0x15, 0x9D, 0xA9, 0x97, 0xE0, 0x0F, 0x8D, + 0x6D, 0xF1, 0x84, 0x86, 0x52, 0x76, 0x5D, 0x75, 0x10, 0x5D, 0x38, 0xA1, + 0x03, 0x01, 0x6E, 0x64, 0x7A, 0xD3, 0x1C, 0x02, 0x57, 0xF5, 0xDA, 0x30, + 0xE0, 0xB4, 0x10, 0xB8, 0x88, 0x43, 0x3C, 0xE6, 0x14, 0x37, 0x36, 0x68, + 0xE1, 0x6D, 0x38, 0x0C, 0x32, 0x7E, 0x6E, 0xE7, 0xF8, 0x83, 0x3A, 0x4D, + 0x87, 0x5F, 0x58, 0x45, 0xE0, 0x43, 0xBB, 0x38, 0x37, 0x09, 0x36, 0xD8, + 0x2B, 0xC4, 0x44, 0x9D, 0x7B, 0xCC, 0x63, 0x82, 0x83, 0xE9, 0xC8, 0x8C, + 0x3A, 0xB6, 0xB1, 0x25, 0x2D, 0x42, 0x22, 0x68, 0x12, 0xA6, 0x15, 0x69, + 0x3F, 0xB4, 0xF0, 0x38, 0xE9, 0x5B, 0xBB, 0x18, 0xFD, 0xBF, 0x51, 0x1C, + 0x4C, 0x0C, 0x78, 0x22, 0x1D, 0x7C, 0xAA, 0xBA, 0xA5, 0xEB, 0x6D, 0x95, + 0xE0, 0x4A, 0x78, 0x95, 0xBF, 0xC9, 0xC5, 0x69, 0xD7, 0x34, 0x7C, 0x3F, + 0xCF, 0x67, 0x69, 0x74, 0xC5, 0x24, 0x24, 0x97, 0xF7, 0x96, 0x69, 0xFE, + 0xFF, 0x86, 0x55, 0xFA, 0x03, 0x41, 0xB4, 0x1C, 0xAA, 0x2D, 0x12, 0x0F, + 0x4C, 0x6D, 0xA5, 0xEC, 0xEE, 0x14, 0x9D, 0x1F, 0xF6, 0x8A, 0xEA, 0xB1, + 0xD3, 0x02, 0xA9, 0x63, 0xB4, 0xE1, 0xE0, 0x87, 0xD7, 0x8D, 0x25, 0x56, + 0xEA, 0xC3, 0x9C, 0xF8, 0x9C, 0x3B, 0x77, 0x98, 0x6C, 0x57, 0xFC, 0xFB, + 0x01, 0x44, 0x44, 0x10, 0x9E, 0x16, 0x5F, 0xA3, 0x4D, 0xA4, 0x1C, 0x10, + 0x64, 0xDD, 0x95, 0xC4, 0x7D, 0x3B, 0x23, 0x1C, 0xAF, 0x93, 0x76, 0x50, + 0x5E, 0x05, 0x50, 0x5C, 0xD8, 0x6B, 0xC8, 0xD0, 0x88, 0x5A, 0x9B, 0x7F, + 0x7D, 0x68, 0xAF, 0xB8, 0xF0, 0xB3, 0xC2, 0xDD, 0x94, 0xB4, 0xEA, 0xE9, + 0xFF, 0x95, 0x82, 0xE7, 0xBC, 0x35, 0x79, 0xE3, 0x2C, 0x5D, 0xC8, 0x6B, + 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE4, 0xA1, 0xF3, 0x2F, 0x8B, 0xD6, + 0x05, 0x77, 0x9F, 0xA3, 0xAF, 0xCA, 0x87, 0x02, 0x09, 0x03, 0xBC, 0xB9, + 0x4D, 0x28, 0x00, 0x6C, 0x38, 0x58, 0xA5, 0x08, 0xB8, 0xD8, 0xB9, 0x6D, + 0xDF, 0xB3, 0x4C, 0x9C, 0x97, 0x32, 0x29, 0x35, 0x66, 0xCB, 0x1A, 0x5F, + 0x60, 0x6F, 0x1E, 0x9E, 0x9D, 0x45, 0xAB, 0x34, 0xD5, 0x62, 0xD7, 0x1E, + 0x64, 0x8C, 0x37, 0x7B, 0xD4, 0xF9, 0x1C, 0x63, 0x6A, 0x52, 0x3A, 0x68, + 0xC4, 0x2A, 0x79, 0xDC, 0xA7, 0x03, 0xBB, 0xC8, 0x86, 0x17, 0xA0, 0x90, + 0xA8, 0x93, 0x05, 0xAE, 0x4F, 0x06, 0x91, 0x54, 0x14, 0xF3, 0x5A, 0x97, + 0xB9, 0x6A, 0x00, 0x3F, 0x6C, 0x89, 0x7D, 0x04, 0xA6, 0x06, 0xE8, 0x60, + 0x3D, 0xD1, 0xCA, 0x50, 0xAD, 0x61, 0x1B, 0x5A, 0x3A, 0xD8, 0x31, 0xF6, + 0x81, 0x6D, 0xDE, 0x9F, 0xB8, 0xF7, 0xA7, 0x10, 0xA9, 0xE0, 0xD8, 0x69, + 0xF4, 0x8A, 0x7F, 0xE7, 0xD8, 0x34, 0x2E, 0xC3, 0xC3, 0x06, 0x0E, 0xA7, + 0x62, 0x2F, 0x31, 0xE5, 0xA0, 0xCD, 0xA1, 0x89, 0xAF, 0x11, 0x51, 0x00, + 0xE4, 0x54, 0x73, 0xEE, 0xEF, 0xF0, 0xC2, 0x0C, 0xA6, 0x18, 0x83, 0xE0, + 0x5A, 0x96, 0x06, 0xDD, 0xCE, 0x64, 0xB3, 0x59, 0xD0, 0x01, 0x91, 0x92, + 0x6B, 0xB7, 0x4B, 0x16, 0xD0, 0xCC, 0x11, 0x83, 0xB1, 0xE3, 0xBE, 0xB0, + 0x06, 0xE1, 0xC0, 0x55, 0x25, 0x92, 0x02, 0x97, 0x54, 0x33, 0xC5, 0x70, + 0x93, 0xF9, 0x1C, 0x7F, 0xFB, 0xDA, 0xA3, 0x73, 0xB3, 0xA9, 0xED, 0xE8, + 0xB3, 0xBE, 0xC2, 0xA0, 0x54, 0x70, 0x64, 0xC3, 0x5A, 0xCF, 0xF1, 0x7F, + 0xCF, 0x94, 0xB0, 0x70, 0x6C, 0x01, 0xA1, 0xEE, 0x10, 0x82, 0x20, 0x68, + 0xDF, 0xD2, 0x97, 0x47, 0x76, 0x5C, 0x5B, 0x71, 0xDC, 0x86, 0xFC, 0xF3, + 0x17, 0xC4, 0xFA, 0x9B, 0xCD, 0x31, 0x6B, 0x38, 0x13, 0x8D, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xC3, 0xF7, 0xD4, 0x6D, 0x2C, 0x62, 0x93, 0x7C, 0x7C, + 0x66, 0x96, 0x0F, 0xA9, 0x22, 0x59, 0xA1, 0xBD, 0x17, 0xBC, 0xA9, 0x2D, + 0x83, 0xB4, 0x79, 0x0C, 0xDD, 0x96, 0xFD, 0xE2, 0xE7, 0x17, 0x87, 0x14, + 0x13, 0x7A, 0x7E, 0xF1, 0x9D, 0xFC, 0x2D, 0x6B, 0xFB, 0x97, 0xAA, 0xEB, + 0x34, 0x63, 0x00, 0x34, 0x78, 0xD8, 0x0C, 0x64, 0x99, 0xFE, 0x0C, 0xBE, + 0x1F, 0x5A, 0x2E, 0xF2, 0x2A, 0xD3, 0xF9, 0x47, 0x41, 0x96, 0x55, 0xCA, + 0xA6, 0xA0, 0xF9, 0xF5, 0x95, 0x54, 0xB3, 0xEA, 0x2F, 0x36, 0xCC, 0xD1, + 0x84, 0xFE, 0xF0, 0x94, 0xB3, 0x34, 0x06, 0x01, 0x03, 0x5F, 0x79, 0x27, + 0xEB, 0x14, 0x40, 0x71, 0x9F, 0xAD, 0x91, 0x84, 0x9D, 0x5B, 0x27, 0x28, + 0xF9, 0x92, 0x8E, 0x1D, 0xC7, 0xEA, 0xB9, 0x3F, 0xEB, 0x27, 0x82, 0xB9, + 0x04, 0x27, 0xAE, 0xDF, 0x51, 0xE8, 0xC6, 0xD4, 0x25, 0xC4, 0x90, 0xD0, + 0x47, 0xA2, 0xA3, 0x04, 0xDF, 0x9A, 0x73, 0xB7, 0x37, 0x97, 0x65, 0x91, + 0x1B, 0x3F, 0xE9, 0x2A, 0x3D, 0x06, 0xF7, 0x9D, 0xAA, 0x04, 0x2A, 0xD7, + 0x2E, 0xAD, 0xB6, 0xD1, 0x33, 0x6A, 0x0C, 0xA0, 0xC3, 0x2C, 0xF4, 0xF6, + 0x64, 0x50, 0x24, 0x78, 0x7C, 0x33, 0x3D, 0x7D, 0x9E, 0x7A, 0xA4, 0xAE, + 0x6F, 0x89, 0x07, 0x10, 0x80, 0xF3, 0xAD, 0xF3, 0x14, 0x0F, 0x97, 0x26, + 0x4E, 0xC0, 0x4F, 0xAB, 0x42, 0xD8, 0xBA, 0x58, 0x2E, 0x05, 0x8C, 0x59, + 0x5A, 0xC3, 0xDC, 0x9B, 0xE7, 0xD6, 0x71, 0x29, 0xF0, 0xF6, 0xCD, 0x8B, + 0xF8, 0x42, 0xF6, 0xED, 0x1F, 0x7D, 0x22, 0x0C, 0xC2, 0xBD, 0xD1, 0x88, + 0xA9, 0x11, 0x2E, 0xFE, 0x27, 0x99, 0x7F, 0x29, 0xB5, 0x9E, 0x07, 0x97, + 0xDD, 0x5A, 0x6C, 0x3C, 0xD4, 0xFA, 0xC0, 0x25, 0x17, 0x2D, 0xA7, 0x29, + 0xC8, 0x91, 0x66, 0x61, 0x6A, 0x9E, 0xFF, 0x02, 0x82, 0x01, 0x00, 0x43, + 0xB8, 0x84, 0xA3, 0xCC, 0xB3, 0xB4, 0x22, 0xDE, 0xB2, 0x49, 0x5B, 0x9F, + 0xE9, 0xC6, 0x69, 0xC8, 0xF8, 0xCA, 0x15, 0xD3, 0x2A, 0x3A, 0xF6, 0x66, + 0xCB, 0xD2, 0x94, 0xBF, 0x38, 0x00, 0x4E, 0xC7, 0x9E, 0x8A, 0x5A, 0x5D, + 0x42, 0x68, 0x14, 0x38, 0xD7, 0x8F, 0x64, 0xDA, 0x98, 0xF8, 0xD4, 0xAE, + 0x05, 0x94, 0x3F, 0x16, 0xA0, 0xF5, 0xFF, 0x62, 0x5F, 0xD7, 0x91, 0x82, + 0x0B, 0x20, 0x6F, 0x02, 0xC6, 0x0D, 0x74, 0xD7, 0x6B, 0xB8, 0x3C, 0xAE, + 0xDE, 0x92, 0x79, 0x65, 0x21, 0xF4, 0x0C, 0xB8, 0x5E, 0x0D, 0x22, 0x50, + 0xE1, 0xAA, 0xE2, 0xFA, 0x2E, 0x7E, 0xC9, 0x1B, 0x11, 0xAE, 0x40, 0x7B, + 0x3B, 0x69, 0x42, 0x1A, 0x8A, 0x87, 0x45, 0x20, 0xBC, 0x0C, 0xDE, 0xFA, + 0x8F, 0x1B, 0x66, 0xF6, 0x45, 0xA1, 0xC8, 0xBD, 0x6C, 0x6A, 0x09, 0x50, + 0xFE, 0xEA, 0x92, 0xA3, 0x07, 0x9F, 0x5B, 0x76, 0xCF, 0x61, 0x86, 0xF3, + 0x63, 0x20, 0xDD, 0x26, 0x72, 0x05, 0xEF, 0x1C, 0xC4, 0x7B, 0x80, 0xED, + 0x1C, 0x2F, 0x32, 0xDD, 0xDF, 0x79, 0xC2, 0x5A, 0x24, 0xA8, 0x05, 0x94, + 0x34, 0xD2, 0xED, 0xE8, 0x02, 0x08, 0x52, 0xEF, 0xED, 0xBB, 0x38, 0xB0, + 0xB7, 0x25, 0x99, 0x91, 0x58, 0xE1, 0x77, 0x05, 0x52, 0xD8, 0xBD, 0xD9, + 0x62, 0x3C, 0xCA, 0xCC, 0xAC, 0xAF, 0x1D, 0x27, 0x2F, 0x11, 0x55, 0xD2, + 0x56, 0xA8, 0xA6, 0x3D, 0x11, 0x3F, 0x83, 0x46, 0x21, 0xD2, 0xA3, 0xCC, + 0x4E, 0x75, 0x25, 0x1C, 0x47, 0x15, 0x16, 0xA7, 0xE7, 0x7D, 0x9B, 0x5C, + 0xFD, 0x50, 0x64, 0xCF, 0x8F, 0xA0, 0x18, 0xD6, 0xBA, 0x36, 0x03, 0xB5, + 0xC7, 0x22, 0x34, 0xA9, 0x0C, 0xE7, 0x5F, 0x25, 0x58, 0x11, 0x83, 0x01, + 0xC3, 0x14, 0x23, 0x17, 0xC1, 0x99, 0x84, 0xCB, 0x06, 0x63, 0x1D, 0x04, + 0x39, 0x33, 0xF1, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x83, 0x5A, 0xCB, + 0xD9, 0x8D, 0xAC, 0xBF, 0xB3, 0x8A, 0xD3, 0xD1, 0xED, 0xF1, 0x02, 0x8B, + 0xFC, 0x73, 0xE6, 0x1C, 0xC2, 0x73, 0xF2, 0xC5, 0xAF, 0x68, 0x0B, 0x78, + 0x72, 0x87, 0xED, 0x8D, 0xA3, 0x9E, 0xF2, 0x5C, 0x75, 0xCC, 0x1E, 0xA2, + 0x80, 0x7E, 0xC1, 0x98, 0x6C, 0x29, 0xBC, 0xFC, 0x09, 0xF4, 0xE5, 0xF3, + 0xCD, 0x9F, 0xFE, 0xFA, 0xD3, 0xCC, 0xF5, 0x41, 0x6F, 0x3D, 0x31, 0xE8, + 0xB4, 0x58, 0x09, 0x24, 0x78, 0x2E, 0xE5, 0x51, 0x61, 0xDA, 0x91, 0xF6, + 0x76, 0xB2, 0x1F, 0x1F, 0x0B, 0x5E, 0xE0, 0x61, 0xCB, 0xB5, 0x4F, 0x93, + 0x53, 0x4D, 0x89, 0x85, 0xBF, 0x67, 0x6F, 0x2B, 0xF2, 0xB8, 0xBE, 0x32, + 0x9C, 0x9C, 0x43, 0x90, 0x5E, 0xE0, 0x42, 0x0B, 0x3E, 0xEE, 0xF2, 0x49, + 0x04, 0x3B, 0xE4, 0xAB, 0xF7, 0x39, 0xAC, 0x27, 0x34, 0xE7, 0x39, 0x7C, + 0x5B, 0x97, 0xE2, 0xA0, 0xCD, 0x8B, 0x10, 0x90, 0xC5, 0x67, 0xA8, 0x11, + 0xF0, 0x54, 0x59, 0xA4, 0xD2, 0x59, 0xB4, 0x17, 0x50, 0x1B, 0x2B, 0x51, + 0x6A, 0xDE, 0xD0, 0xFE, 0x91, 0x35, 0x1D, 0x22, 0xBD, 0xB3, 0x88, 0x80, + 0xA2, 0x55, 0x80, 0xA9, 0x59, 0x04, 0x9A, 0x7E, 0x4B, 0xAA, 0x31, 0xFE, + 0x99, 0x5B, 0xC7, 0x70, 0x8B, 0x91, 0x1E, 0x42, 0xFA, 0x03, 0x31, 0x69, + 0xE2, 0xCB, 0xBA, 0x16, 0xEA, 0xDF, 0xD3, 0x0B, 0x40, 0x7D, 0xEB, 0xD6, + 0x57, 0x6C, 0xDD, 0xDC, 0xF9, 0x82, 0x78, 0x9D, 0xB4, 0x49, 0x10, 0x81, + 0x3E, 0x20, 0xD8, 0x55, 0x43, 0x9D, 0xB2, 0xE6, 0x5F, 0x43, 0x34, 0x0E, + 0xBF, 0x44, 0x6D, 0xF3, 0x4A, 0x75, 0x57, 0x24, 0x79, 0xC2, 0xE8, 0xC5, + 0xED, 0xC5, 0xAC, 0xB5, 0x9F, 0x65, 0x8E, 0xD5, 0x2B, 0x84, 0xAD, 0x6F, + 0x49, 0x1D, 0xE9, 0x56, 0x8B, 0x5F, 0x18, 0x7B, 0xEF, 0xC5, 0xD1, 0xB3, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x62, 0x96, 0x5D, 0xA8, 0xCF, 0x33, + 0x87, 0x61, 0x00, 0xD2, 0xE8, 0x8D, 0x50, 0xE5, 0x3F, 0x65, 0xC9, 0x93, + 0xD5, 0x09, 0x4A, 0x83, 0xE8, 0x97, 0x29, 0xF5, 0x99, 0xAF, 0xE6, 0x9D, + 0x3D, 0x53, 0xE4, 0xD5, 0xD4, 0xFD, 0xC9, 0x4C, 0x7F, 0x9C, 0xDC, 0x27, + 0x7C, 0x26, 0xCA, 0x71, 0x31, 0x32, 0x56, 0xDE, 0xDE, 0x03, 0x38, 0xA2, + 0x2E, 0xC6, 0x70, 0xE9, 0xE5, 0xCE, 0x3C, 0xAF, 0x54, 0xF3, 0x3B, 0xC2, + 0x24, 0xF5, 0x2A, 0x82, 0x00, 0xD9, 0xDF, 0xE9, 0x1B, 0x93, 0x88, 0xCF, + 0x0D, 0x10, 0x2C, 0xD0, 0x9B, 0x48, 0x90, 0x2D, 0x1B, 0xB8, 0x7C, 0xE4, + 0x5C, 0x5D, 0x2B, 0x80, 0x0E, 0x1B, 0x27, 0x3B, 0x94, 0x1B, 0x40, 0xC4, + 0xB1, 0x72, 0x57, 0x62, 0xA1, 0xE8, 0x3A, 0x6D, 0x4E, 0x54, 0x97, 0xEC, + 0xC6, 0x82, 0xCF, 0xB6, 0x1A, 0x94, 0x0F, 0x40, 0xF6, 0xB0, 0x26, 0x3B, + 0x15, 0xB2, 0x57, 0x80, 0xD3, 0x1A, 0xDE, 0xF6, 0x1B, 0xD7, 0xD9, 0x31, + 0x78, 0x9E, 0xEF, 0xEE, 0x98, 0x81, 0xAE, 0x28, 0x4B, 0x93, 0x92, 0xD6, + 0x74, 0x0C, 0x76, 0x95, 0x25, 0x74, 0x95, 0xA1, 0x44, 0xF4, 0xEF, 0x02, + 0x87, 0xA1, 0xDA, 0x65, 0xE4, 0x7D, 0x24, 0x88, 0x81, 0xC9, 0xC6, 0x7D, + 0xA8, 0x72, 0x17, 0x3C, 0xF0, 0xB2, 0x65, 0x64, 0x0E, 0x96, 0x50, 0x88, + 0x56, 0x2D, 0x6A, 0x49, 0x09, 0x29, 0x40, 0xDE, 0x2F, 0x60, 0xD8, 0x5C, + 0xFC, 0x81, 0x3B, 0x32, 0xF4, 0x35, 0x24, 0x16, 0x8E, 0x6E, 0x52, 0x47, + 0x72, 0xC7, 0x08, 0x2C, 0x2F, 0x1A, 0x88, 0x95, 0x45, 0x39, 0x26, 0x67, + 0x36, 0x32, 0xBF, 0x7E, 0x5A, 0x2B, 0xC6, 0xD4, 0xCE, 0x0C, 0x3F, 0x48, + 0x1B, 0x78, 0x07, 0x7F, 0x14, 0xD1, 0xD6, 0xC7, 0xDA, 0x2E, 0xFE, 0x73, + 0x09, 0x07, 0xFD, 0xC0, 0xE2, 0x39, 0x44, 0x8F, 0xF1 +}; + +unsigned char client_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x44, + 0x43, + 0x43, + 0x41, + 0x77, + 0x43, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x45, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x78, + 0x4f, + 0x46, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x45, + 0x34, + 0x57, + 0x6a, + 0x42, + 0x4d, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x42, + 0x6d, + 0x4e, + 0x73, + 0x61, + 0x57, + 0x56, + 0x75, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x0a, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4b, + 0x38, + 0x45, + 0x73, + 0x6a, + 0x5a, + 0x34, + 0x58, + 0x37, + 0x77, + 0x34, + 0x6e, + 0x42, + 0x69, + 0x54, + 0x67, + 0x58, + 0x31, + 0x45, + 0x43, + 0x78, + 0x68, + 0x4b, + 0x53, + 0x67, + 0x74, + 0x76, + 0x7a, + 0x64, + 0x75, + 0x55, + 0x36, + 0x4e, + 0x69, + 0x6e, + 0x2b, + 0x75, + 0x73, + 0x35, + 0x0a, + 0x41, + 0x32, + 0x39, + 0x35, + 0x75, + 0x43, + 0x44, + 0x38, + 0x43, + 0x33, + 0x48, + 0x4d, + 0x52, + 0x6c, + 0x68, + 0x31, + 0x72, + 0x6e, + 0x73, + 0x59, + 0x71, + 0x5a, + 0x72, + 0x77, + 0x4d, + 0x42, + 0x66, + 0x6c, + 0x39, + 0x75, + 0x68, + 0x52, + 0x73, + 0x2b, + 0x58, + 0x79, + 0x58, + 0x61, + 0x76, + 0x42, + 0x59, + 0x63, + 0x4a, + 0x61, + 0x76, + 0x70, + 0x36, + 0x6f, + 0x2b, + 0x44, + 0x34, + 0x5a, + 0x66, + 0x30, + 0x50, + 0x73, + 0x46, + 0x41, + 0x5a, + 0x55, + 0x50, + 0x6c, + 0x7a, + 0x49, + 0x0a, + 0x36, + 0x69, + 0x4b, + 0x63, + 0x4d, + 0x73, + 0x36, + 0x57, + 0x5a, + 0x75, + 0x42, + 0x35, + 0x76, + 0x6a, + 0x6a, + 0x67, + 0x69, + 0x6e, + 0x37, + 0x4d, + 0x6c, + 0x33, + 0x5a, + 0x69, + 0x79, + 0x36, + 0x52, + 0x63, + 0x6c, + 0x5a, + 0x4f, + 0x4b, + 0x36, + 0x39, + 0x68, + 0x6b, + 0x4f, + 0x43, + 0x68, + 0x55, + 0x6b, + 0x46, + 0x30, + 0x46, + 0x38, + 0x6d, + 0x63, + 0x63, + 0x57, + 0x7a, + 0x52, + 0x58, + 0x59, + 0x4a, + 0x77, + 0x71, + 0x37, + 0x35, + 0x72, + 0x5a, + 0x43, + 0x41, + 0x55, + 0x47, + 0x0a, + 0x37, + 0x35, + 0x70, + 0x77, + 0x63, + 0x6a, + 0x2f, + 0x73, + 0x4a, + 0x33, + 0x73, + 0x34, + 0x56, + 0x45, + 0x43, + 0x69, + 0x35, + 0x4a, + 0x6b, + 0x44, + 0x39, + 0x6b, + 0x6f, + 0x2b, + 0x33, + 0x6b, + 0x63, + 0x79, + 0x36, + 0x4d, + 0x6c, + 0x39, + 0x71, + 0x67, + 0x43, + 0x4c, + 0x67, + 0x68, + 0x57, + 0x47, + 0x31, + 0x67, + 0x4f, + 0x67, + 0x31, + 0x6b, + 0x76, + 0x54, + 0x79, + 0x31, + 0x62, + 0x6f, + 0x4b, + 0x6b, + 0x72, + 0x79, + 0x33, + 0x6b, + 0x4b, + 0x73, + 0x39, + 0x62, + 0x61, + 0x64, + 0x0a, + 0x6e, + 0x32, + 0x61, + 0x59, + 0x51, + 0x58, + 0x59, + 0x33, + 0x79, + 0x4a, + 0x58, + 0x67, + 0x77, + 0x62, + 0x73, + 0x32, + 0x49, + 0x41, + 0x64, + 0x47, + 0x6e, + 0x50, + 0x63, + 0x67, + 0x78, + 0x39, + 0x5a, + 0x6e, + 0x6e, + 0x6b, + 0x44, + 0x67, + 0x6a, + 0x54, + 0x32, + 0x73, + 0x30, + 0x51, + 0x48, + 0x46, + 0x6c, + 0x73, + 0x4c, + 0x76, + 0x71, + 0x46, + 0x56, + 0x70, + 0x68, + 0x41, + 0x52, + 0x4a, + 0x44, + 0x77, + 0x79, + 0x4e, + 0x61, + 0x59, + 0x62, + 0x70, + 0x77, + 0x2f, + 0x64, + 0x39, + 0x0a, + 0x78, + 0x74, + 0x55, + 0x58, + 0x46, + 0x6e, + 0x5a, + 0x6b, + 0x46, + 0x4b, + 0x2b, + 0x35, + 0x42, + 0x33, + 0x6a, + 0x7a, + 0x44, + 0x38, + 0x4f, + 0x61, + 0x64, + 0x77, + 0x63, + 0x2f, + 0x71, + 0x6f, + 0x36, + 0x6d, + 0x68, + 0x2f, + 0x75, + 0x71, + 0x41, + 0x6b, + 0x34, + 0x57, + 0x51, + 0x71, + 0x50, + 0x6d, + 0x46, + 0x50, + 0x59, + 0x55, + 0x50, + 0x2b, + 0x50, + 0x68, + 0x38, + 0x44, + 0x44, + 0x63, + 0x76, + 0x45, + 0x4c, + 0x4d, + 0x6f, + 0x32, + 0x70, + 0x6e, + 0x2b, + 0x39, + 0x63, + 0x2b, + 0x0a, + 0x59, + 0x4f, + 0x41, + 0x6f, + 0x58, + 0x74, + 0x75, + 0x45, + 0x44, + 0x33, + 0x33, + 0x4a, + 0x39, + 0x4e, + 0x31, + 0x73, + 0x76, + 0x32, + 0x55, + 0x57, + 0x72, + 0x6b, + 0x6f, + 0x4c, + 0x45, + 0x4a, + 0x38, + 0x73, + 0x34, + 0x61, + 0x32, + 0x63, + 0x2f, + 0x56, + 0x69, + 0x65, + 0x66, + 0x76, + 0x4d, + 0x46, + 0x4b, + 0x6f, + 0x2b, + 0x4f, + 0x45, + 0x50, + 0x42, + 0x4f, + 0x71, + 0x57, + 0x45, + 0x53, + 0x39, + 0x49, + 0x65, + 0x4d, + 0x58, + 0x69, + 0x4c, + 0x42, + 0x44, + 0x72, + 0x2f, + 0x69, + 0x0a, + 0x4d, + 0x44, + 0x57, + 0x31, + 0x57, + 0x4a, + 0x76, + 0x46, + 0x74, + 0x57, + 0x78, + 0x32, + 0x57, + 0x33, + 0x56, + 0x74, + 0x55, + 0x6e, + 0x6a, + 0x4d, + 0x4b, + 0x62, + 0x52, + 0x47, + 0x2b, + 0x78, + 0x6a, + 0x34, + 0x46, + 0x47, + 0x6a, + 0x76, + 0x38, + 0x69, + 0x63, + 0x2f, + 0x34, + 0x53, + 0x71, + 0x6e, + 0x4b, + 0x36, + 0x53, + 0x43, + 0x65, + 0x2f, + 0x79, + 0x4b, + 0x47, + 0x67, + 0x45, + 0x79, + 0x76, + 0x6f, + 0x2f, + 0x61, + 0x66, + 0x4e, + 0x6d, + 0x39, + 0x30, + 0x49, + 0x74, + 0x76, + 0x0a, + 0x31, + 0x45, + 0x36, + 0x61, + 0x69, + 0x41, + 0x79, + 0x49, + 0x4d, + 0x73, + 0x6d, + 0x51, + 0x43, + 0x31, + 0x4a, + 0x4f, + 0x6f, + 0x56, + 0x68, + 0x2f, + 0x65, + 0x30, + 0x71, + 0x44, + 0x47, + 0x71, + 0x70, + 0x49, + 0x56, + 0x49, + 0x37, + 0x41, + 0x2f, + 0x4f, + 0x73, + 0x7a, + 0x39, + 0x74, + 0x53, + 0x68, + 0x78, + 0x44, + 0x65, + 0x52, + 0x75, + 0x5a, + 0x46, + 0x39, + 0x2b, + 0x67, + 0x4d, + 0x4d, + 0x6e, + 0x30, + 0x68, + 0x41, + 0x4a, + 0x6b, + 0x62, + 0x35, + 0x52, + 0x33, + 0x4d, + 0x78, + 0x0a, + 0x6f, + 0x38, + 0x58, + 0x36, + 0x6b, + 0x44, + 0x77, + 0x61, + 0x73, + 0x7a, + 0x48, + 0x48, + 0x76, + 0x52, + 0x55, + 0x73, + 0x66, + 0x54, + 0x66, + 0x6b, + 0x66, + 0x57, + 0x38, + 0x50, + 0x31, + 0x51, + 0x62, + 0x48, + 0x77, + 0x6e, + 0x4d, + 0x78, + 0x65, + 0x53, + 0x79, + 0x70, + 0x75, + 0x30, + 0x61, + 0x49, + 0x78, + 0x77, + 0x2b, + 0x63, + 0x38, + 0x44, + 0x6d, + 0x7a, + 0x74, + 0x2b, + 0x6c, + 0x73, + 0x37, + 0x62, + 0x6a, + 0x32, + 0x6d, + 0x32, + 0x36, + 0x48, + 0x38, + 0x39, + 0x4a, + 0x68, + 0x0a, + 0x59, + 0x6c, + 0x63, + 0x72, + 0x68, + 0x64, + 0x33, + 0x44, + 0x6d, + 0x2f, + 0x37, + 0x61, + 0x68, + 0x6c, + 0x56, + 0x66, + 0x64, + 0x35, + 0x79, + 0x4b, + 0x77, + 0x41, + 0x57, + 0x38, + 0x76, + 0x77, + 0x32, + 0x77, + 0x57, + 0x79, + 0x45, + 0x43, + 0x56, + 0x79, + 0x37, + 0x4c, + 0x4c, + 0x5a, + 0x36, + 0x63, + 0x4b, + 0x5a, + 0x57, + 0x57, + 0x32, + 0x6d, + 0x5a, + 0x47, + 0x4b, + 0x6a, + 0x37, + 0x7a, + 0x75, + 0x78, + 0x6b, + 0x56, + 0x65, + 0x39, + 0x42, + 0x2f, + 0x4d, + 0x44, + 0x54, + 0x75, + 0x0a, + 0x54, + 0x6e, + 0x39, + 0x7a, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4d, + 0x4c, + 0x70, + 0x6f, + 0x2b, + 0x53, + 0x58, + 0x43, + 0x46, + 0x2b, + 0x35, + 0x73, + 0x4d, + 0x35, + 0x68, + 0x57, + 0x62, + 0x4a, + 0x6d, + 0x38, + 0x77, + 0x52, + 0x64, + 0x4e, + 0x36, + 0x45, + 0x63, + 0x0a, + 0x4d, + 0x48, + 0x6a, + 0x54, + 0x33, + 0x74, + 0x61, + 0x2b, + 0x7a, + 0x79, + 0x74, + 0x6d, + 0x33, + 0x6f, + 0x79, + 0x71, + 0x59, + 0x63, + 0x53, + 0x2f, + 0x62, + 0x53, + 0x43, + 0x56, + 0x74, + 0x30, + 0x6a, + 0x38, + 0x65, + 0x71, + 0x72, + 0x5a, + 0x54, + 0x33, + 0x73, + 0x63, + 0x43, + 0x77, + 0x77, + 0x69, + 0x77, + 0x53, + 0x79, + 0x32, + 0x73, + 0x37, + 0x5a, + 0x63, + 0x65, + 0x74, + 0x35, + 0x75, + 0x6d, + 0x6b, + 0x46, + 0x55, + 0x48, + 0x62, + 0x36, + 0x4a, + 0x65, + 0x6e, + 0x30, + 0x51, + 0x0a, + 0x6c, + 0x58, + 0x4b, + 0x42, + 0x6f, + 0x61, + 0x53, + 0x47, + 0x54, + 0x57, + 0x42, + 0x4f, + 0x5a, + 0x62, + 0x6b, + 0x69, + 0x55, + 0x36, + 0x4f, + 0x36, + 0x6d, + 0x36, + 0x52, + 0x4c, + 0x4d, + 0x72, + 0x4d, + 0x6d, + 0x62, + 0x70, + 0x32, + 0x6b, + 0x68, + 0x4e, + 0x61, + 0x67, + 0x36, + 0x59, + 0x71, + 0x39, + 0x70, + 0x33, + 0x73, + 0x66, + 0x73, + 0x59, + 0x7a, + 0x38, + 0x69, + 0x45, + 0x31, + 0x38, + 0x7a, + 0x36, + 0x43, + 0x31, + 0x76, + 0x70, + 0x75, + 0x4b, + 0x63, + 0x39, + 0x51, + 0x54, + 0x0a, + 0x50, + 0x51, + 0x4c, + 0x51, + 0x65, + 0x7a, + 0x6c, + 0x68, + 0x77, + 0x5a, + 0x5a, + 0x57, + 0x52, + 0x4e, + 0x77, + 0x6b, + 0x43, + 0x79, + 0x4e, + 0x52, + 0x42, + 0x6a, + 0x69, + 0x39, + 0x4e, + 0x37, + 0x55, + 0x2f, + 0x75, + 0x4f, + 0x52, + 0x4f, + 0x49, + 0x38, + 0x64, + 0x6b, + 0x36, + 0x4b, + 0x48, + 0x6e, + 0x4d, + 0x5a, + 0x52, + 0x32, + 0x69, + 0x52, + 0x34, + 0x65, + 0x43, + 0x53, + 0x42, + 0x32, + 0x67, + 0x74, + 0x6b, + 0x76, + 0x68, + 0x50, + 0x65, + 0x4c, + 0x55, + 0x58, + 0x77, + 0x58, + 0x0a, + 0x38, + 0x4a, + 0x79, + 0x6d, + 0x32, + 0x51, + 0x48, + 0x58, + 0x35, + 0x43, + 0x59, + 0x37, + 0x2f, + 0x59, + 0x4d, + 0x66, + 0x73, + 0x34, + 0x44, + 0x30, + 0x49, + 0x38, + 0x66, + 0x70, + 0x6a, + 0x53, + 0x4f, + 0x65, + 0x65, + 0x52, + 0x59, + 0x33, + 0x63, + 0x6e, + 0x4a, + 0x76, + 0x33, + 0x37, + 0x4c, + 0x75, + 0x51, + 0x4c, + 0x35, + 0x72, + 0x4a, + 0x70, + 0x75, + 0x76, + 0x49, + 0x5a, + 0x78, + 0x46, + 0x4a, + 0x33, + 0x6e, + 0x56, + 0x72, + 0x37, + 0x56, + 0x2b, + 0x44, + 0x54, + 0x4d, + 0x72, + 0x0a, + 0x4e, + 0x71, + 0x4e, + 0x45, + 0x34, + 0x69, + 0x57, + 0x48, + 0x4d, + 0x56, + 0x58, + 0x71, + 0x63, + 0x45, + 0x62, + 0x4d, + 0x53, + 0x2f, + 0x59, + 0x4a, + 0x6a, + 0x38, + 0x34, + 0x69, + 0x63, + 0x47, + 0x65, + 0x31, + 0x70, + 0x43, + 0x55, + 0x30, + 0x31, + 0x65, + 0x49, + 0x2f, + 0x61, + 0x33, + 0x34, + 0x72, + 0x79, + 0x53, + 0x67, + 0x55, + 0x75, + 0x2b, + 0x33, + 0x53, + 0x43, + 0x55, + 0x55, + 0x61, + 0x58, + 0x5a, + 0x73, + 0x2f, + 0x2b, + 0x51, + 0x41, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char server_key[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x4a, + 0x4b, + 0x41, + 0x49, + 0x42, + 0x41, + 0x41, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x79, + 0x31, + 0x65, + 0x71, + 0x47, + 0x4a, + 0x69, + 0x45, + 0x61, + 0x4e, + 0x55, + 0x76, + 0x42, + 0x57, + 0x35, + 0x72, + 0x4b, + 0x38, + 0x77, + 0x77, + 0x69, + 0x75, + 0x4d, + 0x72, + 0x74, + 0x4c, + 0x67, + 0x74, + 0x74, + 0x45, + 0x65, + 0x55, + 0x31, + 0x51, + 0x4f, + 0x30, + 0x6e, + 0x4d, + 0x57, + 0x6a, + 0x59, + 0x43, + 0x4f, + 0x34, + 0x75, + 0x36, + 0x61, + 0x31, + 0x0a, + 0x34, + 0x58, + 0x34, + 0x6f, + 0x75, + 0x7a, + 0x68, + 0x57, + 0x4e, + 0x6a, + 0x50, + 0x47, + 0x35, + 0x31, + 0x6f, + 0x4a, + 0x43, + 0x76, + 0x4b, + 0x38, + 0x78, + 0x71, + 0x58, + 0x52, + 0x33, + 0x2f, + 0x7a, + 0x62, + 0x79, + 0x36, + 0x66, + 0x66, + 0x48, + 0x69, + 0x42, + 0x61, + 0x4e, + 0x68, + 0x4d, + 0x34, + 0x4c, + 0x6e, + 0x6c, + 0x78, + 0x44, + 0x30, + 0x77, + 0x4d, + 0x31, + 0x33, + 0x36, + 0x6d, + 0x59, + 0x77, + 0x51, + 0x54, + 0x6a, + 0x35, + 0x33, + 0x57, + 0x36, + 0x64, + 0x4e, + 0x2b, + 0x0a, + 0x6a, + 0x33, + 0x7a, + 0x7a, + 0x61, + 0x4d, + 0x7a, + 0x78, + 0x36, + 0x4f, + 0x55, + 0x30, + 0x56, + 0x4e, + 0x77, + 0x4f, + 0x68, + 0x4d, + 0x2b, + 0x47, + 0x67, + 0x33, + 0x76, + 0x43, + 0x70, + 0x2f, + 0x45, + 0x37, + 0x58, + 0x6b, + 0x57, + 0x65, + 0x4a, + 0x50, + 0x42, + 0x4d, + 0x4a, + 0x62, + 0x33, + 0x52, + 0x75, + 0x62, + 0x50, + 0x76, + 0x61, + 0x33, + 0x72, + 0x4d, + 0x73, + 0x36, + 0x62, + 0x63, + 0x4b, + 0x6c, + 0x6d, + 0x36, + 0x35, + 0x30, + 0x64, + 0x4b, + 0x64, + 0x63, + 0x65, + 0x71, + 0x0a, + 0x32, + 0x41, + 0x37, + 0x50, + 0x71, + 0x64, + 0x4a, + 0x4a, + 0x74, + 0x4f, + 0x4d, + 0x37, + 0x56, + 0x42, + 0x48, + 0x63, + 0x73, + 0x33, + 0x34, + 0x4f, + 0x38, + 0x33, + 0x54, + 0x68, + 0x73, + 0x78, + 0x63, + 0x46, + 0x39, + 0x37, + 0x2f, + 0x51, + 0x31, + 0x55, + 0x54, + 0x39, + 0x43, + 0x6c, + 0x34, + 0x34, + 0x34, + 0x59, + 0x39, + 0x6c, + 0x49, + 0x59, + 0x49, + 0x36, + 0x30, + 0x71, + 0x38, + 0x4e, + 0x6e, + 0x67, + 0x36, + 0x37, + 0x49, + 0x71, + 0x47, + 0x62, + 0x5a, + 0x47, + 0x69, + 0x73, + 0x0a, + 0x59, + 0x6d, + 0x63, + 0x75, + 0x5a, + 0x66, + 0x45, + 0x4d, + 0x55, + 0x65, + 0x59, + 0x45, + 0x79, + 0x6e, + 0x54, + 0x52, + 0x30, + 0x42, + 0x78, + 0x36, + 0x73, + 0x45, + 0x52, + 0x45, + 0x73, + 0x4f, + 0x51, + 0x74, + 0x47, + 0x6c, + 0x31, + 0x62, + 0x6a, + 0x77, + 0x72, + 0x61, + 0x4c, + 0x6d, + 0x30, + 0x46, + 0x43, + 0x38, + 0x42, + 0x77, + 0x6c, + 0x6e, + 0x4f, + 0x55, + 0x76, + 0x78, + 0x44, + 0x34, + 0x66, + 0x5a, + 0x2b, + 0x43, + 0x35, + 0x4a, + 0x73, + 0x48, + 0x62, + 0x5a, + 0x35, + 0x54, + 0x0a, + 0x4b, + 0x30, + 0x46, + 0x74, + 0x73, + 0x32, + 0x65, + 0x43, + 0x2f, + 0x71, + 0x48, + 0x4a, + 0x48, + 0x4c, + 0x52, + 0x73, + 0x41, + 0x6a, + 0x4e, + 0x70, + 0x67, + 0x66, + 0x42, + 0x71, + 0x6c, + 0x45, + 0x6a, + 0x47, + 0x73, + 0x74, + 0x4e, + 0x61, + 0x64, + 0x30, + 0x79, + 0x49, + 0x48, + 0x76, + 0x49, + 0x36, + 0x39, + 0x32, + 0x4b, + 0x79, + 0x56, + 0x54, + 0x37, + 0x56, + 0x4b, + 0x67, + 0x56, + 0x59, + 0x42, + 0x74, + 0x39, + 0x58, + 0x6e, + 0x4b, + 0x36, + 0x48, + 0x41, + 0x36, + 0x66, + 0x42, + 0x0a, + 0x4d, + 0x78, + 0x4b, + 0x68, + 0x31, + 0x48, + 0x54, + 0x30, + 0x37, + 0x47, + 0x42, + 0x52, + 0x71, + 0x34, + 0x44, + 0x62, + 0x4b, + 0x73, + 0x41, + 0x76, + 0x5a, + 0x44, + 0x73, + 0x45, + 0x7a, + 0x6b, + 0x61, + 0x64, + 0x67, + 0x75, + 0x6c, + 0x53, + 0x6a, + 0x6d, + 0x72, + 0x6b, + 0x57, + 0x41, + 0x44, + 0x43, + 0x53, + 0x77, + 0x6d, + 0x58, + 0x7a, + 0x44, + 0x4a, + 0x49, + 0x4f, + 0x6c, + 0x68, + 0x78, + 0x43, + 0x76, + 0x73, + 0x57, + 0x76, + 0x4e, + 0x4b, + 0x4f, + 0x70, + 0x7a, + 0x57, + 0x6a, + 0x0a, + 0x31, + 0x53, + 0x7a, + 0x75, + 0x45, + 0x51, + 0x53, + 0x57, + 0x53, + 0x38, + 0x30, + 0x46, + 0x6d, + 0x54, + 0x58, + 0x35, + 0x55, + 0x39, + 0x4e, + 0x6f, + 0x2f, + 0x6e, + 0x45, + 0x52, + 0x6a, + 0x4b, + 0x6c, + 0x78, + 0x75, + 0x44, + 0x36, + 0x37, + 0x35, + 0x67, + 0x4c, + 0x4e, + 0x79, + 0x30, + 0x6c, + 0x61, + 0x63, + 0x6b, + 0x70, + 0x73, + 0x49, + 0x48, + 0x50, + 0x5a, + 0x36, + 0x7a, + 0x6f, + 0x71, + 0x53, + 0x6c, + 0x38, + 0x4c, + 0x68, + 0x70, + 0x5a, + 0x7a, + 0x78, + 0x51, + 0x67, + 0x75, + 0x0a, + 0x56, + 0x51, + 0x72, + 0x39, + 0x62, + 0x69, + 0x4e, + 0x45, + 0x57, + 0x77, + 0x30, + 0x62, + 0x64, + 0x53, + 0x45, + 0x44, + 0x57, + 0x2b, + 0x63, + 0x68, + 0x6e, + 0x41, + 0x72, + 0x7a, + 0x71, + 0x4b, + 0x71, + 0x62, + 0x65, + 0x54, + 0x34, + 0x38, + 0x52, + 0x74, + 0x71, + 0x46, + 0x77, + 0x54, + 0x45, + 0x44, + 0x69, + 0x6f, + 0x48, + 0x38, + 0x73, + 0x47, + 0x47, + 0x69, + 0x43, + 0x2b, + 0x7a, + 0x6a, + 0x50, + 0x31, + 0x76, + 0x59, + 0x78, + 0x44, + 0x66, + 0x6d, + 0x32, + 0x49, + 0x69, + 0x68, + 0x0a, + 0x41, + 0x4f, + 0x50, + 0x74, + 0x48, + 0x6e, + 0x45, + 0x33, + 0x78, + 0x7a, + 0x6b, + 0x44, + 0x4c, + 0x41, + 0x50, + 0x4a, + 0x76, + 0x7a, + 0x45, + 0x61, + 0x2f, + 0x5a, + 0x69, + 0x61, + 0x54, + 0x69, + 0x4d, + 0x61, + 0x30, + 0x4f, + 0x52, + 0x32, + 0x71, + 0x74, + 0x75, + 0x30, + 0x68, + 0x54, + 0x6a, + 0x62, + 0x34, + 0x63, + 0x7a, + 0x58, + 0x49, + 0x36, + 0x54, + 0x79, + 0x37, + 0x78, + 0x6b, + 0x53, + 0x4c, + 0x45, + 0x74, + 0x44, + 0x4f, + 0x6a, + 0x43, + 0x61, + 0x70, + 0x53, + 0x71, + 0x74, + 0x0a, + 0x34, + 0x45, + 0x4b, + 0x47, + 0x67, + 0x43, + 0x69, + 0x78, + 0x7a, + 0x63, + 0x55, + 0x35, + 0x45, + 0x36, + 0x5a, + 0x4d, + 0x49, + 0x4f, + 0x56, + 0x6a, + 0x72, + 0x66, + 0x6a, + 0x39, + 0x70, + 0x77, + 0x67, + 0x67, + 0x2f, + 0x66, + 0x56, + 0x31, + 0x35, + 0x30, + 0x63, + 0x31, + 0x68, + 0x6e, + 0x6e, + 0x56, + 0x47, + 0x75, + 0x36, + 0x51, + 0x42, + 0x30, + 0x44, + 0x72, + 0x59, + 0x4e, + 0x73, + 0x59, + 0x50, + 0x43, + 0x44, + 0x44, + 0x50, + 0x70, + 0x4d, + 0x43, + 0x41, + 0x77, + 0x45, + 0x41, + 0x0a, + 0x41, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x74, + 0x69, + 0x33, + 0x49, + 0x65, + 0x31, + 0x6a, + 0x4a, + 0x36, + 0x4a, + 0x4a, + 0x33, + 0x47, + 0x6b, + 0x71, + 0x66, + 0x51, + 0x68, + 0x49, + 0x69, + 0x75, + 0x34, + 0x78, + 0x6b, + 0x4c, + 0x2f, + 0x6d, + 0x44, + 0x47, + 0x53, + 0x32, + 0x34, + 0x72, + 0x6c, + 0x41, + 0x76, + 0x6c, + 0x50, + 0x57, + 0x4e, + 0x4e, + 0x69, + 0x6c, + 0x4b, + 0x37, + 0x50, + 0x53, + 0x6a, + 0x70, + 0x32, + 0x38, + 0x38, + 0x56, + 0x75, + 0x42, + 0x57, + 0x0a, + 0x66, + 0x53, + 0x46, + 0x4f, + 0x5a, + 0x79, + 0x42, + 0x48, + 0x55, + 0x63, + 0x50, + 0x41, + 0x56, + 0x63, + 0x6c, + 0x69, + 0x69, + 0x32, + 0x63, + 0x6b, + 0x46, + 0x51, + 0x5a, + 0x46, + 0x34, + 0x39, + 0x76, + 0x67, + 0x64, + 0x75, + 0x6f, + 0x70, + 0x50, + 0x35, + 0x6f, + 0x57, + 0x4c, + 0x54, + 0x70, + 0x5a, + 0x6c, + 0x77, + 0x67, + 0x6c, + 0x55, + 0x6a, + 0x6a, + 0x6f, + 0x76, + 0x5a, + 0x63, + 0x51, + 0x59, + 0x56, + 0x58, + 0x57, + 0x34, + 0x66, + 0x39, + 0x30, + 0x70, + 0x4d, + 0x52, + 0x5a, + 0x0a, + 0x52, + 0x2b, + 0x74, + 0x79, + 0x51, + 0x30, + 0x55, + 0x55, + 0x7a, + 0x50, + 0x43, + 0x51, + 0x39, + 0x32, + 0x50, + 0x2b, + 0x4e, + 0x6a, + 0x44, + 0x75, + 0x6d, + 0x37, + 0x75, + 0x4c, + 0x77, + 0x46, + 0x52, + 0x36, + 0x4a, + 0x4b, + 0x59, + 0x34, + 0x4c, + 0x45, + 0x53, + 0x46, + 0x6b, + 0x4b, + 0x6e, + 0x6f, + 0x6c, + 0x46, + 0x77, + 0x2b, + 0x6a, + 0x4d, + 0x4c, + 0x76, + 0x72, + 0x52, + 0x34, + 0x32, + 0x47, + 0x2b, + 0x66, + 0x54, + 0x72, + 0x4e, + 0x63, + 0x34, + 0x50, + 0x6c, + 0x48, + 0x6c, + 0x0a, + 0x4c, + 0x32, + 0x63, + 0x57, + 0x56, + 0x47, + 0x2f, + 0x63, + 0x61, + 0x41, + 0x50, + 0x63, + 0x71, + 0x55, + 0x70, + 0x69, + 0x6d, + 0x2b, + 0x31, + 0x59, + 0x72, + 0x32, + 0x72, + 0x76, + 0x55, + 0x75, + 0x51, + 0x49, + 0x51, + 0x53, + 0x33, + 0x42, + 0x6b, + 0x48, + 0x54, + 0x45, + 0x69, + 0x6f, + 0x7a, + 0x2b, + 0x33, + 0x70, + 0x74, + 0x76, + 0x65, + 0x39, + 0x68, + 0x2b, + 0x32, + 0x4c, + 0x77, + 0x70, + 0x57, + 0x49, + 0x42, + 0x66, + 0x55, + 0x5a, + 0x70, + 0x58, + 0x2b, + 0x34, + 0x56, + 0x48, + 0x0a, + 0x49, + 0x47, + 0x6b, + 0x51, + 0x69, + 0x63, + 0x4b, + 0x33, + 0x35, + 0x39, + 0x68, + 0x63, + 0x49, + 0x32, + 0x68, + 0x47, + 0x6b, + 0x71, + 0x55, + 0x62, + 0x73, + 0x36, + 0x39, + 0x49, + 0x78, + 0x4a, + 0x4e, + 0x59, + 0x66, + 0x70, + 0x65, + 0x32, + 0x47, + 0x5a, + 0x31, + 0x45, + 0x51, + 0x63, + 0x63, + 0x7a, + 0x30, + 0x53, + 0x73, + 0x48, + 0x4e, + 0x71, + 0x57, + 0x65, + 0x77, + 0x2b, + 0x6f, + 0x52, + 0x63, + 0x61, + 0x69, + 0x54, + 0x6f, + 0x77, + 0x46, + 0x76, + 0x6f, + 0x33, + 0x68, + 0x74, + 0x0a, + 0x36, + 0x47, + 0x78, + 0x53, + 0x6e, + 0x71, + 0x45, + 0x57, + 0x30, + 0x37, + 0x48, + 0x30, + 0x4b, + 0x54, + 0x4b, + 0x59, + 0x69, + 0x6c, + 0x6e, + 0x5a, + 0x69, + 0x34, + 0x58, + 0x49, + 0x57, + 0x33, + 0x48, + 0x35, + 0x30, + 0x51, + 0x61, + 0x75, + 0x61, + 0x68, + 0x62, + 0x77, + 0x48, + 0x30, + 0x75, + 0x56, + 0x73, + 0x64, + 0x32, + 0x55, + 0x39, + 0x6f, + 0x34, + 0x6c, + 0x37, + 0x54, + 0x38, + 0x5a, + 0x4d, + 0x65, + 0x34, + 0x52, + 0x50, + 0x6d, + 0x66, + 0x39, + 0x77, + 0x46, + 0x6e, + 0x2f, + 0x0a, + 0x45, + 0x7a, + 0x34, + 0x46, + 0x74, + 0x68, + 0x62, + 0x6e, + 0x51, + 0x4f, + 0x57, + 0x42, + 0x6f, + 0x31, + 0x44, + 0x78, + 0x61, + 0x42, + 0x6d, + 0x78, + 0x39, + 0x42, + 0x66, + 0x6e, + 0x35, + 0x6e, + 0x43, + 0x35, + 0x49, + 0x36, + 0x52, + 0x79, + 0x72, + 0x36, + 0x58, + 0x4d, + 0x6e, + 0x6e, + 0x4a, + 0x4a, + 0x2b, + 0x65, + 0x41, + 0x74, + 0x53, + 0x6a, + 0x58, + 0x53, + 0x41, + 0x56, + 0x37, + 0x2f, + 0x36, + 0x33, + 0x43, + 0x72, + 0x70, + 0x63, + 0x42, + 0x2b, + 0x73, + 0x42, + 0x74, + 0x46, + 0x0a, + 0x69, + 0x7a, + 0x46, + 0x33, + 0x79, + 0x45, + 0x51, + 0x38, + 0x72, + 0x74, + 0x57, + 0x41, + 0x43, + 0x71, + 0x78, + 0x6a, + 0x4d, + 0x55, + 0x50, + 0x43, + 0x33, + 0x4a, + 0x71, + 0x39, + 0x6c, + 0x39, + 0x33, + 0x39, + 0x38, + 0x2f, + 0x6a, + 0x7a, + 0x43, + 0x70, + 0x5a, + 0x5a, + 0x4c, + 0x76, + 0x7a, + 0x34, + 0x4e, + 0x55, + 0x6a, + 0x6a, + 0x74, + 0x66, + 0x4e, + 0x68, + 0x33, + 0x69, + 0x36, + 0x44, + 0x50, + 0x36, + 0x49, + 0x68, + 0x41, + 0x4b, + 0x31, + 0x33, + 0x54, + 0x56, + 0x37, + 0x49, + 0x0a, + 0x34, + 0x47, + 0x34, + 0x30, + 0x62, + 0x72, + 0x5a, + 0x68, + 0x6d, + 0x58, + 0x53, + 0x56, + 0x4b, + 0x42, + 0x6e, + 0x78, + 0x47, + 0x37, + 0x44, + 0x35, + 0x69, + 0x59, + 0x36, + 0x50, + 0x62, + 0x67, + 0x56, + 0x57, + 0x48, + 0x46, + 0x44, + 0x45, + 0x6a, + 0x50, + 0x44, + 0x77, + 0x6f, + 0x4a, + 0x6b, + 0x74, + 0x4b, + 0x31, + 0x42, + 0x50, + 0x61, + 0x64, + 0x33, + 0x71, + 0x57, + 0x66, + 0x39, + 0x34, + 0x66, + 0x6d, + 0x74, + 0x35, + 0x79, + 0x6b, + 0x5a, + 0x64, + 0x2f, + 0x74, + 0x65, + 0x71, + 0x0a, + 0x65, + 0x52, + 0x49, + 0x45, + 0x76, + 0x55, + 0x7a, + 0x4a, + 0x47, + 0x35, + 0x50, + 0x46, + 0x47, + 0x77, + 0x4f, + 0x7a, + 0x78, + 0x56, + 0x52, + 0x48, + 0x44, + 0x4e, + 0x38, + 0x51, + 0x51, + 0x6d, + 0x4e, + 0x4a, + 0x63, + 0x43, + 0x4a, + 0x75, + 0x79, + 0x74, + 0x77, + 0x34, + 0x44, + 0x6e, + 0x73, + 0x78, + 0x6f, + 0x43, + 0x38, + 0x33, + 0x69, + 0x7a, + 0x66, + 0x68, + 0x2b, + 0x6e, + 0x45, + 0x71, + 0x45, + 0x50, + 0x50, + 0x51, + 0x69, + 0x44, + 0x46, + 0x6f, + 0x54, + 0x4c, + 0x79, + 0x44, + 0x0a, + 0x2b, + 0x34, + 0x56, + 0x52, + 0x67, + 0x46, + 0x42, + 0x62, + 0x36, + 0x61, + 0x69, + 0x72, + 0x56, + 0x63, + 0x79, + 0x58, + 0x57, + 0x64, + 0x72, + 0x32, + 0x30, + 0x43, + 0x64, + 0x4f, + 0x47, + 0x67, + 0x58, + 0x6f, + 0x42, + 0x4d, + 0x72, + 0x4e, + 0x42, + 0x6c, + 0x2b, + 0x45, + 0x36, + 0x32, + 0x4d, + 0x59, + 0x41, + 0x65, + 0x32, + 0x71, + 0x63, + 0x36, + 0x37, + 0x63, + 0x51, + 0x59, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4f, + 0x39, + 0x32, + 0x58, + 0x4e, + 0x41, + 0x46, + 0x0a, + 0x52, + 0x41, + 0x77, + 0x5a, + 0x79, + 0x64, + 0x7a, + 0x59, + 0x63, + 0x68, + 0x35, + 0x45, + 0x35, + 0x65, + 0x6a, + 0x7a, + 0x39, + 0x54, + 0x5a, + 0x62, + 0x48, + 0x66, + 0x65, + 0x66, + 0x34, + 0x7a, + 0x4e, + 0x42, + 0x48, + 0x55, + 0x2f, + 0x6d, + 0x56, + 0x31, + 0x76, + 0x68, + 0x38, + 0x39, + 0x64, + 0x39, + 0x4b, + 0x6a, + 0x53, + 0x46, + 0x2f, + 0x4f, + 0x69, + 0x4c, + 0x33, + 0x45, + 0x6e, + 0x6f, + 0x59, + 0x78, + 0x50, + 0x6b, + 0x48, + 0x67, + 0x55, + 0x58, + 0x79, + 0x6e, + 0x39, + 0x41, + 0x0a, + 0x34, + 0x43, + 0x34, + 0x75, + 0x31, + 0x72, + 0x6c, + 0x64, + 0x5a, + 0x36, + 0x64, + 0x77, + 0x43, + 0x56, + 0x41, + 0x62, + 0x5a, + 0x4a, + 0x6e, + 0x54, + 0x38, + 0x55, + 0x46, + 0x32, + 0x6e, + 0x4c, + 0x52, + 0x46, + 0x4d, + 0x58, + 0x68, + 0x61, + 0x71, + 0x79, + 0x4a, + 0x45, + 0x6d, + 0x54, + 0x37, + 0x6a, + 0x43, + 0x59, + 0x52, + 0x79, + 0x37, + 0x65, + 0x72, + 0x64, + 0x37, + 0x56, + 0x4c, + 0x39, + 0x35, + 0x67, + 0x56, + 0x68, + 0x55, + 0x4f, + 0x73, + 0x4a, + 0x59, + 0x77, + 0x4c, + 0x6d, + 0x0a, + 0x74, + 0x59, + 0x66, + 0x63, + 0x69, + 0x6b, + 0x30, + 0x37, + 0x51, + 0x6d, + 0x44, + 0x53, + 0x50, + 0x56, + 0x75, + 0x47, + 0x68, + 0x56, + 0x32, + 0x4b, + 0x65, + 0x6b, + 0x6e, + 0x7a, + 0x54, + 0x61, + 0x31, + 0x4e, + 0x53, + 0x54, + 0x55, + 0x38, + 0x76, + 0x77, + 0x51, + 0x46, + 0x2b, + 0x77, + 0x4b, + 0x48, + 0x69, + 0x38, + 0x36, + 0x34, + 0x64, + 0x6d, + 0x7a, + 0x6b, + 0x73, + 0x6c, + 0x59, + 0x6e, + 0x51, + 0x71, + 0x62, + 0x46, + 0x6f, + 0x54, + 0x56, + 0x52, + 0x5a, + 0x74, + 0x4e, + 0x65, + 0x0a, + 0x72, + 0x4c, + 0x62, + 0x36, + 0x67, + 0x36, + 0x6e, + 0x63, + 0x77, + 0x36, + 0x4a, + 0x57, + 0x78, + 0x39, + 0x79, + 0x65, + 0x42, + 0x30, + 0x37, + 0x56, + 0x64, + 0x70, + 0x4a, + 0x70, + 0x36, + 0x78, + 0x39, + 0x54, + 0x59, + 0x54, + 0x2b, + 0x48, + 0x47, + 0x6a, + 0x33, + 0x6b, + 0x62, + 0x35, + 0x61, + 0x63, + 0x75, + 0x49, + 0x6e, + 0x50, + 0x2b, + 0x6a, + 0x67, + 0x37, + 0x47, + 0x5a, + 0x7a, + 0x6b, + 0x72, + 0x48, + 0x4a, + 0x4d, + 0x6e, + 0x48, + 0x4c, + 0x77, + 0x65, + 0x46, + 0x76, + 0x62, + 0x0a, + 0x4e, + 0x57, + 0x5a, + 0x66, + 0x6c, + 0x50, + 0x66, + 0x69, + 0x41, + 0x4e, + 0x6d, + 0x77, + 0x4d, + 0x38, + 0x77, + 0x4d, + 0x52, + 0x2b, + 0x6b, + 0x66, + 0x50, + 0x32, + 0x76, + 0x6f, + 0x67, + 0x47, + 0x61, + 0x6d, + 0x4b, + 0x43, + 0x69, + 0x65, + 0x6f, + 0x6a, + 0x56, + 0x61, + 0x54, + 0x69, + 0x38, + 0x76, + 0x67, + 0x71, + 0x71, + 0x6a, + 0x78, + 0x34, + 0x55, + 0x47, + 0x50, + 0x64, + 0x35, + 0x39, + 0x36, + 0x46, + 0x4e, + 0x46, + 0x65, + 0x69, + 0x5a, + 0x52, + 0x76, + 0x58, + 0x4e, + 0x6b, + 0x0a, + 0x30, + 0x64, + 0x42, + 0x30, + 0x41, + 0x4b, + 0x32, + 0x51, + 0x2f, + 0x31, + 0x32, + 0x36, + 0x53, + 0x6c, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4e, + 0x6c, + 0x69, + 0x74, + 0x64, + 0x78, + 0x48, + 0x74, + 0x74, + 0x78, + 0x68, + 0x2f, + 0x57, + 0x71, + 0x77, + 0x46, + 0x4b, + 0x33, + 0x35, + 0x62, + 0x63, + 0x4f, + 0x54, + 0x58, + 0x61, + 0x68, + 0x36, + 0x50, + 0x61, + 0x70, + 0x50, + 0x64, + 0x67, + 0x2f, + 0x37, + 0x32, + 0x50, + 0x75, + 0x4f, + 0x6c, + 0x33, + 0x57, + 0x74, + 0x0a, + 0x37, + 0x57, + 0x6e, + 0x38, + 0x41, + 0x74, + 0x36, + 0x46, + 0x49, + 0x47, + 0x51, + 0x33, + 0x58, + 0x5a, + 0x4f, + 0x4f, + 0x50, + 0x66, + 0x39, + 0x76, + 0x35, + 0x48, + 0x66, + 0x71, + 0x50, + 0x70, + 0x53, + 0x6c, + 0x4e, + 0x56, + 0x39, + 0x33, + 0x41, + 0x58, + 0x58, + 0x61, + 0x4f, + 0x36, + 0x49, + 0x54, + 0x7a, + 0x77, + 0x49, + 0x45, + 0x62, + 0x38, + 0x49, + 0x47, + 0x33, + 0x69, + 0x45, + 0x4f, + 0x74, + 0x54, + 0x4c, + 0x77, + 0x32, + 0x39, + 0x32, + 0x4b, + 0x55, + 0x38, + 0x6c, + 0x4a, + 0x0a, + 0x4c, + 0x6c, + 0x38, + 0x58, + 0x70, + 0x68, + 0x4f, + 0x6b, + 0x56, + 0x55, + 0x72, + 0x50, + 0x6b, + 0x7a, + 0x47, + 0x47, + 0x36, + 0x64, + 0x66, + 0x56, + 0x4e, + 0x70, + 0x36, + 0x49, + 0x32, + 0x68, + 0x54, + 0x70, + 0x59, + 0x4a, + 0x49, + 0x53, + 0x77, + 0x41, + 0x55, + 0x35, + 0x48, + 0x76, + 0x4b, + 0x70, + 0x63, + 0x39, + 0x6f, + 0x42, + 0x30, + 0x59, + 0x4b, + 0x68, + 0x56, + 0x4c, + 0x66, + 0x43, + 0x2b, + 0x69, + 0x64, + 0x61, + 0x6b, + 0x56, + 0x43, + 0x35, + 0x66, + 0x5a, + 0x7a, + 0x36, + 0x0a, + 0x7a, + 0x46, + 0x41, + 0x6f, + 0x32, + 0x37, + 0x46, + 0x65, + 0x6f, + 0x49, + 0x45, + 0x64, + 0x48, + 0x74, + 0x39, + 0x32, + 0x5a, + 0x65, + 0x36, + 0x51, + 0x33, + 0x34, + 0x4e, + 0x4a, + 0x67, + 0x70, + 0x56, + 0x62, + 0x67, + 0x6d, + 0x54, + 0x30, + 0x2b, + 0x77, + 0x74, + 0x75, + 0x75, + 0x72, + 0x71, + 0x6f, + 0x5a, + 0x2f, + 0x7a, + 0x52, + 0x74, + 0x6b, + 0x78, + 0x34, + 0x35, + 0x77, + 0x75, + 0x71, + 0x49, + 0x33, + 0x45, + 0x2b, + 0x54, + 0x44, + 0x61, + 0x31, + 0x53, + 0x75, + 0x71, + 0x33, + 0x0a, + 0x34, + 0x4f, + 0x51, + 0x73, + 0x65, + 0x32, + 0x57, + 0x63, + 0x67, + 0x6a, + 0x7a, + 0x53, + 0x66, + 0x64, + 0x4c, + 0x4a, + 0x74, + 0x54, + 0x66, + 0x79, + 0x65, + 0x4c, + 0x31, + 0x70, + 0x34, + 0x56, + 0x68, + 0x47, + 0x79, + 0x63, + 0x78, + 0x67, + 0x2b, + 0x4a, + 0x45, + 0x35, + 0x48, + 0x34, + 0x6a, + 0x69, + 0x33, + 0x7a, + 0x35, + 0x33, + 0x46, + 0x78, + 0x72, + 0x4d, + 0x6e, + 0x41, + 0x30, + 0x6d, + 0x53, + 0x45, + 0x6c, + 0x79, + 0x45, + 0x6a, + 0x69, + 0x38, + 0x38, + 0x78, + 0x54, + 0x47, + 0x0a, + 0x48, + 0x43, + 0x59, + 0x47, + 0x49, + 0x6e, + 0x4a, + 0x70, + 0x5a, + 0x72, + 0x2b, + 0x51, + 0x7a, + 0x67, + 0x50, + 0x54, + 0x7a, + 0x57, + 0x66, + 0x35, + 0x52, + 0x34, + 0x36, + 0x6b, + 0x30, + 0x38, + 0x47, + 0x4b, + 0x73, + 0x6d, + 0x56, + 0x67, + 0x6a, + 0x31, + 0x50, + 0x32, + 0x7a, + 0x39, + 0x4f, + 0x65, + 0x33, + 0x61, + 0x4d, + 0x43, + 0x67, + 0x67, + 0x45, + 0x41, + 0x48, + 0x42, + 0x6b, + 0x64, + 0x5a, + 0x75, + 0x4e, + 0x58, + 0x53, + 0x72, + 0x77, + 0x7a, + 0x37, + 0x5a, + 0x41, + 0x51, + 0x0a, + 0x51, + 0x2f, + 0x44, + 0x39, + 0x73, + 0x55, + 0x6e, + 0x2b, + 0x2b, + 0x66, + 0x50, + 0x54, + 0x48, + 0x6c, + 0x31, + 0x4b, + 0x67, + 0x5a, + 0x63, + 0x67, + 0x59, + 0x32, + 0x47, + 0x35, + 0x32, + 0x6e, + 0x51, + 0x32, + 0x38, + 0x70, + 0x41, + 0x6a, + 0x52, + 0x61, + 0x70, + 0x37, + 0x4e, + 0x4b, + 0x5a, + 0x45, + 0x6f, + 0x50, + 0x39, + 0x39, + 0x73, + 0x4c, + 0x58, + 0x52, + 0x74, + 0x2f, + 0x4e, + 0x45, + 0x74, + 0x59, + 0x33, + 0x64, + 0x51, + 0x45, + 0x34, + 0x4b, + 0x73, + 0x42, + 0x46, + 0x2f, + 0x0a, + 0x75, + 0x69, + 0x76, + 0x78, + 0x53, + 0x38, + 0x38, + 0x4c, + 0x44, + 0x4f, + 0x6e, + 0x4c, + 0x6f, + 0x30, + 0x7a, + 0x52, + 0x73, + 0x6d, + 0x31, + 0x30, + 0x45, + 0x70, + 0x56, + 0x42, + 0x41, + 0x33, + 0x4a, + 0x64, + 0x4d, + 0x50, + 0x33, + 0x65, + 0x2f, + 0x67, + 0x57, + 0x6d, + 0x57, + 0x53, + 0x72, + 0x56, + 0x55, + 0x43, + 0x6d, + 0x75, + 0x74, + 0x65, + 0x37, + 0x6e, + 0x57, + 0x63, + 0x7a, + 0x75, + 0x4b, + 0x30, + 0x62, + 0x37, + 0x41, + 0x67, + 0x67, + 0x6b, + 0x79, + 0x6b, + 0x38, + 0x72, + 0x0a, + 0x79, + 0x4d, + 0x53, + 0x69, + 0x6f, + 0x6e, + 0x79, + 0x30, + 0x5a, + 0x58, + 0x64, + 0x38, + 0x52, + 0x66, + 0x55, + 0x67, + 0x70, + 0x6a, + 0x63, + 0x74, + 0x59, + 0x65, + 0x76, + 0x51, + 0x31, + 0x68, + 0x34, + 0x46, + 0x69, + 0x42, + 0x52, + 0x7a, + 0x6d, + 0x54, + 0x77, + 0x58, + 0x32, + 0x55, + 0x73, + 0x30, + 0x69, + 0x64, + 0x74, + 0x74, + 0x66, + 0x4c, + 0x67, + 0x76, + 0x35, + 0x46, + 0x75, + 0x36, + 0x33, + 0x77, + 0x35, + 0x36, + 0x34, + 0x62, + 0x57, + 0x66, + 0x67, + 0x57, + 0x30, + 0x41, + 0x0a, + 0x48, + 0x36, + 0x70, + 0x6a, + 0x4a, + 0x55, + 0x58, + 0x39, + 0x77, + 0x59, + 0x68, + 0x57, + 0x73, + 0x33, + 0x4e, + 0x75, + 0x50, + 0x57, + 0x7a, + 0x42, + 0x4f, + 0x6e, + 0x30, + 0x56, + 0x33, + 0x54, + 0x46, + 0x53, + 0x7a, + 0x71, + 0x39, + 0x78, + 0x44, + 0x2b, + 0x71, + 0x78, + 0x68, + 0x36, + 0x75, + 0x58, + 0x6f, + 0x38, + 0x74, + 0x4a, + 0x57, + 0x4e, + 0x67, + 0x59, + 0x65, + 0x33, + 0x77, + 0x77, + 0x41, + 0x30, + 0x64, + 0x44, + 0x69, + 0x74, + 0x47, + 0x6f, + 0x58, + 0x7a, + 0x58, + 0x4c, + 0x0a, + 0x74, + 0x37, + 0x39, + 0x76, + 0x4e, + 0x49, + 0x41, + 0x30, + 0x5a, + 0x2b, + 0x78, + 0x63, + 0x75, + 0x73, + 0x4b, + 0x37, + 0x58, + 0x6c, + 0x41, + 0x63, + 0x34, + 0x6a, + 0x64, + 0x4b, + 0x2f, + 0x55, + 0x75, + 0x74, + 0x5a, + 0x4f, + 0x67, + 0x45, + 0x51, + 0x78, + 0x57, + 0x59, + 0x73, + 0x63, + 0x6b, + 0x37, + 0x74, + 0x55, + 0x47, + 0x38, + 0x34, + 0x78, + 0x69, + 0x42, + 0x37, + 0x73, + 0x45, + 0x6b, + 0x6f, + 0x38, + 0x65, + 0x75, + 0x76, + 0x76, + 0x44, + 0x39, + 0x71, + 0x32, + 0x41, + 0x61, + 0x0a, + 0x4e, + 0x74, + 0x74, + 0x48, + 0x49, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x42, + 0x44, + 0x77, + 0x30, + 0x5a, + 0x6e, + 0x56, + 0x52, + 0x2b, + 0x47, + 0x55, + 0x54, + 0x46, + 0x61, + 0x71, + 0x57, + 0x71, + 0x34, + 0x31, + 0x4a, + 0x44, + 0x48, + 0x72, + 0x30, + 0x38, + 0x35, + 0x72, + 0x6d, + 0x6b, + 0x38, + 0x68, + 0x71, + 0x71, + 0x4a, + 0x7a, + 0x76, + 0x72, + 0x54, + 0x5a, + 0x35, + 0x36, + 0x45, + 0x2b, + 0x32, + 0x4f, + 0x50, + 0x62, + 0x78, + 0x52, + 0x42, + 0x34, + 0x77, + 0x4f, + 0x32, + 0x0a, + 0x32, + 0x5a, + 0x39, + 0x63, + 0x32, + 0x4c, + 0x45, + 0x77, + 0x78, + 0x2b, + 0x4e, + 0x54, + 0x45, + 0x66, + 0x7a, + 0x52, + 0x2f, + 0x7a, + 0x48, + 0x46, + 0x41, + 0x57, + 0x61, + 0x2b, + 0x49, + 0x76, + 0x6f, + 0x46, + 0x79, + 0x75, + 0x44, + 0x7a, + 0x67, + 0x77, + 0x69, + 0x48, + 0x56, + 0x66, + 0x61, + 0x32, + 0x48, + 0x55, + 0x79, + 0x55, + 0x68, + 0x59, + 0x6f, + 0x7a, + 0x34, + 0x2b, + 0x4f, + 0x38, + 0x55, + 0x67, + 0x69, + 0x58, + 0x63, + 0x64, + 0x71, + 0x2b, + 0x4d, + 0x72, + 0x4b, + 0x33, + 0x0a, + 0x73, + 0x44, + 0x37, + 0x30, + 0x36, + 0x46, + 0x5a, + 0x4b, + 0x68, + 0x6c, + 0x63, + 0x52, + 0x55, + 0x68, + 0x32, + 0x51, + 0x2b, + 0x54, + 0x4b, + 0x32, + 0x4e, + 0x67, + 0x77, + 0x50, + 0x56, + 0x4c, + 0x2f, + 0x31, + 0x36, + 0x71, + 0x61, + 0x48, + 0x5a, + 0x5a, + 0x75, + 0x32, + 0x65, + 0x61, + 0x33, + 0x62, + 0x33, + 0x6e, + 0x5a, + 0x32, + 0x41, + 0x41, + 0x42, + 0x65, + 0x52, + 0x6f, + 0x51, + 0x47, + 0x68, + 0x42, + 0x4e, + 0x65, + 0x52, + 0x57, + 0x34, + 0x2b, + 0x4f, + 0x76, + 0x34, + 0x54, + 0x0a, + 0x59, + 0x55, + 0x31, + 0x33, + 0x59, + 0x51, + 0x50, + 0x6a, + 0x4f, + 0x74, + 0x76, + 0x42, + 0x70, + 0x41, + 0x2b, + 0x46, + 0x6d, + 0x56, + 0x48, + 0x72, + 0x51, + 0x49, + 0x5a, + 0x6c, + 0x43, + 0x4f, + 0x35, + 0x43, + 0x4d, + 0x79, + 0x36, + 0x2f, + 0x47, + 0x32, + 0x77, + 0x56, + 0x4b, + 0x48, + 0x62, + 0x31, + 0x4a, + 0x75, + 0x4d, + 0x48, + 0x63, + 0x4f, + 0x44, + 0x33, + 0x43, + 0x4d, + 0x70, + 0x43, + 0x6f, + 0x6e, + 0x45, + 0x44, + 0x44, + 0x32, + 0x36, + 0x6a, + 0x76, + 0x51, + 0x62, + 0x49, + 0x0a, + 0x43, + 0x4f, + 0x46, + 0x5a, + 0x37, + 0x4c, + 0x43, + 0x46, + 0x73, + 0x52, + 0x71, + 0x38, + 0x43, + 0x4a, + 0x35, + 0x4c, + 0x45, + 0x7a, + 0x33, + 0x69, + 0x62, + 0x4c, + 0x50, + 0x76, + 0x4b, + 0x50, + 0x78, + 0x51, + 0x67, + 0x6c, + 0x41, + 0x4f, + 0x75, + 0x37, + 0x62, + 0x44, + 0x44, + 0x74, + 0x62, + 0x79, + 0x4c, + 0x6d, + 0x58, + 0x49, + 0x4a, + 0x7a, + 0x76, + 0x51, + 0x33, + 0x65, + 0x31, + 0x30, + 0x49, + 0x31, + 0x45, + 0x77, + 0x39, + 0x53, + 0x61, + 0x56, + 0x76, + 0x59, + 0x39, + 0x65, + 0x0a, + 0x31, + 0x62, + 0x65, + 0x79, + 0x62, + 0x37, + 0x71, + 0x4f, + 0x31, + 0x36, + 0x44, + 0x62, + 0x73, + 0x54, + 0x75, + 0x6c, + 0x45, + 0x43, + 0x51, + 0x4e, + 0x2f, + 0x2b, + 0x79, + 0x4e, + 0x77, + 0x74, + 0x6b, + 0x44, + 0x34, + 0x58, + 0x69, + 0x37, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x41, + 0x63, + 0x73, + 0x6f, + 0x4f, + 0x7a, + 0x75, + 0x50, + 0x6d, + 0x37, + 0x35, + 0x4a, + 0x4f, + 0x32, + 0x42, + 0x33, + 0x76, + 0x32, + 0x45, + 0x64, + 0x2b, + 0x37, + 0x47, + 0x4b, + 0x31, + 0x53, + 0x63, + 0x0a, + 0x6c, + 0x76, + 0x56, + 0x4e, + 0x55, + 0x33, + 0x43, + 0x7a, + 0x30, + 0x4e, + 0x66, + 0x4c, + 0x50, + 0x4a, + 0x56, + 0x79, + 0x73, + 0x56, + 0x53, + 0x48, + 0x65, + 0x67, + 0x61, + 0x53, + 0x5a, + 0x38, + 0x33, + 0x5a, + 0x6f, + 0x4e, + 0x50, + 0x68, + 0x6d, + 0x54, + 0x42, + 0x47, + 0x62, + 0x72, + 0x56, + 0x45, + 0x72, + 0x31, + 0x62, + 0x33, + 0x42, + 0x55, + 0x59, + 0x30, + 0x6c, + 0x6b, + 0x46, + 0x49, + 0x4f, + 0x6f, + 0x33, + 0x6d, + 0x66, + 0x30, + 0x32, + 0x6c, + 0x37, + 0x57, + 0x48, + 0x59, + 0x0a, + 0x78, + 0x4d, + 0x51, + 0x38, + 0x48, + 0x63, + 0x32, + 0x61, + 0x6e, + 0x55, + 0x38, + 0x71, + 0x64, + 0x4f, + 0x58, + 0x76, + 0x6f, + 0x58, + 0x62, + 0x51, + 0x34, + 0x2f, + 0x59, + 0x59, + 0x4b, + 0x31, + 0x57, + 0x42, + 0x76, + 0x4b, + 0x75, + 0x2f, + 0x43, + 0x6c, + 0x42, + 0x53, + 0x45, + 0x63, + 0x61, + 0x4f, + 0x39, + 0x30, + 0x62, + 0x71, + 0x79, + 0x44, + 0x73, + 0x37, + 0x33, + 0x6d, + 0x69, + 0x46, + 0x57, + 0x2f, + 0x43, + 0x31, + 0x54, + 0x66, + 0x78, + 0x50, + 0x58, + 0x6e, + 0x6b, + 0x53, + 0x0a, + 0x54, + 0x2b, + 0x72, + 0x4f, + 0x4d, + 0x6d, + 0x77, + 0x52, + 0x51, + 0x67, + 0x4c, + 0x46, + 0x50, + 0x6c, + 0x35, + 0x46, + 0x41, + 0x62, + 0x48, + 0x4f, + 0x6b, + 0x31, + 0x41, + 0x58, + 0x5a, + 0x45, + 0x34, + 0x46, + 0x46, + 0x47, + 0x50, + 0x75, + 0x55, + 0x62, + 0x52, + 0x74, + 0x6b, + 0x4f, + 0x45, + 0x72, + 0x64, + 0x77, + 0x34, + 0x74, + 0x5a, + 0x68, + 0x53, + 0x77, + 0x66, + 0x58, + 0x74, + 0x2f, + 0x46, + 0x41, + 0x36, + 0x2b, + 0x32, + 0x4c, + 0x6e, + 0x48, + 0x79, + 0x37, + 0x38, + 0x38, + 0x0a, + 0x58, + 0x4f, + 0x46, + 0x62, + 0x71, + 0x30, + 0x46, + 0x4b, + 0x6e, + 0x63, + 0x6e, + 0x68, + 0x32, + 0x6a, + 0x61, + 0x2b, + 0x64, + 0x37, + 0x65, + 0x4e, + 0x6c, + 0x48, + 0x77, + 0x4c, + 0x4b, + 0x63, + 0x53, + 0x43, + 0x34, + 0x43, + 0x46, + 0x4e, + 0x46, + 0x4b, + 0x51, + 0x6d, + 0x6c, + 0x45, + 0x68, + 0x5a, + 0x5a, + 0x4a, + 0x48, + 0x63, + 0x65, + 0x6e, + 0x2f, + 0x42, + 0x30, + 0x6d, + 0x7a, + 0x72, + 0x48, + 0x6c, + 0x35, + 0x76, + 0x53, + 0x6c, + 0x59, + 0x79, + 0x37, + 0x50, + 0x37, + 0x73, + 0x0a, + 0x63, + 0x32, + 0x52, + 0x34, + 0x33, + 0x54, + 0x6c, + 0x45, + 0x41, + 0x4d, + 0x77, + 0x72, + 0x32, + 0x4a, + 0x30, + 0x4c, + 0x37, + 0x58, + 0x65, + 0x34, + 0x2b, + 0x33, + 0x45, + 0x36, + 0x31, + 0x4f, + 0x64, + 0x43, + 0x4f, + 0x30, + 0x46, + 0x37, + 0x4e, + 0x46, + 0x30, + 0x76, + 0x33, + 0x66, + 0x2f, + 0x34, + 0x5a, + 0x6f, + 0x4a, + 0x49, + 0x6d, + 0x4c, + 0x74, + 0x4e, + 0x7a, + 0x4d, + 0x4f, + 0x31, + 0x68, + 0x69, + 0x61, + 0x51, + 0x34, + 0x69, + 0x67, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char server_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x7a, + 0x43, + 0x43, + 0x41, + 0x77, + 0x4f, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x44, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x77, + 0x4d, + 0x31, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x41, + 0x7a, + 0x57, + 0x6a, + 0x42, + 0x50, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x43, + 0x57, + 0x78, + 0x76, + 0x59, + 0x32, + 0x46, + 0x73, + 0x61, + 0x47, + 0x39, + 0x7a, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x0a, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4d, + 0x74, + 0x58, + 0x71, + 0x68, + 0x69, + 0x59, + 0x68, + 0x47, + 0x6a, + 0x56, + 0x4c, + 0x77, + 0x56, + 0x75, + 0x61, + 0x79, + 0x76, + 0x4d, + 0x4d, + 0x49, + 0x72, + 0x6a, + 0x4b, + 0x37, + 0x53, + 0x34, + 0x4c, + 0x62, + 0x52, + 0x48, + 0x6c, + 0x4e, + 0x55, + 0x44, + 0x0a, + 0x74, + 0x4a, + 0x7a, + 0x46, + 0x6f, + 0x32, + 0x41, + 0x6a, + 0x75, + 0x4c, + 0x75, + 0x6d, + 0x74, + 0x65, + 0x46, + 0x2b, + 0x4b, + 0x4c, + 0x73, + 0x34, + 0x56, + 0x6a, + 0x59, + 0x7a, + 0x78, + 0x75, + 0x64, + 0x61, + 0x43, + 0x51, + 0x72, + 0x79, + 0x76, + 0x4d, + 0x61, + 0x6c, + 0x30, + 0x64, + 0x2f, + 0x38, + 0x32, + 0x38, + 0x75, + 0x6e, + 0x33, + 0x78, + 0x34, + 0x67, + 0x57, + 0x6a, + 0x59, + 0x54, + 0x4f, + 0x43, + 0x35, + 0x35, + 0x63, + 0x51, + 0x39, + 0x4d, + 0x44, + 0x4e, + 0x64, + 0x2b, + 0x0a, + 0x70, + 0x6d, + 0x4d, + 0x45, + 0x45, + 0x34, + 0x2b, + 0x64, + 0x31, + 0x75, + 0x6e, + 0x54, + 0x66, + 0x6f, + 0x39, + 0x38, + 0x38, + 0x32, + 0x6a, + 0x4d, + 0x38, + 0x65, + 0x6a, + 0x6c, + 0x4e, + 0x46, + 0x54, + 0x63, + 0x44, + 0x6f, + 0x54, + 0x50, + 0x68, + 0x6f, + 0x4e, + 0x37, + 0x77, + 0x71, + 0x66, + 0x78, + 0x4f, + 0x31, + 0x35, + 0x46, + 0x6e, + 0x69, + 0x54, + 0x77, + 0x54, + 0x43, + 0x57, + 0x39, + 0x30, + 0x62, + 0x6d, + 0x7a, + 0x37, + 0x32, + 0x74, + 0x36, + 0x7a, + 0x4c, + 0x4f, + 0x6d, + 0x0a, + 0x33, + 0x43, + 0x70, + 0x5a, + 0x75, + 0x75, + 0x64, + 0x48, + 0x53, + 0x6e, + 0x58, + 0x48, + 0x71, + 0x74, + 0x67, + 0x4f, + 0x7a, + 0x36, + 0x6e, + 0x53, + 0x53, + 0x62, + 0x54, + 0x6a, + 0x4f, + 0x31, + 0x51, + 0x52, + 0x33, + 0x4c, + 0x4e, + 0x2b, + 0x44, + 0x76, + 0x4e, + 0x30, + 0x34, + 0x62, + 0x4d, + 0x58, + 0x42, + 0x66, + 0x65, + 0x2f, + 0x30, + 0x4e, + 0x56, + 0x45, + 0x2f, + 0x51, + 0x70, + 0x65, + 0x4f, + 0x4f, + 0x47, + 0x50, + 0x5a, + 0x53, + 0x47, + 0x43, + 0x4f, + 0x74, + 0x4b, + 0x76, + 0x0a, + 0x44, + 0x5a, + 0x34, + 0x4f, + 0x75, + 0x79, + 0x4b, + 0x68, + 0x6d, + 0x32, + 0x52, + 0x6f, + 0x72, + 0x47, + 0x4a, + 0x6e, + 0x4c, + 0x6d, + 0x58, + 0x78, + 0x44, + 0x46, + 0x48, + 0x6d, + 0x42, + 0x4d, + 0x70, + 0x30, + 0x30, + 0x64, + 0x41, + 0x63, + 0x65, + 0x72, + 0x42, + 0x45, + 0x52, + 0x4c, + 0x44, + 0x6b, + 0x4c, + 0x52, + 0x70, + 0x64, + 0x57, + 0x34, + 0x38, + 0x4b, + 0x32, + 0x69, + 0x35, + 0x74, + 0x42, + 0x51, + 0x76, + 0x41, + 0x63, + 0x4a, + 0x5a, + 0x7a, + 0x6c, + 0x4c, + 0x38, + 0x51, + 0x0a, + 0x2b, + 0x48, + 0x32, + 0x66, + 0x67, + 0x75, + 0x53, + 0x62, + 0x42, + 0x32, + 0x32, + 0x65, + 0x55, + 0x79, + 0x74, + 0x42, + 0x62, + 0x62, + 0x4e, + 0x6e, + 0x67, + 0x76, + 0x36, + 0x68, + 0x79, + 0x52, + 0x79, + 0x30, + 0x62, + 0x41, + 0x49, + 0x7a, + 0x61, + 0x59, + 0x48, + 0x77, + 0x61, + 0x70, + 0x52, + 0x49, + 0x78, + 0x72, + 0x4c, + 0x54, + 0x57, + 0x6e, + 0x64, + 0x4d, + 0x69, + 0x42, + 0x37, + 0x79, + 0x4f, + 0x76, + 0x64, + 0x69, + 0x73, + 0x6c, + 0x55, + 0x2b, + 0x31, + 0x53, + 0x6f, + 0x46, + 0x0a, + 0x57, + 0x41, + 0x62, + 0x66, + 0x56, + 0x35, + 0x79, + 0x75, + 0x68, + 0x77, + 0x4f, + 0x6e, + 0x77, + 0x54, + 0x4d, + 0x53, + 0x6f, + 0x64, + 0x52, + 0x30, + 0x39, + 0x4f, + 0x78, + 0x67, + 0x55, + 0x61, + 0x75, + 0x41, + 0x32, + 0x79, + 0x72, + 0x41, + 0x4c, + 0x32, + 0x51, + 0x37, + 0x42, + 0x4d, + 0x35, + 0x47, + 0x6e, + 0x59, + 0x4c, + 0x70, + 0x55, + 0x6f, + 0x35, + 0x71, + 0x35, + 0x46, + 0x67, + 0x41, + 0x77, + 0x6b, + 0x73, + 0x4a, + 0x6c, + 0x38, + 0x77, + 0x79, + 0x53, + 0x44, + 0x70, + 0x59, + 0x0a, + 0x63, + 0x51, + 0x72, + 0x37, + 0x46, + 0x72, + 0x7a, + 0x53, + 0x6a, + 0x71, + 0x63, + 0x31, + 0x6f, + 0x39, + 0x55, + 0x73, + 0x37, + 0x68, + 0x45, + 0x45, + 0x6c, + 0x6b, + 0x76, + 0x4e, + 0x42, + 0x5a, + 0x6b, + 0x31, + 0x2b, + 0x56, + 0x50, + 0x54, + 0x61, + 0x50, + 0x35, + 0x78, + 0x45, + 0x59, + 0x79, + 0x70, + 0x63, + 0x62, + 0x67, + 0x2b, + 0x75, + 0x2b, + 0x59, + 0x43, + 0x7a, + 0x63, + 0x74, + 0x4a, + 0x57, + 0x6e, + 0x4a, + 0x4b, + 0x62, + 0x43, + 0x42, + 0x7a, + 0x32, + 0x65, + 0x73, + 0x36, + 0x0a, + 0x4b, + 0x6b, + 0x70, + 0x66, + 0x43, + 0x34, + 0x61, + 0x57, + 0x63, + 0x38, + 0x55, + 0x49, + 0x4c, + 0x6c, + 0x55, + 0x4b, + 0x2f, + 0x57, + 0x34, + 0x6a, + 0x52, + 0x46, + 0x73, + 0x4e, + 0x47, + 0x33, + 0x55, + 0x68, + 0x41, + 0x31, + 0x76, + 0x6e, + 0x49, + 0x5a, + 0x77, + 0x4b, + 0x38, + 0x36, + 0x69, + 0x71, + 0x6d, + 0x33, + 0x6b, + 0x2b, + 0x50, + 0x45, + 0x62, + 0x61, + 0x68, + 0x63, + 0x45, + 0x78, + 0x41, + 0x34, + 0x71, + 0x42, + 0x2f, + 0x4c, + 0x42, + 0x68, + 0x6f, + 0x67, + 0x76, + 0x73, + 0x0a, + 0x34, + 0x7a, + 0x39, + 0x62, + 0x32, + 0x4d, + 0x51, + 0x33, + 0x35, + 0x74, + 0x69, + 0x49, + 0x6f, + 0x51, + 0x44, + 0x6a, + 0x37, + 0x52, + 0x35, + 0x78, + 0x4e, + 0x38, + 0x63, + 0x35, + 0x41, + 0x79, + 0x77, + 0x44, + 0x79, + 0x62, + 0x38, + 0x78, + 0x47, + 0x76, + 0x32, + 0x59, + 0x6d, + 0x6b, + 0x34, + 0x6a, + 0x47, + 0x74, + 0x44, + 0x6b, + 0x64, + 0x71, + 0x72, + 0x62, + 0x74, + 0x49, + 0x55, + 0x34, + 0x32, + 0x2b, + 0x48, + 0x4d, + 0x31, + 0x79, + 0x4f, + 0x6b, + 0x38, + 0x75, + 0x38, + 0x5a, + 0x0a, + 0x45, + 0x69, + 0x78, + 0x4c, + 0x51, + 0x7a, + 0x6f, + 0x77, + 0x6d, + 0x71, + 0x55, + 0x71, + 0x72, + 0x65, + 0x42, + 0x43, + 0x68, + 0x6f, + 0x41, + 0x6f, + 0x73, + 0x63, + 0x33, + 0x46, + 0x4f, + 0x52, + 0x4f, + 0x6d, + 0x54, + 0x43, + 0x44, + 0x6c, + 0x59, + 0x36, + 0x33, + 0x34, + 0x2f, + 0x61, + 0x63, + 0x49, + 0x49, + 0x50, + 0x33, + 0x31, + 0x64, + 0x65, + 0x64, + 0x48, + 0x4e, + 0x59, + 0x5a, + 0x35, + 0x31, + 0x52, + 0x72, + 0x75, + 0x6b, + 0x41, + 0x64, + 0x41, + 0x36, + 0x32, + 0x44, + 0x62, + 0x0a, + 0x47, + 0x44, + 0x77, + 0x67, + 0x77, + 0x7a, + 0x36, + 0x54, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4b, + 0x56, + 0x76, + 0x2f, + 0x53, + 0x43, + 0x50, + 0x4f, + 0x67, + 0x77, + 0x50, + 0x41, + 0x59, + 0x30, + 0x47, + 0x34, + 0x37, + 0x79, + 0x63, + 0x76, + 0x55, + 0x48, + 0x67, + 0x0a, + 0x6b, + 0x33, + 0x44, + 0x51, + 0x67, + 0x6d, + 0x62, + 0x78, + 0x56, + 0x54, + 0x46, + 0x46, + 0x57, + 0x51, + 0x79, + 0x72, + 0x50, + 0x36, + 0x58, + 0x76, + 0x4b, + 0x48, + 0x4f, + 0x6c, + 0x33, + 0x65, + 0x5a, + 0x55, + 0x32, + 0x52, + 0x65, + 0x4a, + 0x78, + 0x65, + 0x54, + 0x78, + 0x43, + 0x4d, + 0x55, + 0x4b, + 0x63, + 0x4e, + 0x6c, + 0x57, + 0x34, + 0x2b, + 0x65, + 0x4d, + 0x32, + 0x6f, + 0x43, + 0x5a, + 0x37, + 0x57, + 0x31, + 0x76, + 0x69, + 0x74, + 0x61, + 0x75, + 0x70, + 0x6e, + 0x4f, + 0x30, + 0x0a, + 0x7a, + 0x4c, + 0x57, + 0x36, + 0x47, + 0x42, + 0x46, + 0x53, + 0x56, + 0x58, + 0x55, + 0x34, + 0x54, + 0x2b, + 0x59, + 0x5a, + 0x63, + 0x69, + 0x49, + 0x48, + 0x6e, + 0x78, + 0x6b, + 0x71, + 0x6c, + 0x48, + 0x33, + 0x58, + 0x78, + 0x63, + 0x6b, + 0x67, + 0x7a, + 0x4e, + 0x56, + 0x4c, + 0x79, + 0x57, + 0x44, + 0x41, + 0x41, + 0x4a, + 0x6f, + 0x61, + 0x77, + 0x32, + 0x63, + 0x2b, + 0x58, + 0x31, + 0x45, + 0x39, + 0x42, + 0x7a, + 0x59, + 0x62, + 0x49, + 0x54, + 0x2b, + 0x7a, + 0x4d, + 0x59, + 0x6a, + 0x5a, + 0x0a, + 0x31, + 0x62, + 0x59, + 0x41, + 0x61, + 0x76, + 0x79, + 0x51, + 0x4f, + 0x6f, + 0x4d, + 0x50, + 0x64, + 0x2f, + 0x31, + 0x7a, + 0x30, + 0x64, + 0x59, + 0x6d, + 0x49, + 0x39, + 0x46, + 0x72, + 0x77, + 0x78, + 0x5a, + 0x32, + 0x4f, + 0x42, + 0x6e, + 0x76, + 0x2b, + 0x63, + 0x58, + 0x41, + 0x72, + 0x69, + 0x30, + 0x2f, + 0x4a, + 0x4d, + 0x47, + 0x54, + 0x63, + 0x7a, + 0x58, + 0x36, + 0x66, + 0x56, + 0x67, + 0x53, + 0x45, + 0x50, + 0x47, + 0x66, + 0x48, + 0x48, + 0x63, + 0x6f, + 0x36, + 0x53, + 0x4b, + 0x4a, + 0x0a, + 0x52, + 0x5a, + 0x57, + 0x38, + 0x56, + 0x49, + 0x39, + 0x67, + 0x49, + 0x4f, + 0x52, + 0x75, + 0x57, + 0x68, + 0x79, + 0x39, + 0x76, + 0x78, + 0x55, + 0x57, + 0x41, + 0x43, + 0x4a, + 0x6f, + 0x30, + 0x4f, + 0x6b, + 0x4a, + 0x44, + 0x75, + 0x55, + 0x37, + 0x2b, + 0x57, + 0x54, + 0x61, + 0x39, + 0x65, + 0x44, + 0x66, + 0x6a, + 0x46, + 0x2b, + 0x62, + 0x62, + 0x68, + 0x2b, + 0x2f, + 0x49, + 0x73, + 0x38, + 0x36, + 0x66, + 0x72, + 0x4c, + 0x32, + 0x67, + 0x76, + 0x38, + 0x79, + 0x39, + 0x69, + 0x4d, + 0x55, + 0x0a, + 0x74, + 0x32, + 0x36, + 0x37, + 0x75, + 0x58, + 0x39, + 0x52, + 0x52, + 0x64, + 0x30, + 0x46, + 0x70, + 0x69, + 0x4b, + 0x70, + 0x2b, + 0x70, + 0x4b, + 0x4d, + 0x48, + 0x6d, + 0x50, + 0x77, + 0x47, + 0x33, + 0x70, + 0x75, + 0x6b, + 0x78, + 0x57, + 0x64, + 0x38, + 0x76, + 0x6b, + 0x4f, + 0x72, + 0x67, + 0x6a, + 0x4a, + 0x44, + 0x46, + 0x2b, + 0x54, + 0x56, + 0x48, + 0x67, + 0x67, + 0x4c, + 0x31, + 0x4a, + 0x59, + 0x58, + 0x5a, + 0x37, + 0x45, + 0x4a, + 0x39, + 0x79, + 0x6a, + 0x50, + 0x64, + 0x77, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +#ifndef BSC_NETWORK_IFACE +#define BSC_NETWORK_IFACE "127.0.0.1" +#endif + +#ifndef BSC_DATALINK_HUB_IFACE +#define BSC_DATALINK_HUB_IFACE "127.0.0.1:40001" +#endif + +#ifndef BSC_DATALINK_DIRECT_IFACE +#define BSC_DATALINK_DIRECT_IFACE "127.0.0.1:40000" +#endif + +#define BACNET_CLOSED_PORT 65500 +#define BACNET_HUB_PORT 39011 +#define BACNET_LOCALHOST "127.0.0.1" +#define BACNET_WEBSOCKET_SERVER_PORT 39012 +#define BACNET_WEBSOCKET_SERVER_PORT2 39013 +#define BACNET_WEBSOCKET_SERVER_ADDR "127.0.0.1" +#define BACNET_TIMEOUT 5 +#define MAX_BVLC_LEN 1500 +#define MAX_NDPU_LEN 1500 +#define MAX_SERVER_SOCKETS BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM +#define MAX_CLIENT_SOCKETS BSC_CONF_CLIENT_CONNECTIONS_NUM +#define SC_DATALINK_INSTANCE 1 + +// MbedTLS expects a key in DER format +#ifdef CONFIG_MBEDTLS +#define CLIENT_KEY client_key_der +#else +#define CLIENT_KEY client_key +#endif + +#ifdef ZEPHYR_PLATFORM +#define FILE_PREFIX "/lfs/" +bool init_zephyr_env(void); +#else +#define FILE_PREFIX "./" +#endif + +/* Netport constants */ +#define SC_NETPORT_DIRECT_SERVER_PORT 40000 +#ifdef ZEPHYR_PLATFORM +#define SC_NETPORT_DIRECT_SERVER_IFNAME "zeth" +#define SC_NETPORT_DIRECT_SERVER_BINDING "zeth:40000" +#else +#define SC_NETPORT_DIRECT_SERVER_IFNAME "eth0" +#define SC_NETPORT_DIRECT_SERVER_BINDING "eth0:40000" +#endif +#define SC_NETPORT_DIRECT_CONNECT_INITIAT true +#define SC_NETPORT_DIRECT_CONNECT_ACCERT true +#define SC_NETPORT_HUB_SERVER_PORT 40001 +#define SC_NETPORT_HUB_SERVER_BINDING "40001" +#define SC_NETPORT_HUB_FUNCTION_ENABLE true +#define SC_NETPORT_BACFILE_START_INDEX 0 + +#define SC_NETPORT_DIRECT_CONNECT_ACCERT_URIS \ + "SC_Direct_Connect_Accept_URI1 SC_Direct_Connect_Accept_URI2" +#define WAIT_EVENT_MS 500 + +#ifdef CONFIG_MBEDTLS +#define ZERO_BYTE 1 +#else +#define ZERO_BYTE 0 +#endif + +typedef struct { + BSC_NODE_EVENT ev; + BSC_NODE *node; + uint8_t pdu[MAX_BVLC_LEN]; + size_t pdu_len; + BSC_EVENT *e; + BACNET_SC_VMAC_ADDRESS dest; +} node_ev_t; + +static void call_maintenance_timer(bool reset, int time_passed_ms) +{ + static int total_ms; + if (reset) { + total_ms = 0; + } + + total_ms += time_passed_ms; + + if (total_ms >= 1000) { + bsc_maintenance_timer(1); + total_ms = 0; + } +} + +static void wait_sec(int seconds) +{ + while (seconds >= 0) { + bsc_wait(1); + bsc_maintenance_timer(1); + seconds--; + } +} + +static node_ev_t node_ev2; +static node_ev_t node_ev3; +static node_ev_t node_ev4; + +static void init_node_ev(node_ev_t *ev) +{ + memset(ev, 0, sizeof(*ev)); + ev->e = bsc_event_init(); + zassert_not_equal(ev->e, NULL, 0); +} + +static void deinit_node_ev(node_ev_t *ev) +{ + bsc_event_deinit(ev->e); +} + +static void datalink_wait_for_connection_to_hub(node_ev_t *ev) +{ + BACNET_SC_HUB_CONNECTOR_STATE st; + call_maintenance_timer(1, 0); + while (1) { + bsc_event_timedwait(ev->e, WAIT_EVENT_MS); + call_maintenance_timer(0, WAIT_EVENT_MS); + st = Network_Port_SC_Hub_Connector_State(SC_DATALINK_INSTANCE); + + if (st == BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY || + st == BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER) { + break; + } + } +} + +static size_t +datalink_wait_for_data(BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu) +{ + size_t len; + call_maintenance_timer(1, 0); + while (1) { + len = bsc_receive(src, pdu, max_pdu, WAIT_EVENT_MS); + if (len != 0) { + return len; + } + call_maintenance_timer(0, WAIT_EVENT_MS); + } +} + +static void wait_for_connection_to_hub(node_ev_t *ev, BSC_NODE *node) +{ + BACNET_SC_HUB_CONNECTION_STATUS *st1; + BACNET_SC_HUB_CONNECTION_STATUS *st2; + call_maintenance_timer(1, 0); + while (1) { + bsc_event_timedwait(ev->e, WAIT_EVENT_MS); + call_maintenance_timer(0, WAIT_EVENT_MS); + + st1 = bsc_node_hub_connector_status(node, true); + st2 = bsc_node_hub_connector_status(node, false); + if (st1 && st1->State == BACNET_SC_CONNECTION_STATE_CONNECTED) { + break; + } + if (st2 && st2->State == BACNET_SC_CONNECTION_STATE_CONNECTED) { + break; + } + } +} + +static void wait_for_failed_request( + node_ev_t *ev, + BACNET_SC_VMAC_ADDRESS *vmac, + BACNET_SC_UUID *uuid, + BACNET_ERROR_CODE err) +{ + BACNET_SC_FAILED_CONNECTION_REQUEST *r; + int j = 0; + int i = 0; + call_maintenance_timer(1, 0); + while (1) { + bsc_event_timedwait(ev->e, WAIT_EVENT_MS); + call_maintenance_timer(0, WAIT_EVENT_MS); + for (i = 0; i < Network_Port_SC_Failed_Connection_Requests_Count( + Network_Port_Index_To_Instance(0)); + i++) { + r = Network_Port_SC_Failed_Connection_Requests_Get( + Network_Port_Index_To_Instance(0), i); + if (r) { + if ((memcmp( + r->Peer_VMAC, &vmac->address[0], BVLC_SC_VMAC_SIZE) == + 0) && + (memcmp( + r[i].Peer_UUID.uuid.uuid128, &uuid->uuid[0], + BVLC_SC_UUID_SIZE) == 0) && + r->Error == err) { + return; + } + } + j++; + if (j > 10) { + return; + } + } + } +} + +static bool wait_node_ev(node_ev_t *ev, BSC_NODE_EVENT wait_ev, BSC_NODE *node) +{ + call_maintenance_timer(1, 0); + while (!bsc_event_timedwait(ev->e, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + bws_dispatch_lock(); + if (ev->ev == wait_ev && ev->node == node) { + debug_printf("got event %d\n", ev->ev); + ev->ev = -1; + // reset event if it was signalled while we were blocked in call + // bws_dispatch_lock(). (in that case ev->ev contains code of last + // event.) that's tricky but that allows to avoid using mutexes which + // are platform specific in test code + bsc_event_timedwait(ev->e, 1); + bws_dispatch_unlock(); + return true; + } else { + bws_dispatch_unlock(); + return false; + } +} + +static void +wait_specific_node_ev(node_ev_t *ev, BSC_NODE_EVENT wait_ev, BSC_NODE *node) +{ + call_maintenance_timer(1, 0); + while (1) { + while (!bsc_event_timedwait(ev->e, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + bws_dispatch_lock(); + if (ev->ev == wait_ev && ev->node == node) { + debug_printf("got event %d\n", ev->ev); + ev->ev = -1; + // reset event if it was signalled while we were blocked in call + // bws_dispatch_lock(). (in that case ev->ev contains code of last + // event.) that's tricky but that allows to avoid using mutexes + // which are platform specific in test code + bsc_event_timedwait(ev->e, 1); + bws_dispatch_unlock(); + break; + } else { + bws_dispatch_unlock(); + } + } +} + +static void signal_node_ev( + node_ev_t *e, + BSC_NODE_EVENT ev, + BSC_NODE *node, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + e->ev = ev; + e->node = node; + + zassert_equal(true, pdu_len <= MAX_BVLC_LEN, NULL); + + if (pdu) { + memcpy(e->pdu, pdu, pdu_len); + e->pdu_len = pdu_len; + } + + if (dest) { + debug_printf("dest = %s\n", bsc_vmac_to_string(dest)); + memcpy(&e->dest.address[0], &dest->address[0], sizeof(dest->address)); + debug_printf("e->dest = %s\n", bsc_vmac_to_string(&e->dest)); + } + + bsc_event_signal(e->e); +} + +static void node_event( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + (void)node; + (void)ev; + (void)dest; + (void)pdu; + (void)pdu_len; +} + +static void node_event2( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + bws_dispatch_lock(); + debug_printf("node_event2() ev = %d event = %p\n", ev, node_ev2.e); + signal_node_ev(&node_ev2, ev, node, dest, pdu, pdu_len); + bws_dispatch_unlock(); +} + +static void node_event3( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + bws_dispatch_lock(); + debug_printf("node_event3() ev = %d event = %p\n", ev, node_ev3.e); + signal_node_ev(&node_ev3, ev, node, dest, pdu, pdu_len); + bws_dispatch_unlock(); +} + +static void node_event4( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + bws_dispatch_lock(); + debug_printf("node_event4() ev = %d event = %p\n", ev, node_ev4.e); + signal_node_ev(&node_ev4, ev, node, dest, pdu, pdu_len); + bws_dispatch_unlock(); +} + +static void netport_object_init( + uint32_t instance, + uint8_t *ca_cert_chain, + size_t ca_cert_chain_size, + uint8_t *cert_chain, + size_t cert_chain_size, + uint8_t *key, + size_t key_size, + char *hub_binding, + char *direct_binding, + BACNET_SC_UUID *local_uuid, + BACNET_SC_VMAC_ADDRESS *local_vmac, + char *primaryURL, + char *failoverURL, + bool direct_connect_initiate_enable, + bool direct_connect_accept_enable, + bool hub_function_enabled) +{ + const char *filename_ca_cert = FILE_PREFIX "ca_cert.pem"; + const char *filename_cert = FILE_PREFIX "cert.pem"; + const char *filename_key = FILE_PREFIX "key.pem"; + + Network_Port_Init(); + Network_Port_Object_Instance_Number_Set(0, instance); + Network_Port_Type_Set(instance, PORT_TYPE_BSC); + + bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE); + bacfile_pathname_set( + BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE, filename_ca_cert); + bacfile_write( + BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE, ca_cert_chain, + (uint32_t)ca_cert_chain_size); + Network_Port_Issuer_Certificate_File_Set( + instance, 0, BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE); + + bacfile_create(BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE); + bacfile_pathname_set( + BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE, filename_cert); + bacfile_write( + BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE, cert_chain, + (uint32_t)cert_chain_size); + Network_Port_Operational_Certificate_File_Set( + instance, BSC_OPERATIONAL_CERTIFICATE_FILE_INSTANCE); + + bacfile_create(BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE); + bacfile_pathname_set( + BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE, filename_key); + bacfile_write( + BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE, key, (uint32_t)key_size); + Network_Port_Certificate_Key_File_Set( + instance, BSC_CERTIFICATE_SIGNING_REQUEST_FILE_INSTANCE); + + Network_Port_SC_Local_UUID_Set(instance, (BACNET_UUID *)local_uuid); + + Network_Port_MAC_Address_Set( + instance, local_vmac->address, sizeof(*local_vmac)); + + Network_Port_SC_Primary_Hub_URI_Set(instance, primaryURL); + Network_Port_SC_Failover_Hub_URI_Set(instance, failoverURL); + + /* common NP data */ + Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED); + Network_Port_Out_Of_Service_Set(instance, false); + Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN); + Network_Port_APDU_Length_Set(instance, MAX_APDU); + Network_Port_Network_Number_Set(instance, 0); + + /* SC parameters */ + Network_Port_Max_BVLC_Length_Accepted_Set(instance, SC_NETPORT_BVLC_MAX); + Network_Port_Max_NPDU_Length_Accepted_Set(instance, SC_NETPORT_NPDU_MAX); + Network_Port_SC_Connect_Wait_Timeout_Set( + instance, SC_NETPORT_CONNECT_TIMEOUT); + Network_Port_SC_Heartbeat_Timeout_Set( + instance, SC_NETPORT_HEARTBEAT_TIMEOUT); + Network_Port_SC_Disconnect_Wait_Timeout_Set( + instance, SC_NETPORT_DISCONNECT_TIMEOUT); + Network_Port_SC_Maximum_Reconnect_Time_Set( + instance, SC_NETPORT_RECONNECT_TIME); + +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + Network_Port_SC_Direct_Connect_Binding_Set(instance, direct_binding); + Network_Port_SC_Direct_Connect_Initiate_Enable_Set( + instance, direct_connect_initiate_enable); + Network_Port_SC_Direct_Connect_Accept_Enable_Set( + instance, direct_connect_accept_enable); +#endif +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + Network_Port_SC_Hub_Function_Binding_Set(instance, hub_binding); + Network_Port_SC_Hub_Function_Enable_Set(instance, hub_function_enabled); +#endif + +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + Network_Port_SC_Direct_Connect_Accept_URIs_Set( + instance, SC_NETPORT_DIRECT_CONNECT_ACCERT_URIS); +#endif +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(test_datalink_1, test_sc_parameters) +#else +static void test_sc_parameters(void) +#endif +{ + BACNET_SC_UUID hubf_uuid; + BACNET_SC_VMAC_ADDRESS hubf_vmac; + char primary_url[128]; + char secondary_url[128]; + BSC_NODE_CONF bsc_conf; + +#ifdef ZEPHYR_PLATFORM + init_zephyr_env(); +#endif + + memset(&bsc_conf, 0, sizeof(bsc_conf)); + memset(&hubf_uuid, 0x1, sizeof(hubf_uuid)); + memset(&hubf_vmac, 0x2, sizeof(hubf_vmac)); + + sprintf( + primary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT2); + + // prepare + bacfile_init(); + netport_object_init( + SC_DATALINK_INSTANCE, ca_cert, sizeof(ca_cert), server_cert, + sizeof(server_cert), server_key, sizeof(server_key), + SC_NETPORT_HUB_SERVER_BINDING, SC_NETPORT_DIRECT_SERVER_BINDING, + &hubf_uuid, &hubf_vmac, primary_url, secondary_url, + SC_NETPORT_DIRECT_CONNECT_INITIAT, SC_NETPORT_DIRECT_CONNECT_ACCERT, + SC_NETPORT_HUB_FUNCTION_ENABLE); + + // fill + bsc_node_conf_fill_from_netport(&bsc_conf, &node_event); + + // check + zassert_equal( + bsc_conf.ca_cert_chain_size, sizeof(ca_cert) + ZERO_BYTE, NULL); + zassert_mem_equal(bsc_conf.ca_cert_chain, ca_cert, sizeof(ca_cert), NULL); + zassert_equal( + bsc_conf.cert_chain_size, sizeof(server_cert) + ZERO_BYTE, NULL); + zassert_mem_equal( + bsc_conf.cert_chain, server_cert, sizeof(server_cert), NULL); + zassert_equal(bsc_conf.key_size, sizeof(server_key) + ZERO_BYTE, NULL); + zassert_mem_equal(bsc_conf.key, server_key, sizeof(server_key), NULL); + zassert_mem_equal(bsc_conf.local_uuid, &hubf_uuid, sizeof(hubf_uuid), NULL); + zassert_mem_equal( + &bsc_conf.local_vmac, &hubf_vmac, sizeof(hubf_vmac), NULL); + zassert_equal(bsc_conf.max_local_bvlc_len, SC_NETPORT_BVLC_MAX, NULL); + zassert_equal(bsc_conf.max_local_npdu_len, SC_NETPORT_NPDU_MAX, NULL); + zassert_equal(bsc_conf.connect_timeout_s, SC_NETPORT_CONNECT_TIMEOUT, NULL); + zassert_equal( + bsc_conf.heartbeat_timeout_s, SC_NETPORT_HEARTBEAT_TIMEOUT, NULL); + zassert_equal( + bsc_conf.disconnect_timeout_s, SC_NETPORT_DISCONNECT_TIMEOUT, NULL); + zassert_equal( + bsc_conf.reconnnect_timeout_s, SC_NETPORT_RECONNECT_TIME, NULL); + zassert_equal( + bsc_conf.address_resolution_timeout_s, SC_NETPORT_CONNECT_TIMEOUT, + NULL); + zassert_equal( + bsc_conf.address_resolution_freshness_timeout_s, + SC_NETPORT_CONNECT_TIMEOUT, NULL); + zassert_equal(strcmp(bsc_conf.primaryURL, primary_url), 0, NULL); + zassert_equal(strcmp(bsc_conf.failoverURL, secondary_url), 0, NULL); + +#if BSC_CONF_HUB_FUNCTIONS_NUM != 0 + zassert_equal(bsc_conf.hub_server_port, SC_NETPORT_HUB_SERVER_PORT, NULL); + zassert_equal( + bsc_conf.hub_function_enabled, SC_NETPORT_HUB_FUNCTION_ENABLE, NULL); + zassert_is_null(bsc_conf.hub_iface, NULL); +#endif + +#if BSC_CONF_HUB_CONNECTORS_NUM != 0 + zassert_equal( + bsc_conf.direct_server_port, SC_NETPORT_DIRECT_SERVER_PORT, NULL); + zassert_equal( + strcmp(bsc_conf.direct_iface, SC_NETPORT_DIRECT_SERVER_IFNAME), 0, + NULL); + zassert_equal( + bsc_conf.direct_connect_initiate_enable, + SC_NETPORT_DIRECT_CONNECT_INITIAT, NULL); + zassert_equal( + bsc_conf.direct_connect_accept_enable, SC_NETPORT_DIRECT_CONNECT_ACCERT, + NULL); + + zassert_mem_equal( + bsc_conf.direct_connection_accept_uris, + SC_NETPORT_DIRECT_CONNECT_ACCERT_URIS, + strlen(SC_NETPORT_DIRECT_CONNECT_ACCERT_URIS), NULL); + zassert_equal( + bsc_conf.direct_connection_accept_uris_len, + strlen(SC_NETPORT_DIRECT_CONNECT_ACCERT_URIS), NULL); +#endif + + zassert_equal(bsc_conf.event_func, &node_event, NULL); + + bsc_node_conf_cleanup(&bsc_conf); + Network_Port_Cleanup(); + bacfile_cleanup(); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(test_datalink_2, test_sc_datalink) +#else +static void test_sc_datalink(void) +#endif +{ + BSC_NODE_CONF conf2; + BSC_NODE_CONF conf3; + BACNET_SC_UUID uuid1; + BACNET_SC_VMAC_ADDRESS vmac1; + BACNET_SC_UUID uuid2; + BACNET_SC_VMAC_ADDRESS vmac2; + BACNET_SC_UUID uuid3; + BACNET_SC_VMAC_ADDRESS vmac3; + char primary_url1[128]; + char secondary_url1[128]; + char primary_url2[128]; + char secondary_url2[128]; + char primary_url3[128]; + char secondary_url3[128]; + char direct_url[128]; + char *direct_urls[1] = { &direct_url[0] }; + BSC_SC_RET ret; + BSC_NODE *node2; + BSC_NODE *node3; + uint8_t buf[2 * BVLC_SC_NPDU_SIZE_CONF]; + size_t len; + size_t sent; + uint8_t npdu[128]; + BACNET_ADDRESS from; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc; + BACNET_ADDRESS dest; + uint8_t big_npdu[BVLC_SC_NPDU_SIZE_CONF + 100]; + BACNET_ADDRESS test; + uint8_t broadcast[BVLC_SC_VMAC_SIZE]; + +#ifdef ZEPHYR_PLATFORM + init_zephyr_env(); +#endif + + bsc_generate_random_uuid(&uuid1); + memset(&vmac1, 0x32, sizeof(vmac1)); + memset(&uuid2, 0x33, sizeof(uuid2)); + memset(&vmac2, 0x34, sizeof(vmac2)); + memset(&uuid3, 0x35, sizeof(uuid3)); + memset(&vmac3, 0x36, sizeof(vmac3)); + + sprintf(primary_url3, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + secondary_url3, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf(primary_url2, "wss://%s:%d", BACNET_LOCALHOST, BACNET_HUB_PORT); + sprintf(secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, BACNET_HUB_PORT); + sprintf(primary_url1, "wss://%s:%d", BACNET_LOCALHOST, BACNET_HUB_PORT); + sprintf(secondary_url1, "wss://%s:%d", BACNET_LOCALHOST, BACNET_HUB_PORT); + sprintf( + direct_url, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_DIRECT_SERVER_PORT); + + bacfile_init(); + netport_object_init( + SC_DATALINK_INSTANCE, ca_cert, sizeof(ca_cert), server_cert, + sizeof(server_cert), server_key, sizeof(server_key), + BSC_DATALINK_HUB_IFACE, BSC_DATALINK_DIRECT_IFACE, &uuid1, &vmac1, + primary_url1, secondary_url1, true, false, false); + + init_node_ev(&node_ev2); + init_node_ev(&node_ev3); + zassert_equal(bsc_cert_files_check(), true, NULL); + zassert_equal(bsc_init(NULL), true, NULL); + zassert_equal(bsc_init(NULL), false, NULL); + memset(broadcast, 0xFF, sizeof(broadcast)); + bsc_get_broadcast_address(&test); + zassert_equal( + memcmp(&test.mac[0], broadcast, BVLC_SC_VMAC_SIZE), false, NULL); + zassert_equal(test.len == 0, true, NULL); + zassert_equal(test.mac_len == BVLC_SC_VMAC_SIZE, true, NULL); + bsc_get_my_address(&test); + zassert_equal( + memcmp(&test.mac[0], &vmac1.address[0], BVLC_SC_VMAC_SIZE), false, + NULL); + zassert_equal( + bsc_direct_connection_established(&vmac3, NULL, 0) == false, true, + NULL); + + conf2.ca_cert_chain = ca_cert; + conf2.ca_cert_chain_size = sizeof(ca_cert); + conf2.cert_chain = client_cert; + conf2.cert_chain_size = sizeof(client_cert); + conf2.key = CLIENT_KEY; + conf2.key_size = sizeof(CLIENT_KEY); + conf2.local_uuid = &uuid2; + conf2.local_vmac = vmac2; + conf2.max_local_bvlc_len = MAX_BVLC_LEN; + conf2.max_local_npdu_len = MAX_NDPU_LEN; + conf2.connect_timeout_s = BACNET_TIMEOUT; + conf2.heartbeat_timeout_s = BACNET_TIMEOUT; + conf2.disconnect_timeout_s = BACNET_TIMEOUT; + conf2.reconnnect_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf2.primaryURL = primary_url2; + conf2.failoverURL = secondary_url2; + conf2.hub_server_port = 0; + conf2.direct_server_port = 0; + conf2.direct_iface = BSC_NETWORK_IFACE; + conf2.hub_iface = BSC_NETWORK_IFACE; + conf2.direct_connect_accept_enable = false; + conf2.direct_connect_initiate_enable = false; + conf2.hub_function_enabled = false; + conf2.direct_connection_accept_uris = NULL; + conf2.direct_connection_accept_uris_len = 0; + conf2.event_func = node_event2; + + conf3.ca_cert_chain = ca_cert; + conf3.ca_cert_chain_size = sizeof(ca_cert); + conf3.cert_chain = client_cert; + conf3.cert_chain_size = sizeof(client_cert); + conf3.key = client_key; + conf3.key_size = sizeof(client_key); + conf3.local_uuid = &uuid3; + conf3.local_vmac = vmac3; + conf3.max_local_bvlc_len = MAX_BVLC_LEN; + conf3.max_local_npdu_len = MAX_NDPU_LEN; + conf3.connect_timeout_s = BACNET_TIMEOUT; + conf3.heartbeat_timeout_s = BACNET_TIMEOUT; + conf3.disconnect_timeout_s = BACNET_TIMEOUT; + conf3.reconnnect_timeout_s = BACNET_TIMEOUT; + conf3.address_resolution_timeout_s = BACNET_TIMEOUT; + conf3.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf3.primaryURL = primary_url3; + conf3.failoverURL = secondary_url3; + conf3.hub_server_port = BACNET_HUB_PORT; + conf3.direct_server_port = SC_NETPORT_DIRECT_SERVER_PORT; + conf3.direct_iface = BSC_NETWORK_IFACE; + conf3.hub_iface = BSC_NETWORK_IFACE; + conf3.direct_connect_accept_enable = true; + conf3.direct_connect_initiate_enable = false; + conf3.hub_function_enabled = true; + conf3.direct_connection_accept_uris = NULL; + conf3.direct_connection_accept_uris_len = 0; + conf3.event_func = node_event3; + +#ifdef ZEPHYR_PLATFORM + init_zephyr_env(); +#endif + + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + wait_for_connection_to_hub(&node_ev2, node2); + datalink_wait_for_connection_to_hub(&node_ev2); + // send encapsulated npdu packet + memset(npdu, 0x99, sizeof(npdu)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 111, NULL, &vmac1, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node2, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + memset(buf, 0, sizeof(buf)); + len = datalink_wait_for_data(&from, buf, sizeof(buf)); + zassert_equal(len, sizeof(npdu), NULL); + ret = memcmp(npdu, buf, len); + zassert_equal(ret, 0, NULL); + // send encapsulated npdu packet + len = 55; + memset(npdu, 0x22, len); + dest.mac_len = BVLC_SC_VMAC_SIZE; + dest.net = 0; + memcpy(&dest.mac[0], &vmac2.address[0], BVLC_SC_VMAC_SIZE); + sent = bsc_send_pdu(&dest, NULL, npdu, (unsigned int)len); + zassert_equal(sent == len, true, NULL); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal(len, message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, len); + zassert_equal(ret, 0, NULL); + // let's try broadcast + dest.mac_len = 0; + dest.net = BACNET_BROADCAST_NETWORK; + len = 105; + memset(npdu, 0x52, len); + dest.mac_len = BVLC_SC_VMAC_SIZE; + sent = bsc_send_pdu(&dest, NULL, npdu, (unsigned int)len); + zassert_equal(sent == len, true, NULL); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal(len, message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, len); + zassert_equal(ret, 0, NULL); + // big packet test + memset(big_npdu, 0x88, sizeof(big_npdu)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 12, NULL, &vmac1, big_npdu, sizeof(big_npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node2, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + memset(npdu, 0x11, sizeof(npdu)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 13, NULL, &vmac1, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node2, buf, len); + memset(buf, 0, sizeof(buf)); + len = datalink_wait_for_data(&from, buf, sizeof(buf)); + zassert_equal(len == sizeof(npdu), true, 0); + ret = memcmp(npdu, buf, len); + zassert_equal(ret, 0, NULL); + + ret = bsc_connect_direct(NULL, direct_urls, 1); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + wait_sec(1); + zassert_equal( + bsc_direct_connection_established(&vmac3, NULL, 0) == true, true, NULL); + zassert_equal( + bsc_direct_connection_established(NULL, direct_urls, 1) == true, true, + NULL); + bsc_disconnect_direct(&vmac3); + wait_sec(1); + zassert_equal( + bsc_direct_connection_established(&vmac3, NULL, 0) == false, true, + NULL); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + bsc_cleanup(); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev2); + deinit_node_ev(&node_ev3); + + Network_Port_Cleanup(); + bacfile_cleanup(); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(test_datalink_3, test_sc_datalink_properties) +#else +static void test_sc_datalink_properties(void) +#endif +{ + BSC_NODE_CONF conf2; + BSC_NODE_CONF conf3; + BSC_NODE_CONF conf4; + BACNET_SC_UUID uuid1; + BACNET_SC_VMAC_ADDRESS vmac1; + BACNET_SC_UUID uuid2; + BACNET_SC_VMAC_ADDRESS vmac2; + BACNET_SC_UUID uuid3; + BACNET_SC_VMAC_ADDRESS vmac3; + BACNET_SC_UUID uuid4; + BACNET_SC_VMAC_ADDRESS vmac4; + char primary_url1[128]; + char secondary_url1[128]; + char primary_url2[128]; + char secondary_url2[128]; + char primary_url3[128]; + char secondary_url3[128]; + char direct_url[128]; + char *direct_urls[1] = { &direct_url[0] }; + BSC_SC_RET ret; + BSC_NODE *node2; + BSC_NODE *node3; + BSC_NODE *node4; + BACNET_SC_DIRECT_CONNECTION_STATUS *sd; + BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *sh = NULL; + +#ifdef ZEPHYR_PLATFORM + init_zephyr_env(); +#endif + + memset(&uuid1, 0x1, sizeof(uuid1)); + memset(&vmac1, 0x2, sizeof(vmac1)); + memset(&uuid2, 0x3, sizeof(uuid2)); + memset(&vmac2, 0x4, sizeof(vmac2)); + memset(&uuid3, 0x5, sizeof(uuid3)); + memset(&vmac3, 0x6, sizeof(vmac3)); + memset(&uuid4, 0x7, sizeof(uuid4)); + memset(&vmac4, 0x8, sizeof(vmac4)); + + sprintf( + primary_url3, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_HUB_SERVER_PORT); + sprintf( + secondary_url3, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_HUB_SERVER_PORT); + sprintf( + primary_url2, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_HUB_SERVER_PORT); + sprintf( + secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_HUB_SERVER_PORT); + sprintf( + primary_url1, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_HUB_SERVER_PORT); + sprintf( + secondary_url1, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_HUB_SERVER_PORT); + sprintf( + direct_url, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_DIRECT_SERVER_PORT); + + bacfile_init(); + netport_object_init( + SC_DATALINK_INSTANCE, ca_cert, sizeof(ca_cert), server_cert, + sizeof(server_cert), server_key, sizeof(server_key), + BSC_DATALINK_HUB_IFACE, BSC_DATALINK_DIRECT_IFACE, &uuid1, &vmac1, + primary_url1, secondary_url1, true, true, true); + + init_node_ev(&node_ev2); + init_node_ev(&node_ev3); + init_node_ev(&node_ev4); + zassert_equal(bsc_cert_files_check(), true, NULL); + zassert_equal(bsc_init(NULL), true, NULL); + + conf2.ca_cert_chain = ca_cert; + conf2.ca_cert_chain_size = sizeof(ca_cert); + conf2.cert_chain = client_cert; + conf2.cert_chain_size = sizeof(client_cert); + conf2.key = CLIENT_KEY; + conf2.key_size = sizeof(CLIENT_KEY); + conf2.local_uuid = &uuid2; + conf2.local_vmac = vmac2; + conf2.max_local_bvlc_len = MAX_BVLC_LEN; + conf2.max_local_npdu_len = MAX_NDPU_LEN; + conf2.connect_timeout_s = BACNET_TIMEOUT; + conf2.heartbeat_timeout_s = BACNET_TIMEOUT; + conf2.disconnect_timeout_s = BACNET_TIMEOUT; + conf2.reconnnect_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf2.primaryURL = primary_url2; + conf2.failoverURL = secondary_url2; + conf2.hub_server_port = 0; + conf2.direct_server_port = 0; + conf2.direct_iface = BSC_NETWORK_IFACE; + conf2.hub_iface = BSC_NETWORK_IFACE; + conf2.direct_connect_accept_enable = false; + conf2.direct_connect_initiate_enable = true; + conf2.hub_function_enabled = false; + conf2.direct_connection_accept_uris = NULL; + conf2.direct_connection_accept_uris_len = 0; + conf2.event_func = node_event2; + + conf3.ca_cert_chain = ca_cert; + conf3.ca_cert_chain_size = sizeof(ca_cert); + conf3.cert_chain = client_cert; + conf3.cert_chain_size = sizeof(client_cert); + conf3.key = CLIENT_KEY; + conf3.key_size = sizeof(CLIENT_KEY); + conf3.local_uuid = &uuid3; + conf3.local_vmac = vmac3; + conf3.max_local_bvlc_len = MAX_BVLC_LEN; + conf3.max_local_npdu_len = MAX_NDPU_LEN; + conf3.connect_timeout_s = BACNET_TIMEOUT; + conf3.heartbeat_timeout_s = BACNET_TIMEOUT; + conf3.disconnect_timeout_s = BACNET_TIMEOUT; + conf3.reconnnect_timeout_s = BACNET_TIMEOUT; + conf3.address_resolution_timeout_s = BACNET_TIMEOUT; + conf3.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf3.primaryURL = primary_url3; + conf3.failoverURL = secondary_url3; + conf3.hub_server_port = BACNET_HUB_PORT; + conf3.direct_server_port = SC_NETPORT_DIRECT_SERVER_PORT; + conf3.direct_iface = BSC_NETWORK_IFACE; + conf3.hub_iface = BSC_NETWORK_IFACE; + conf3.direct_connect_accept_enable = false; + conf3.direct_connect_initiate_enable = true; + conf3.hub_function_enabled = false; + conf3.direct_connection_accept_uris = NULL; + conf3.direct_connection_accept_uris_len = 0; + conf3.event_func = node_event3; + conf4 = conf3; + conf4.local_uuid = &uuid4; + conf4.local_vmac = vmac4; + conf4.event_func = node_event4; + + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf4, &node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + wait_for_connection_to_hub(&node_ev2, node2); + wait_for_connection_to_hub(&node_ev3, node3); + + ret = bsc_node_start(node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev4, BSC_NODE_EVENT_STARTED, node4), true, 0); + + ret = bsc_node_connect_direct(node2, NULL, direct_urls, 1); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_DIRECT_CONNECTED, node2); + zassert_equal( + bsc_direct_connection_established(&vmac2, NULL, 0) == true, true, NULL); + bsc_maintenance_timer(0); + zassert_equal( + Network_Port_SC_Direct_Connect_Connection_Status_Count( + Network_Port_Index_To_Instance(0)) == 1, + true, NULL); + sd = Network_Port_SC_Direct_Connect_Connection_Status_Get( + Network_Port_Index_To_Instance(0), 0); + zassert_equal(sd != NULL, true, NULL); + zassert_equal( + sd->State == BACNET_SC_CONNECTION_STATE_CONNECTED, true, NULL); + zassert_equal( + memcmp(sd->Peer_VMAC, &vmac2.address[0], BVLC_SC_VMAC_SIZE) == 0, true, + NULL); + bsc_node_disconnect_direct(node2, &vmac1); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node2); + sd = Network_Port_SC_Direct_Connect_Connection_Status_Get( + Network_Port_Index_To_Instance(0), 1); + while (sd && sd->State == BACNET_SC_CONNECTION_STATE_CONNECTED) { + wait_sec(1); + sd = Network_Port_SC_Direct_Connect_Connection_Status_Get( + Network_Port_Index_To_Instance(0), 1); + } + zassert_equal( + Network_Port_SC_Direct_Connect_Connection_Status_Count( + Network_Port_Index_To_Instance(0)) == 1, + true, NULL); + zassert_equal( + bsc_direct_connection_established(&vmac2, NULL, 0) == false, true, + NULL); + sd = Network_Port_SC_Direct_Connect_Connection_Status_Get( + Network_Port_Index_To_Instance(0), 0); + zassert_equal(sd != NULL, true, NULL); + zassert_equal( + sd->State == BACNET_SC_CONNECTION_STATE_CONNECTED, true, NULL); + zassert_equal( + memcmp(sd->Peer_VMAC, &vmac2.address[0], BVLC_SC_VMAC_SIZE) == 0, true, + NULL); + zassert_equal( + memcmp(sd->Peer_UUID.uuid.uuid128, &uuid2.uuid[0], BVLC_SC_UUID_SIZE) == + 0, + true, NULL); + zassert_equal( + Network_Port_SC_Hub_Function_Connection_Status_Count( + Network_Port_Index_To_Instance(0)) == 2, + true, NULL); + sh = Network_Port_SC_Hub_Function_Connection_Status_Get( + Network_Port_Index_To_Instance(0), 1); + zassert_equal(sh != NULL, true, 0); + ret = bsc_node_connect_direct(node2, NULL, direct_urls, 1); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_DIRECT_CONNECTED, node2); + zassert_equal( + bsc_direct_connection_established(&vmac2, NULL, 0) == true, true, NULL); + bsc_maintenance_timer(0); + ret = bsc_node_connect_direct(node3, NULL, direct_urls, 1); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3); + zassert_equal( + bsc_direct_connection_established(&vmac3, NULL, 0) == true, true, NULL); + bsc_maintenance_timer(0); + ret = bsc_node_connect_direct(node4, NULL, direct_urls, 1); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + wait_specific_node_ev(&node_ev4, BSC_NODE_EVENT_DIRECT_CONNECTED, node4); + zassert_equal( + bsc_direct_connection_established(&vmac4, NULL, 0) == true, true, NULL); + bsc_maintenance_timer(0); + zassert_equal( + Network_Port_SC_Direct_Connect_Connection_Status_Count( + Network_Port_Index_To_Instance(0)) == 2, + true, NULL); + sd = Network_Port_SC_Direct_Connect_Connection_Status_Get( + Network_Port_Index_To_Instance(0), 0); + zassert_equal(sd != NULL, true, NULL); + zassert_equal( + memcmp(sd->Peer_VMAC, &vmac4.address[0], BVLC_SC_VMAC_SIZE) == 0, true, + NULL); + sd = Network_Port_SC_Direct_Connect_Connection_Status_Get( + Network_Port_Index_To_Instance(0), 1); + zassert_equal(sd != NULL, true, NULL); + zassert_equal( + memcmp(sd->Peer_VMAC, &vmac3.address[0], BVLC_SC_VMAC_SIZE) == 0, true, + NULL); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + /* this wait is needed for different Disconnect_Timestamp in status */ + wait_sec(1); + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + /* this wait is needed for different Disconnect_Timestamp in status */ + wait_sec(1); + bsc_node_stop(node4); + wait_specific_node_ev(&node_ev4, BSC_NODE_EVENT_STOPPED, node4); + /* this wait is needed for different Disconnect_Timestamp in status */ + wait_sec(1); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + memset(&uuid2, 0x10, sizeof(uuid2)); + memset(&vmac2, 0x11, sizeof(vmac2)); + memset(&uuid3, 0x12, sizeof(uuid3)); + memset(&vmac3, 0x13, sizeof(vmac3)); + memset(&uuid4, 0x14, sizeof(uuid4)); + memset(&vmac4, 0x15, sizeof(vmac4)); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf4, &node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + wait_for_connection_to_hub(&node_ev2, node2); + wait_for_connection_to_hub(&node_ev3, node3); + + ret = bsc_node_start(node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev4, BSC_NODE_EVENT_STARTED, node4), true, 0); + wait_sec(1); + bsc_node_stop(node4); + wait_specific_node_ev(&node_ev4, BSC_NODE_EVENT_STOPPED, node4); + ret = bsc_node_start(node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev4, BSC_NODE_EVENT_STARTED, node4), true, 0); + wait_sec(1); + bsc_node_stop(node4); + wait_specific_node_ev(&node_ev4, BSC_NODE_EVENT_STOPPED, node4); + ret = bsc_node_start(node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev4, BSC_NODE_EVENT_STARTED, node4), true, 0); + wait_sec(1); + bsc_node_stop(node4); + wait_specific_node_ev(&node_ev4, BSC_NODE_EVENT_STOPPED, node4); + ret = bsc_node_start(node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev4, BSC_NODE_EVENT_STARTED, node4), true, 0); + + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + bsc_node_stop(node4); + wait_specific_node_ev(&node_ev4, BSC_NODE_EVENT_STOPPED, node4); + + bsc_cleanup(); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node4); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev2); + deinit_node_ev(&node_ev3); + deinit_node_ev(&node_ev4); + + Network_Port_Cleanup(); + bacfile_cleanup(); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(test_datalink_4, test_sc_datalink_failed_requests) +#else +static void test_sc_datalink_failed_requests(void) +#endif +{ + BSC_NODE_CONF conf2; + BACNET_SC_UUID uuid1; + BACNET_SC_VMAC_ADDRESS vmac1; + BACNET_SC_UUID uuid2; + BACNET_SC_VMAC_ADDRESS vmac2; + BACNET_SC_VMAC_ADDRESS vmac2_original; + BACNET_SC_UUID failed_uuid; + BACNET_SC_VMAC_ADDRESS failed_vmac; + char primary_url2[128]; + char secondary_url2[128]; + BSC_SC_RET ret; + BSC_NODE *node2; + bool b; + BACNET_OCTET_STRING mac_address; + +#ifdef ZEPHYR_PLATFORM + init_zephyr_env(); +#endif + + memset(&uuid1, 0x41, sizeof(uuid1)); + memset(&vmac1, 0x42, sizeof(vmac1)); + memset(&uuid2, 0x43, sizeof(uuid2)); + memset(&vmac2, 0x42, sizeof(vmac2)); + + sprintf(primary_url2, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, + SC_NETPORT_HUB_SERVER_PORT); + + bacfile_init(); + netport_object_init( + SC_DATALINK_INSTANCE, ca_cert, sizeof(ca_cert), server_cert, + sizeof(server_cert), server_key, sizeof(server_key), + BSC_DATALINK_HUB_IFACE, BSC_DATALINK_DIRECT_IFACE, &uuid1, &vmac1, NULL, + NULL, true, true, true); + + init_node_ev(&node_ev2); + zassert_equal(bsc_init(NULL), true, NULL); + + conf2.ca_cert_chain = ca_cert; + conf2.ca_cert_chain_size = sizeof(ca_cert); + conf2.cert_chain = client_cert; + conf2.cert_chain_size = sizeof(client_cert); + conf2.key = CLIENT_KEY; + conf2.key_size = sizeof(CLIENT_KEY); + conf2.local_uuid = &uuid2; + conf2.local_vmac = vmac2; + conf2.max_local_bvlc_len = MAX_BVLC_LEN; + conf2.max_local_npdu_len = MAX_NDPU_LEN; + conf2.connect_timeout_s = BACNET_TIMEOUT; + conf2.heartbeat_timeout_s = BACNET_TIMEOUT; + conf2.disconnect_timeout_s = BACNET_TIMEOUT; + conf2.reconnnect_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf2.primaryURL = primary_url2; + conf2.failoverURL = secondary_url2; + conf2.hub_server_port = 0; + conf2.direct_server_port = 0; + conf2.direct_iface = BSC_NETWORK_IFACE; + conf2.hub_iface = BSC_NETWORK_IFACE; + conf2.direct_connect_accept_enable = false; + conf2.direct_connect_initiate_enable = true; + conf2.hub_function_enabled = false; + conf2.direct_connection_accept_uris = NULL; + conf2.direct_connection_accept_uris_len = 0; + conf2.event_func = node_event2; + + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2); + wait_for_connection_to_hub(&node_ev2, node2); + bsc_cleanup(); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + memset(&uuid1, 0x51, sizeof(uuid1)); + memset(&vmac1, 0x52, sizeof(vmac1)); + memset(&failed_uuid, 0x53, sizeof(failed_uuid)); + memset(&failed_vmac, 0x52, sizeof(failed_vmac)); + memset(&uuid2, 0x53, sizeof(uuid2)); + memset(&vmac2, 0x52, sizeof(vmac2)); + memcpy(&vmac2_original, &vmac2, sizeof(vmac2)); + + netport_object_init( + SC_DATALINK_INSTANCE, ca_cert, sizeof(ca_cert), server_cert, + sizeof(server_cert), server_key, sizeof(server_key), + BSC_DATALINK_HUB_IFACE, BSC_DATALINK_DIRECT_IFACE, &uuid1, &vmac1, NULL, + NULL, true, true, true); + zassert_equal(bsc_init(NULL), true, NULL); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + b = Network_Port_MAC_Address( + Network_Port_Index_To_Instance(0), &mac_address); + zassert_true(b, NULL); + zassert_equal( + memcmp( + &vmac2_original.address[0], octetstring_value(&mac_address), + sizeof(vmac2_original)) == 0, + true, NULL); + + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2); + wait_for_connection_to_hub(&node_ev2, node2); + wait_for_failed_request( + &node_ev2, &failed_vmac, &failed_uuid, ERROR_CODE_NODE_DUPLICATE_VMAC); + b = Network_Port_MAC_Address( + Network_Port_Index_To_Instance(0), &mac_address); + zassert_true(b, NULL); + /*zassert_not_equal( + memcmp(&vmac2_original.address[0], octetstring_value(&mac_address), + sizeof(vmac2_original)) == 0, true, NULL);*/ + bsc_cleanup(); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev2); + + Network_Port_Cleanup(); + bacfile_cleanup(); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST_SUITE(test_datalink_1, NULL, NULL, NULL, NULL, NULL); +ZTEST_SUITE(test_datalink_2, NULL, NULL, NULL, NULL, NULL); +ZTEST_SUITE(test_datalink_3, NULL, NULL, NULL, NULL, NULL); +ZTEST_SUITE(test_datalink_4, NULL, NULL, NULL, NULL, NULL); +#else +void test_main(void) +{ + // setbuf(stdout, NULL); + // Tests must not be run in parallel threads! + // Thats why tests functions are in different suites. + ztest_test_suite(test_datalink_1, ztest_unit_test(test_sc_parameters)); + ztest_test_suite(test_datalink_2, ztest_unit_test(test_sc_datalink)); + ztest_test_suite( + test_datalink_3, ztest_unit_test(test_sc_datalink_properties)); + ztest_test_suite( + test_datalink_4, ztest_unit_test(test_sc_datalink_failed_requests)); + + ztest_run_test_suite(test_datalink_1); + ztest_run_test_suite(test_datalink_2); + ztest_run_test_suite(test_datalink_3); + ztest_run_test_suite(test_datalink_4); +} +#endif diff --git a/test/bacnet/datalink/bsc-node/CMakeLists.txt b/test/bacnet/datalink/bsc-node/CMakeLists.txt new file mode 100644 index 00000000..236823eb --- /dev/null +++ b/test/bacnet/datalink/bsc-node/CMakeLists.txt @@ -0,0 +1,290 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BACDL_BSC + BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM=3 + BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM=3 + BSC_CONF_WSURL_MAX_LEN=128 + BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN=128 + BSC_CONF_WEBSOCKET_SERVERS_NUM=4 + BSC_CONF_HUB_CONNECTORS_NUM=3 + BSC_CONF_HUB_FUNCTIONS_NUM=3 + BSC_CONF_NODES_NUM=3 + BSC_CONF_NODE_SWITCHES_NUM=3 + MAX_TSM_TRANSACTIONS=0 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +if(ZEPHYR_BASE) + message(FATAL_ERROR "ZEPHYR_BASE env variable defined. Use zephyr/CMakeLists.txt for Zephyr build") +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(STATUS "BACNet/SC node test: building for linux") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + add_compile_definitions(BACNET_PORT=linux) + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/linux/websocket-cli.c + ${PORTS_DIR}/linux/websocket-srv.c + ${PORTS_DIR}/linux/websocket-global.c + ${PORTS_DIR}/linux/bsc-event.c + ${PORTS_DIR}/linux/mstimer-init.c + ${PORTS_DIR}/linux/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) + +elseif(WIN32) + message(STATUS "BACNet/SC node test: building for win32") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/win32) + add_compile_definitions(BACNET_PORT=win32) + add_compile_definitions(BACNET_STACK_STATIC_DEFINE) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/win32/websocket-cli.c + ${PORTS_DIR}/win32/websocket-srv.c + ${PORTS_DIR}/win32/websocket-global.c + ${PORTS_DIR}/win32/bsc-event.c + ${PORTS_DIR}/win32/mstimer-init.c + ${PORTS_DIR}/win32/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + + # basically if you use vcpkg you should just add ${LIBWEBSOCKETS_LIBRARIES} + # into target_link_libraries() but for some reason it does not work as expected + # so that's why libs have to be hardcoded as workaround + target_link_libraries(${PROJECT_NAME} + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\websockets.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libssl.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libcrypto.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\pthreadVC3.lib + ws2_32.lib + userenv.lib + psapi.lib + iphlpapi.lib + crypt32.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\zlib.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\uv.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + comdlg32.lib + advapi32.lib + ) + +elseif(APPLE) + message(STATUS "BACNet/SC node test: building for APPLE") + execute_process ( + COMMAND bash -c "brew --prefix openssl" + OUTPUT_VARIABLE OPEN_SSL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE + ) + include_directories(${OPEN_SSL_DIR}/include) + add_compile_definitions(BACNET_PORT=bsd) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/bsd/websocket-cli.c + ${PORTS_DIR}/bsd/websocket-srv.c + ${PORTS_DIR}/bsd/websocket-global.c + ${PORTS_DIR}/bsd/bsc-event.c + ${PORTS_DIR}/bsd/mstimer-init.c + ${PORTS_DIR}/bsd/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) +endif() diff --git a/test/bacnet/datalink/bsc-node/src/main.c b/test/bacnet/datalink/bsc-node/src/main.c new file mode 100644 index 00000000..4de4dec6 --- /dev/null +++ b/test/bacnet/datalink/bsc-node/src/main.c @@ -0,0 +1,13100 @@ +/* + * Copyright (c) 2020 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +/* @file + * @brief test of bsc-socket interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned char ca_key[] = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x52, + 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, + 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x45, + 0x6f, 0x67, 0x49, 0x42, 0x41, 0x41, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, + 0x31, 0x66, 0x7a, 0x73, 0x2b, 0x56, 0x62, 0x46, 0x39, 0x31, 0x65, 0x45, + 0x66, 0x4c, 0x32, 0x67, 0x44, 0x63, 0x6a, 0x79, 0x44, 0x47, 0x68, 0x62, + 0x4d, 0x6f, 0x44, 0x48, 0x6c, 0x71, 0x53, 0x6d, 0x70, 0x78, 0x7a, 0x45, + 0x2b, 0x4a, 0x48, 0x34, 0x6f, 0x46, 0x47, 0x46, 0x42, 0x31, 0x47, 0x48, + 0x0a, 0x69, 0x35, 0x47, 0x7a, 0x33, 0x73, 0x47, 0x51, 0x4d, 0x6e, 0x41, + 0x6e, 0x32, 0x4c, 0x41, 0x6e, 0x51, 0x53, 0x5a, 0x72, 0x46, 0x77, 0x6c, + 0x77, 0x72, 0x48, 0x34, 0x68, 0x43, 0x36, 0x62, 0x6d, 0x4a, 0x33, 0x71, + 0x55, 0x58, 0x72, 0x61, 0x32, 0x64, 0x76, 0x63, 0x45, 0x34, 0x2f, 0x32, + 0x2f, 0x70, 0x74, 0x6b, 0x34, 0x31, 0x47, 0x39, 0x68, 0x75, 0x45, 0x4f, + 0x75, 0x77, 0x69, 0x6b, 0x68, 0x0a, 0x7a, 0x5a, 0x59, 0x65, 0x67, 0x6b, + 0x4b, 0x68, 0x62, 0x78, 0x46, 0x70, 0x77, 0x56, 0x2f, 0x76, 0x48, 0x32, + 0x48, 0x6f, 0x55, 0x45, 0x39, 0x30, 0x4d, 0x43, 0x56, 0x7a, 0x66, 0x59, + 0x56, 0x51, 0x62, 0x6a, 0x38, 0x51, 0x2f, 0x73, 0x51, 0x50, 0x6d, 0x79, + 0x4f, 0x6d, 0x64, 0x76, 0x46, 0x74, 0x4a, 0x41, 0x4e, 0x47, 0x53, 0x2f, + 0x52, 0x58, 0x31, 0x67, 0x6b, 0x2b, 0x6e, 0x7a, 0x67, 0x38, 0x0a, 0x54, + 0x4f, 0x33, 0x6f, 0x4a, 0x45, 0x6b, 0x70, 0x69, 0x6e, 0x67, 0x34, 0x45, + 0x31, 0x4c, 0x4a, 0x2b, 0x63, 0x48, 0x66, 0x45, 0x66, 0x4a, 0x45, 0x49, + 0x5a, 0x4f, 0x4d, 0x69, 0x4e, 0x69, 0x6a, 0x31, 0x4d, 0x72, 0x50, 0x70, + 0x32, 0x4d, 0x63, 0x36, 0x4d, 0x49, 0x7a, 0x54, 0x6f, 0x76, 0x4c, 0x6f, + 0x51, 0x31, 0x38, 0x36, 0x2f, 0x68, 0x63, 0x59, 0x50, 0x36, 0x62, 0x4f, + 0x76, 0x46, 0x59, 0x0a, 0x6d, 0x59, 0x78, 0x68, 0x59, 0x46, 0x30, 0x68, + 0x52, 0x38, 0x4f, 0x57, 0x53, 0x4c, 0x30, 0x55, 0x45, 0x78, 0x69, 0x4e, + 0x6f, 0x64, 0x45, 0x2f, 0x61, 0x70, 0x68, 0x7a, 0x39, 0x41, 0x56, 0x52, + 0x63, 0x6d, 0x79, 0x62, 0x76, 0x62, 0x4f, 0x6c, 0x77, 0x32, 0x4c, 0x71, + 0x44, 0x50, 0x42, 0x43, 0x62, 0x46, 0x6b, 0x72, 0x50, 0x75, 0x4c, 0x7a, + 0x6d, 0x6e, 0x47, 0x6b, 0x78, 0x46, 0x57, 0x41, 0x0a, 0x45, 0x42, 0x65, + 0x52, 0x38, 0x51, 0x43, 0x33, 0x77, 0x72, 0x57, 0x6a, 0x6e, 0x6a, 0x34, + 0x30, 0x71, 0x66, 0x6e, 0x33, 0x78, 0x50, 0x6c, 0x4e, 0x41, 0x73, 0x57, + 0x42, 0x64, 0x7a, 0x52, 0x2f, 0x64, 0x32, 0x43, 0x6d, 0x63, 0x51, 0x49, + 0x44, 0x41, 0x51, 0x41, 0x42, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x43, 0x58, + 0x49, 0x78, 0x7a, 0x38, 0x76, 0x70, 0x7a, 0x30, 0x4a, 0x59, 0x77, 0x64, + 0x7a, 0x0a, 0x70, 0x44, 0x4e, 0x4b, 0x37, 0x42, 0x4a, 0x73, 0x79, 0x73, + 0x32, 0x63, 0x46, 0x36, 0x48, 0x74, 0x36, 0x4d, 0x39, 0x52, 0x4d, 0x53, + 0x61, 0x43, 0x2f, 0x39, 0x65, 0x76, 0x44, 0x55, 0x4a, 0x42, 0x6a, 0x79, + 0x47, 0x42, 0x31, 0x4c, 0x54, 0x63, 0x6b, 0x4d, 0x32, 0x58, 0x4b, 0x44, + 0x49, 0x47, 0x79, 0x4b, 0x65, 0x6b, 0x56, 0x50, 0x78, 0x34, 0x57, 0x6b, + 0x44, 0x61, 0x39, 0x4a, 0x4c, 0x4f, 0x0a, 0x49, 0x59, 0x32, 0x50, 0x41, + 0x32, 0x76, 0x46, 0x37, 0x32, 0x6f, 0x4b, 0x4b, 0x2f, 0x37, 0x6c, 0x36, + 0x31, 0x56, 0x57, 0x76, 0x63, 0x59, 0x6b, 0x39, 0x4b, 0x68, 0x49, 0x71, + 0x79, 0x37, 0x31, 0x66, 0x46, 0x61, 0x45, 0x7a, 0x31, 0x5a, 0x49, 0x31, + 0x61, 0x42, 0x36, 0x2f, 0x71, 0x56, 0x36, 0x66, 0x77, 0x71, 0x58, 0x69, + 0x79, 0x48, 0x44, 0x4a, 0x63, 0x7a, 0x71, 0x6a, 0x2f, 0x33, 0x31, 0x0a, + 0x38, 0x45, 0x48, 0x48, 0x4f, 0x51, 0x55, 0x44, 0x4d, 0x59, 0x34, 0x2f, + 0x4f, 0x55, 0x46, 0x2f, 0x56, 0x37, 0x6f, 0x6f, 0x4b, 0x64, 0x31, 0x33, + 0x67, 0x35, 0x72, 0x7a, 0x4c, 0x41, 0x50, 0x47, 0x4e, 0x74, 0x43, 0x37, + 0x6e, 0x2b, 0x56, 0x6b, 0x6a, 0x41, 0x31, 0x31, 0x78, 0x38, 0x4f, 0x6c, + 0x32, 0x57, 0x62, 0x48, 0x33, 0x64, 0x6a, 0x31, 0x78, 0x4f, 0x47, 0x2f, + 0x66, 0x50, 0x52, 0x57, 0x0a, 0x72, 0x63, 0x69, 0x57, 0x46, 0x61, 0x79, + 0x4a, 0x72, 0x33, 0x71, 0x56, 0x6c, 0x5a, 0x2b, 0x42, 0x79, 0x31, 0x66, + 0x44, 0x43, 0x59, 0x38, 0x31, 0x4d, 0x73, 0x55, 0x56, 0x41, 0x65, 0x55, + 0x57, 0x2f, 0x54, 0x33, 0x6b, 0x39, 0x42, 0x6e, 0x4b, 0x67, 0x36, 0x68, + 0x71, 0x61, 0x33, 0x4a, 0x47, 0x36, 0x37, 0x4e, 0x41, 0x36, 0x42, 0x72, + 0x63, 0x7a, 0x4e, 0x4b, 0x53, 0x4b, 0x47, 0x79, 0x37, 0x0a, 0x70, 0x70, + 0x56, 0x56, 0x41, 0x65, 0x61, 0x47, 0x44, 0x49, 0x73, 0x37, 0x62, 0x73, + 0x43, 0x7a, 0x52, 0x70, 0x45, 0x42, 0x43, 0x67, 0x4e, 0x68, 0x52, 0x42, + 0x5a, 0x45, 0x2f, 0x41, 0x79, 0x65, 0x77, 0x4d, 0x56, 0x6e, 0x42, 0x4a, + 0x62, 0x4b, 0x41, 0x72, 0x6e, 0x72, 0x79, 0x69, 0x69, 0x38, 0x2f, 0x5a, + 0x62, 0x56, 0x51, 0x55, 0x4d, 0x4c, 0x56, 0x48, 0x32, 0x70, 0x68, 0x34, + 0x46, 0x69, 0x0a, 0x6b, 0x2b, 0x30, 0x56, 0x66, 0x56, 0x45, 0x43, 0x67, + 0x59, 0x45, 0x41, 0x2f, 0x5a, 0x45, 0x39, 0x35, 0x71, 0x4b, 0x46, 0x6d, + 0x71, 0x51, 0x65, 0x4f, 0x36, 0x6d, 0x76, 0x42, 0x37, 0x6b, 0x56, 0x6b, + 0x70, 0x56, 0x4b, 0x53, 0x58, 0x47, 0x75, 0x2f, 0x59, 0x35, 0x6b, 0x46, + 0x63, 0x70, 0x4c, 0x57, 0x4f, 0x63, 0x35, 0x45, 0x56, 0x48, 0x4c, 0x36, + 0x46, 0x6d, 0x66, 0x7a, 0x62, 0x71, 0x42, 0x0a, 0x41, 0x78, 0x6f, 0x71, + 0x39, 0x48, 0x68, 0x66, 0x4c, 0x79, 0x39, 0x61, 0x37, 0x4e, 0x7a, 0x73, + 0x61, 0x43, 0x77, 0x30, 0x66, 0x34, 0x56, 0x79, 0x5a, 0x72, 0x73, 0x4f, + 0x61, 0x73, 0x67, 0x42, 0x31, 0x43, 0x42, 0x5a, 0x50, 0x36, 0x30, 0x32, + 0x43, 0x47, 0x69, 0x38, 0x68, 0x62, 0x48, 0x61, 0x6a, 0x4a, 0x4d, 0x77, + 0x4a, 0x67, 0x37, 0x42, 0x39, 0x64, 0x38, 0x52, 0x71, 0x62, 0x67, 0x35, + 0x0a, 0x76, 0x61, 0x6b, 0x45, 0x75, 0x64, 0x70, 0x42, 0x31, 0x68, 0x39, + 0x6a, 0x6d, 0x39, 0x4e, 0x4a, 0x35, 0x61, 0x63, 0x52, 0x37, 0x2f, 0x76, + 0x63, 0x64, 0x56, 0x70, 0x45, 0x67, 0x70, 0x6e, 0x32, 0x6e, 0x50, 0x38, + 0x34, 0x52, 0x6e, 0x2f, 0x4e, 0x36, 0x72, 0x65, 0x75, 0x77, 0x52, 0x56, + 0x61, 0x38, 0x2f, 0x75, 0x2b, 0x59, 0x65, 0x63, 0x43, 0x67, 0x59, 0x45, + 0x41, 0x32, 0x41, 0x70, 0x36, 0x0a, 0x4f, 0x36, 0x78, 0x4d, 0x32, 0x32, + 0x70, 0x4e, 0x44, 0x64, 0x33, 0x70, 0x35, 0x79, 0x50, 0x65, 0x38, 0x36, + 0x38, 0x66, 0x79, 0x66, 0x35, 0x56, 0x35, 0x2f, 0x32, 0x4b, 0x6f, 0x76, + 0x34, 0x76, 0x46, 0x38, 0x77, 0x66, 0x69, 0x73, 0x61, 0x45, 0x63, 0x66, + 0x62, 0x2f, 0x57, 0x62, 0x4a, 0x52, 0x67, 0x77, 0x63, 0x36, 0x2f, 0x6b, + 0x55, 0x55, 0x6b, 0x31, 0x6c, 0x4a, 0x6e, 0x78, 0x6e, 0x47, 0x0a, 0x50, + 0x4b, 0x39, 0x38, 0x48, 0x6d, 0x6a, 0x69, 0x64, 0x35, 0x59, 0x33, 0x39, + 0x64, 0x7a, 0x69, 0x6c, 0x58, 0x67, 0x74, 0x43, 0x36, 0x36, 0x58, 0x56, + 0x41, 0x75, 0x30, 0x4f, 0x4a, 0x30, 0x34, 0x6c, 0x71, 0x69, 0x6c, 0x73, + 0x4d, 0x34, 0x42, 0x53, 0x43, 0x64, 0x6b, 0x6c, 0x69, 0x6d, 0x70, 0x68, + 0x75, 0x46, 0x73, 0x7a, 0x4a, 0x33, 0x50, 0x4f, 0x74, 0x36, 0x55, 0x37, + 0x68, 0x67, 0x42, 0x0a, 0x31, 0x71, 0x73, 0x4b, 0x4d, 0x4b, 0x61, 0x75, + 0x74, 0x6a, 0x33, 0x2f, 0x38, 0x76, 0x32, 0x53, 0x77, 0x63, 0x71, 0x36, + 0x46, 0x49, 0x78, 0x36, 0x6c, 0x36, 0x46, 0x6f, 0x37, 0x71, 0x2b, 0x73, + 0x70, 0x74, 0x56, 0x72, 0x57, 0x65, 0x63, 0x43, 0x67, 0x59, 0x42, 0x2b, + 0x36, 0x63, 0x31, 0x54, 0x76, 0x4a, 0x43, 0x6d, 0x66, 0x2f, 0x4a, 0x70, + 0x35, 0x6c, 0x6f, 0x6d, 0x77, 0x57, 0x71, 0x63, 0x0a, 0x76, 0x59, 0x41, + 0x37, 0x46, 0x6c, 0x32, 0x42, 0x70, 0x31, 0x31, 0x4d, 0x30, 0x72, 0x32, + 0x33, 0x74, 0x37, 0x4f, 0x47, 0x69, 0x61, 0x78, 0x48, 0x6c, 0x57, 0x51, + 0x34, 0x73, 0x6c, 0x71, 0x55, 0x56, 0x4f, 0x71, 0x66, 0x42, 0x67, 0x69, + 0x4f, 0x4d, 0x32, 0x4f, 0x4e, 0x48, 0x6c, 0x35, 0x74, 0x48, 0x59, 0x4d, + 0x42, 0x4f, 0x4b, 0x65, 0x7a, 0x35, 0x33, 0x67, 0x6c, 0x31, 0x67, 0x6d, + 0x6b, 0x0a, 0x52, 0x4c, 0x53, 0x6d, 0x2f, 0x47, 0x6b, 0x49, 0x2b, 0x48, + 0x4d, 0x7a, 0x62, 0x33, 0x74, 0x31, 0x31, 0x4d, 0x33, 0x4b, 0x6e, 0x71, + 0x52, 0x53, 0x44, 0x64, 0x35, 0x6e, 0x56, 0x6b, 0x41, 0x41, 0x50, 0x37, + 0x4b, 0x50, 0x32, 0x30, 0x41, 0x4d, 0x6a, 0x68, 0x56, 0x72, 0x44, 0x75, + 0x76, 0x7a, 0x75, 0x42, 0x56, 0x77, 0x53, 0x6c, 0x31, 0x6a, 0x6c, 0x31, + 0x53, 0x6e, 0x45, 0x61, 0x79, 0x76, 0x0a, 0x6b, 0x38, 0x5a, 0x30, 0x38, + 0x73, 0x37, 0x37, 0x35, 0x67, 0x66, 0x66, 0x75, 0x48, 0x4b, 0x58, 0x6e, + 0x36, 0x38, 0x41, 0x6a, 0x51, 0x4b, 0x42, 0x67, 0x46, 0x58, 0x73, 0x4c, + 0x4e, 0x73, 0x6f, 0x31, 0x74, 0x52, 0x35, 0x50, 0x62, 0x59, 0x6a, 0x4b, + 0x56, 0x44, 0x39, 0x69, 0x6b, 0x47, 0x65, 0x78, 0x2b, 0x54, 0x64, 0x57, + 0x36, 0x74, 0x4e, 0x77, 0x6d, 0x4b, 0x36, 0x39, 0x31, 0x33, 0x65, 0x0a, + 0x6d, 0x44, 0x6a, 0x6f, 0x5a, 0x57, 0x71, 0x79, 0x45, 0x72, 0x4c, 0x49, + 0x34, 0x66, 0x52, 0x62, 0x33, 0x74, 0x47, 0x63, 0x42, 0x65, 0x66, 0x6f, + 0x6e, 0x67, 0x68, 0x43, 0x42, 0x76, 0x37, 0x42, 0x79, 0x48, 0x71, 0x4c, + 0x75, 0x6d, 0x35, 0x58, 0x64, 0x32, 0x41, 0x34, 0x66, 0x6f, 0x46, 0x31, + 0x37, 0x32, 0x78, 0x79, 0x2f, 0x73, 0x71, 0x31, 0x63, 0x50, 0x4d, 0x48, + 0x54, 0x4b, 0x64, 0x57, 0x0a, 0x34, 0x62, 0x63, 0x6b, 0x35, 0x34, 0x75, + 0x62, 0x35, 0x7a, 0x78, 0x31, 0x79, 0x32, 0x2f, 0x53, 0x6e, 0x69, 0x50, + 0x76, 0x4b, 0x36, 0x6b, 0x39, 0x4e, 0x7a, 0x78, 0x4f, 0x6e, 0x67, 0x53, + 0x54, 0x75, 0x42, 0x54, 0x4c, 0x5a, 0x6a, 0x63, 0x6a, 0x42, 0x33, 0x58, + 0x4c, 0x39, 0x68, 0x39, 0x50, 0x45, 0x70, 0x7a, 0x7a, 0x6c, 0x68, 0x70, + 0x53, 0x58, 0x74, 0x70, 0x33, 0x55, 0x68, 0x4a, 0x30, 0x0a, 0x56, 0x53, + 0x4c, 0x48, 0x41, 0x6f, 0x47, 0x41, 0x44, 0x4e, 0x79, 0x6b, 0x52, 0x50, + 0x32, 0x55, 0x38, 0x34, 0x78, 0x70, 0x50, 0x6d, 0x36, 0x77, 0x31, 0x69, + 0x36, 0x72, 0x47, 0x48, 0x71, 0x50, 0x38, 0x67, 0x65, 0x2b, 0x4f, 0x6f, + 0x6f, 0x77, 0x69, 0x47, 0x47, 0x4b, 0x48, 0x54, 0x45, 0x48, 0x72, 0x56, + 0x39, 0x53, 0x79, 0x66, 0x6c, 0x2f, 0x63, 0x42, 0x39, 0x55, 0x4d, 0x65, + 0x79, 0x43, 0x0a, 0x45, 0x49, 0x68, 0x46, 0x37, 0x4e, 0x71, 0x6b, 0x35, + 0x76, 0x2b, 0x4d, 0x68, 0x2b, 0x50, 0x31, 0x52, 0x49, 0x52, 0x71, 0x35, + 0x39, 0x66, 0x4d, 0x41, 0x34, 0x72, 0x4e, 0x7a, 0x6d, 0x61, 0x78, 0x65, + 0x6b, 0x35, 0x72, 0x4b, 0x36, 0x45, 0x50, 0x67, 0x31, 0x62, 0x2f, 0x67, + 0x78, 0x7a, 0x45, 0x73, 0x4c, 0x56, 0x45, 0x45, 0x42, 0x58, 0x49, 0x51, + 0x35, 0x57, 0x57, 0x6e, 0x58, 0x6a, 0x52, 0x0a, 0x75, 0x2b, 0x5a, 0x56, + 0x49, 0x37, 0x33, 0x68, 0x62, 0x36, 0x79, 0x56, 0x56, 0x70, 0x75, 0x6f, + 0x4e, 0x68, 0x52, 0x43, 0x67, 0x59, 0x67, 0x45, 0x70, 0x48, 0x6b, 0x6e, + 0x47, 0x43, 0x45, 0x76, 0x62, 0x55, 0x52, 0x4b, 0x4f, 0x68, 0x79, 0x44, + 0x4d, 0x4e, 0x69, 0x6c, 0x49, 0x6a, 0x31, 0x6d, 0x71, 0x44, 0x4d, 0x3d, + 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x52, 0x53, + 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, + 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a +}; + +unsigned char ca_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x44, + 0x48, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x51, + 0x43, + 0x43, + 0x51, + 0x44, + 0x43, + 0x44, + 0x35, + 0x33, + 0x59, + 0x5a, + 0x4a, + 0x4a, + 0x37, + 0x6c, + 0x6a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x6b, + 0x71, + 0x68, + 0x6b, + 0x69, + 0x47, + 0x39, + 0x77, + 0x30, + 0x42, + 0x41, + 0x51, + 0x73, + 0x46, + 0x41, + 0x44, + 0x42, + 0x50, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x63, + 0x54, + 0x43, + 0x56, + 0x68, + 0x70, + 0x59, + 0x57, + 0x39, + 0x69, + 0x61, + 0x58, + 0x52, + 0x68, + 0x62, + 0x6a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x0a, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x41, + 0x67, + 0x46, + 0x77, + 0x30, + 0x79, + 0x4d, + 0x6a, + 0x41, + 0x33, + 0x4d, + 0x44, + 0x59, + 0x78, + 0x4d, + 0x54, + 0x49, + 0x30, + 0x4d, + 0x6a, + 0x42, + 0x61, + 0x47, + 0x41, + 0x38, + 0x79, + 0x4d, + 0x44, + 0x55, + 0x77, + 0x4d, + 0x44, + 0x63, + 0x78, + 0x4f, + 0x54, + 0x45, + 0x78, + 0x0a, + 0x4d, + 0x6a, + 0x51, + 0x79, + 0x4d, + 0x46, + 0x6f, + 0x77, + 0x54, + 0x7a, + 0x45, + 0x62, + 0x4d, + 0x42, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x68, + 0x4d, + 0x53, + 0x62, + 0x47, + 0x6c, + 0x69, + 0x64, + 0x32, + 0x56, + 0x69, + 0x63, + 0x32, + 0x39, + 0x6a, + 0x61, + 0x32, + 0x56, + 0x30, + 0x63, + 0x79, + 0x31, + 0x30, + 0x5a, + 0x58, + 0x4e, + 0x30, + 0x4d, + 0x52, + 0x49, + 0x77, + 0x45, + 0x41, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x48, + 0x45, + 0x77, + 0x6c, + 0x59, + 0x0a, + 0x61, + 0x57, + 0x46, + 0x76, + 0x59, + 0x6d, + 0x6c, + 0x30, + 0x59, + 0x57, + 0x34, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x67, + 0x54, + 0x42, + 0x6c, + 0x52, + 0x68, + 0x61, + 0x58, + 0x42, + 0x6c, + 0x61, + 0x54, + 0x45, + 0x4c, + 0x4d, + 0x41, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x68, + 0x4d, + 0x43, + 0x56, + 0x46, + 0x63, + 0x77, + 0x67, + 0x67, + 0x45, + 0x69, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x0a, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x41, + 0x51, + 0x55, + 0x41, + 0x41, + 0x34, + 0x49, + 0x42, + 0x44, + 0x77, + 0x41, + 0x77, + 0x67, + 0x67, + 0x45, + 0x4b, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x51, + 0x44, + 0x56, + 0x2f, + 0x4f, + 0x7a, + 0x35, + 0x56, + 0x73, + 0x58, + 0x33, + 0x56, + 0x34, + 0x52, + 0x38, + 0x76, + 0x61, + 0x41, + 0x4e, + 0x79, + 0x50, + 0x49, + 0x4d, + 0x61, + 0x46, + 0x73, + 0x79, + 0x67, + 0x4d, + 0x65, + 0x57, + 0x70, + 0x4b, + 0x61, + 0x6e, + 0x0a, + 0x48, + 0x4d, + 0x54, + 0x34, + 0x6b, + 0x66, + 0x69, + 0x67, + 0x55, + 0x59, + 0x55, + 0x48, + 0x55, + 0x59, + 0x65, + 0x4c, + 0x6b, + 0x62, + 0x50, + 0x65, + 0x77, + 0x5a, + 0x41, + 0x79, + 0x63, + 0x43, + 0x66, + 0x59, + 0x73, + 0x43, + 0x64, + 0x42, + 0x4a, + 0x6d, + 0x73, + 0x58, + 0x43, + 0x58, + 0x43, + 0x73, + 0x66, + 0x69, + 0x45, + 0x4c, + 0x70, + 0x75, + 0x59, + 0x6e, + 0x65, + 0x70, + 0x52, + 0x65, + 0x74, + 0x72, + 0x5a, + 0x32, + 0x39, + 0x77, + 0x54, + 0x6a, + 0x2f, + 0x62, + 0x2b, + 0x6d, + 0x0a, + 0x32, + 0x54, + 0x6a, + 0x55, + 0x62, + 0x32, + 0x47, + 0x34, + 0x51, + 0x36, + 0x37, + 0x43, + 0x4b, + 0x53, + 0x48, + 0x4e, + 0x6c, + 0x68, + 0x36, + 0x43, + 0x51, + 0x71, + 0x46, + 0x76, + 0x45, + 0x57, + 0x6e, + 0x42, + 0x58, + 0x2b, + 0x38, + 0x66, + 0x59, + 0x65, + 0x68, + 0x51, + 0x54, + 0x33, + 0x51, + 0x77, + 0x4a, + 0x58, + 0x4e, + 0x39, + 0x68, + 0x56, + 0x42, + 0x75, + 0x50, + 0x78, + 0x44, + 0x2b, + 0x78, + 0x41, + 0x2b, + 0x62, + 0x49, + 0x36, + 0x5a, + 0x32, + 0x38, + 0x57, + 0x30, + 0x6b, + 0x0a, + 0x41, + 0x30, + 0x5a, + 0x4c, + 0x39, + 0x46, + 0x66, + 0x57, + 0x43, + 0x54, + 0x36, + 0x66, + 0x4f, + 0x44, + 0x78, + 0x4d, + 0x37, + 0x65, + 0x67, + 0x6b, + 0x53, + 0x53, + 0x6d, + 0x4b, + 0x65, + 0x44, + 0x67, + 0x54, + 0x55, + 0x73, + 0x6e, + 0x35, + 0x77, + 0x64, + 0x38, + 0x52, + 0x38, + 0x6b, + 0x51, + 0x68, + 0x6b, + 0x34, + 0x79, + 0x49, + 0x32, + 0x4b, + 0x50, + 0x55, + 0x79, + 0x73, + 0x2b, + 0x6e, + 0x59, + 0x78, + 0x7a, + 0x6f, + 0x77, + 0x6a, + 0x4e, + 0x4f, + 0x69, + 0x38, + 0x75, + 0x68, + 0x0a, + 0x44, + 0x58, + 0x7a, + 0x72, + 0x2b, + 0x46, + 0x78, + 0x67, + 0x2f, + 0x70, + 0x73, + 0x36, + 0x38, + 0x56, + 0x69, + 0x5a, + 0x6a, + 0x47, + 0x46, + 0x67, + 0x58, + 0x53, + 0x46, + 0x48, + 0x77, + 0x35, + 0x5a, + 0x49, + 0x76, + 0x52, + 0x51, + 0x54, + 0x47, + 0x49, + 0x32, + 0x68, + 0x30, + 0x54, + 0x39, + 0x71, + 0x6d, + 0x48, + 0x50, + 0x30, + 0x42, + 0x56, + 0x46, + 0x79, + 0x62, + 0x4a, + 0x75, + 0x39, + 0x73, + 0x36, + 0x58, + 0x44, + 0x59, + 0x75, + 0x6f, + 0x4d, + 0x38, + 0x45, + 0x4a, + 0x73, + 0x0a, + 0x57, + 0x53, + 0x73, + 0x2b, + 0x34, + 0x76, + 0x4f, + 0x61, + 0x63, + 0x61, + 0x54, + 0x45, + 0x56, + 0x59, + 0x41, + 0x51, + 0x46, + 0x35, + 0x48, + 0x78, + 0x41, + 0x4c, + 0x66, + 0x43, + 0x74, + 0x61, + 0x4f, + 0x65, + 0x50, + 0x6a, + 0x53, + 0x70, + 0x2b, + 0x66, + 0x66, + 0x45, + 0x2b, + 0x55, + 0x30, + 0x43, + 0x78, + 0x59, + 0x46, + 0x33, + 0x4e, + 0x48, + 0x39, + 0x33, + 0x59, + 0x4b, + 0x5a, + 0x78, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x0a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x49, + 0x6d, + 0x4a, + 0x76, + 0x71, + 0x6e, + 0x70, + 0x50, + 0x64, + 0x53, + 0x75, + 0x46, + 0x4f, + 0x5a, + 0x54, + 0x36, + 0x76, + 0x74, + 0x48, + 0x31, + 0x70, + 0x4a, + 0x43, + 0x45, + 0x76, + 0x4a, + 0x39, + 0x62, + 0x53, + 0x78, + 0x31, + 0x43, + 0x41, + 0x76, + 0x36, + 0x46, + 0x34, + 0x46, + 0x44, + 0x6f, + 0x77, + 0x4b, + 0x77, + 0x0a, + 0x43, + 0x71, + 0x4b, + 0x53, + 0x6c, + 0x59, + 0x45, + 0x6a, + 0x72, + 0x49, + 0x4c, + 0x64, + 0x6c, + 0x42, + 0x30, + 0x39, + 0x32, + 0x31, + 0x4f, + 0x54, + 0x30, + 0x76, + 0x61, + 0x68, + 0x33, + 0x6c, + 0x55, + 0x76, + 0x2f, + 0x6b, + 0x47, + 0x4e, + 0x4c, + 0x76, + 0x58, + 0x55, + 0x71, + 0x54, + 0x69, + 0x42, + 0x61, + 0x6b, + 0x77, + 0x66, + 0x52, + 0x47, + 0x30, + 0x39, + 0x61, + 0x49, + 0x45, + 0x6e, + 0x53, + 0x68, + 0x6d, + 0x79, + 0x6f, + 0x30, + 0x68, + 0x63, + 0x65, + 0x4f, + 0x68, + 0x33, + 0x0a, + 0x4f, + 0x31, + 0x4b, + 0x4b, + 0x59, + 0x76, + 0x4a, + 0x32, + 0x6a, + 0x4a, + 0x47, + 0x6b, + 0x36, + 0x50, + 0x6c, + 0x52, + 0x78, + 0x65, + 0x53, + 0x67, + 0x37, + 0x64, + 0x35, + 0x4d, + 0x69, + 0x37, + 0x58, + 0x67, + 0x6e, + 0x41, + 0x64, + 0x65, + 0x61, + 0x78, + 0x77, + 0x68, + 0x75, + 0x76, + 0x5a, + 0x5a, + 0x6d, + 0x61, + 0x49, + 0x7a, + 0x62, + 0x68, + 0x41, + 0x57, + 0x50, + 0x38, + 0x71, + 0x67, + 0x49, + 0x30, + 0x36, + 0x50, + 0x32, + 0x52, + 0x42, + 0x53, + 0x35, + 0x42, + 0x4a, + 0x76, + 0x0a, + 0x72, + 0x44, + 0x44, + 0x33, + 0x44, + 0x68, + 0x77, + 0x38, + 0x4e, + 0x38, + 0x47, + 0x77, + 0x42, + 0x44, + 0x31, + 0x52, + 0x59, + 0x32, + 0x79, + 0x4b, + 0x72, + 0x79, + 0x46, + 0x51, + 0x2b, + 0x34, + 0x55, + 0x32, + 0x31, + 0x45, + 0x72, + 0x73, + 0x77, + 0x2f, + 0x33, + 0x38, + 0x63, + 0x59, + 0x38, + 0x55, + 0x41, + 0x46, + 0x54, + 0x6b, + 0x67, + 0x33, + 0x72, + 0x57, + 0x72, + 0x34, + 0x44, + 0x57, + 0x78, + 0x36, + 0x74, + 0x6e, + 0x49, + 0x66, + 0x64, + 0x72, + 0x4e, + 0x31, + 0x49, + 0x49, + 0x0a, + 0x70, + 0x52, + 0x71, + 0x53, + 0x78, + 0x48, + 0x51, + 0x57, + 0x34, + 0x6b, + 0x5a, + 0x61, + 0x67, + 0x31, + 0x75, + 0x5a, + 0x64, + 0x46, + 0x63, + 0x53, + 0x69, + 0x61, + 0x59, + 0x54, + 0x2b, + 0x65, + 0x62, + 0x4c, + 0x57, + 0x56, + 0x58, + 0x41, + 0x7a, + 0x62, + 0x66, + 0x4a, + 0x4c, + 0x4f, + 0x56, + 0x38, + 0x45, + 0x46, + 0x46, + 0x33, + 0x52, + 0x44, + 0x43, + 0x2f, + 0x41, + 0x33, + 0x55, + 0x33, + 0x56, + 0x63, + 0x4f, + 0x52, + 0x44, + 0x42, + 0x44, + 0x57, + 0x44, + 0x68, + 0x6d, + 0x68, + 0x0a, + 0x38, + 0x72, + 0x34, + 0x54, + 0x2b, + 0x36, + 0x46, + 0x67, + 0x67, + 0x59, + 0x4c, + 0x32, + 0x65, + 0x6a, + 0x4d, + 0x6d, + 0x66, + 0x77, + 0x62, + 0x65, + 0x77, + 0x7a, + 0x49, + 0x2b, + 0x48, + 0x6d, + 0x34, + 0x53, + 0x69, + 0x64, + 0x67, + 0x4f, + 0x78, + 0x4c, + 0x66, + 0x31, + 0x46, + 0x78, + 0x31, + 0x64, + 0x32, + 0x76, + 0x34, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char node_key[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x4a, + 0x4b, + 0x51, + 0x49, + 0x42, + 0x41, + 0x41, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x72, + 0x77, + 0x53, + 0x79, + 0x4e, + 0x6e, + 0x68, + 0x66, + 0x76, + 0x44, + 0x69, + 0x63, + 0x47, + 0x4a, + 0x4f, + 0x42, + 0x66, + 0x55, + 0x51, + 0x4c, + 0x47, + 0x45, + 0x70, + 0x4b, + 0x43, + 0x32, + 0x2f, + 0x4e, + 0x32, + 0x35, + 0x54, + 0x6f, + 0x32, + 0x4b, + 0x66, + 0x36, + 0x36, + 0x7a, + 0x6b, + 0x44, + 0x62, + 0x33, + 0x6d, + 0x34, + 0x49, + 0x50, + 0x77, + 0x4c, + 0x0a, + 0x63, + 0x63, + 0x78, + 0x47, + 0x57, + 0x48, + 0x57, + 0x75, + 0x65, + 0x78, + 0x69, + 0x70, + 0x6d, + 0x76, + 0x41, + 0x77, + 0x46, + 0x2b, + 0x58, + 0x32, + 0x36, + 0x46, + 0x47, + 0x7a, + 0x35, + 0x66, + 0x4a, + 0x64, + 0x71, + 0x38, + 0x46, + 0x68, + 0x77, + 0x6c, + 0x71, + 0x2b, + 0x6e, + 0x71, + 0x6a, + 0x34, + 0x50, + 0x68, + 0x6c, + 0x2f, + 0x51, + 0x2b, + 0x77, + 0x55, + 0x42, + 0x6c, + 0x51, + 0x2b, + 0x58, + 0x4d, + 0x6a, + 0x71, + 0x49, + 0x70, + 0x77, + 0x79, + 0x7a, + 0x70, + 0x5a, + 0x6d, + 0x0a, + 0x34, + 0x48, + 0x6d, + 0x2b, + 0x4f, + 0x4f, + 0x43, + 0x4b, + 0x66, + 0x73, + 0x79, + 0x58, + 0x64, + 0x6d, + 0x4c, + 0x4c, + 0x70, + 0x46, + 0x79, + 0x56, + 0x6b, + 0x34, + 0x72, + 0x72, + 0x32, + 0x47, + 0x51, + 0x34, + 0x4b, + 0x46, + 0x53, + 0x51, + 0x58, + 0x51, + 0x58, + 0x79, + 0x5a, + 0x78, + 0x78, + 0x62, + 0x4e, + 0x46, + 0x64, + 0x67, + 0x6e, + 0x43, + 0x72, + 0x76, + 0x6d, + 0x74, + 0x6b, + 0x49, + 0x42, + 0x51, + 0x62, + 0x76, + 0x6d, + 0x6e, + 0x42, + 0x79, + 0x50, + 0x2b, + 0x77, + 0x6e, + 0x0a, + 0x65, + 0x7a, + 0x68, + 0x55, + 0x51, + 0x4b, + 0x4c, + 0x6b, + 0x6d, + 0x51, + 0x50, + 0x32, + 0x53, + 0x6a, + 0x37, + 0x65, + 0x52, + 0x7a, + 0x4c, + 0x6f, + 0x79, + 0x58, + 0x32, + 0x71, + 0x41, + 0x49, + 0x75, + 0x43, + 0x46, + 0x59, + 0x62, + 0x57, + 0x41, + 0x36, + 0x44, + 0x57, + 0x53, + 0x39, + 0x50, + 0x4c, + 0x56, + 0x75, + 0x67, + 0x71, + 0x53, + 0x76, + 0x4c, + 0x65, + 0x51, + 0x71, + 0x7a, + 0x31, + 0x74, + 0x70, + 0x32, + 0x66, + 0x5a, + 0x70, + 0x68, + 0x42, + 0x64, + 0x6a, + 0x66, + 0x49, + 0x0a, + 0x6c, + 0x65, + 0x44, + 0x42, + 0x75, + 0x7a, + 0x59, + 0x67, + 0x42, + 0x30, + 0x61, + 0x63, + 0x39, + 0x79, + 0x44, + 0x48, + 0x31, + 0x6d, + 0x65, + 0x65, + 0x51, + 0x4f, + 0x43, + 0x4e, + 0x50, + 0x61, + 0x7a, + 0x52, + 0x41, + 0x63, + 0x57, + 0x57, + 0x77, + 0x75, + 0x2b, + 0x6f, + 0x56, + 0x57, + 0x6d, + 0x45, + 0x42, + 0x45, + 0x6b, + 0x50, + 0x44, + 0x49, + 0x31, + 0x70, + 0x68, + 0x75, + 0x6e, + 0x44, + 0x39, + 0x33, + 0x33, + 0x47, + 0x31, + 0x52, + 0x63, + 0x57, + 0x64, + 0x6d, + 0x51, + 0x55, + 0x0a, + 0x72, + 0x37, + 0x6b, + 0x48, + 0x65, + 0x50, + 0x4d, + 0x50, + 0x77, + 0x35, + 0x70, + 0x33, + 0x42, + 0x7a, + 0x2b, + 0x71, + 0x6a, + 0x71, + 0x61, + 0x48, + 0x2b, + 0x36, + 0x6f, + 0x43, + 0x54, + 0x68, + 0x5a, + 0x43, + 0x6f, + 0x2b, + 0x59, + 0x55, + 0x39, + 0x68, + 0x51, + 0x2f, + 0x34, + 0x2b, + 0x48, + 0x77, + 0x4d, + 0x4e, + 0x79, + 0x38, + 0x51, + 0x73, + 0x79, + 0x6a, + 0x61, + 0x6d, + 0x66, + 0x37, + 0x31, + 0x7a, + 0x35, + 0x67, + 0x34, + 0x43, + 0x68, + 0x65, + 0x32, + 0x34, + 0x51, + 0x50, + 0x0a, + 0x66, + 0x63, + 0x6e, + 0x30, + 0x33, + 0x57, + 0x79, + 0x2f, + 0x5a, + 0x52, + 0x61, + 0x75, + 0x53, + 0x67, + 0x73, + 0x51, + 0x6e, + 0x79, + 0x7a, + 0x68, + 0x72, + 0x5a, + 0x7a, + 0x39, + 0x57, + 0x4a, + 0x35, + 0x2b, + 0x38, + 0x77, + 0x55, + 0x71, + 0x6a, + 0x34, + 0x34, + 0x51, + 0x38, + 0x45, + 0x36, + 0x70, + 0x59, + 0x52, + 0x4c, + 0x30, + 0x68, + 0x34, + 0x78, + 0x65, + 0x49, + 0x73, + 0x45, + 0x4f, + 0x76, + 0x2b, + 0x49, + 0x77, + 0x4e, + 0x62, + 0x56, + 0x59, + 0x6d, + 0x38, + 0x57, + 0x31, + 0x0a, + 0x62, + 0x48, + 0x5a, + 0x62, + 0x64, + 0x57, + 0x31, + 0x53, + 0x65, + 0x4d, + 0x77, + 0x70, + 0x74, + 0x45, + 0x62, + 0x37, + 0x47, + 0x50, + 0x67, + 0x55, + 0x61, + 0x4f, + 0x2f, + 0x79, + 0x4a, + 0x7a, + 0x2f, + 0x68, + 0x4b, + 0x71, + 0x63, + 0x72, + 0x70, + 0x49, + 0x4a, + 0x37, + 0x2f, + 0x49, + 0x6f, + 0x61, + 0x41, + 0x54, + 0x4b, + 0x2b, + 0x6a, + 0x39, + 0x70, + 0x38, + 0x32, + 0x62, + 0x33, + 0x51, + 0x69, + 0x32, + 0x2f, + 0x55, + 0x54, + 0x70, + 0x71, + 0x49, + 0x44, + 0x49, + 0x67, + 0x79, + 0x0a, + 0x79, + 0x5a, + 0x41, + 0x4c, + 0x55, + 0x6b, + 0x36, + 0x68, + 0x57, + 0x48, + 0x39, + 0x37, + 0x53, + 0x6f, + 0x4d, + 0x61, + 0x71, + 0x6b, + 0x68, + 0x55, + 0x6a, + 0x73, + 0x44, + 0x38, + 0x36, + 0x7a, + 0x50, + 0x32, + 0x31, + 0x4b, + 0x48, + 0x45, + 0x4e, + 0x35, + 0x47, + 0x35, + 0x6b, + 0x58, + 0x33, + 0x36, + 0x41, + 0x77, + 0x79, + 0x66, + 0x53, + 0x45, + 0x41, + 0x6d, + 0x52, + 0x76, + 0x6c, + 0x48, + 0x63, + 0x7a, + 0x47, + 0x6a, + 0x78, + 0x66, + 0x71, + 0x51, + 0x50, + 0x42, + 0x71, + 0x7a, + 0x0a, + 0x4d, + 0x63, + 0x65, + 0x39, + 0x46, + 0x53, + 0x78, + 0x39, + 0x4e, + 0x2b, + 0x52, + 0x39, + 0x62, + 0x77, + 0x2f, + 0x56, + 0x42, + 0x73, + 0x66, + 0x43, + 0x63, + 0x7a, + 0x46, + 0x35, + 0x4c, + 0x4b, + 0x6d, + 0x37, + 0x52, + 0x6f, + 0x6a, + 0x48, + 0x44, + 0x35, + 0x7a, + 0x77, + 0x4f, + 0x62, + 0x4f, + 0x33, + 0x36, + 0x57, + 0x7a, + 0x74, + 0x75, + 0x50, + 0x61, + 0x62, + 0x62, + 0x6f, + 0x66, + 0x7a, + 0x30, + 0x6d, + 0x46, + 0x69, + 0x56, + 0x79, + 0x75, + 0x46, + 0x33, + 0x63, + 0x4f, + 0x62, + 0x0a, + 0x2f, + 0x74, + 0x71, + 0x47, + 0x56, + 0x56, + 0x39, + 0x33, + 0x6e, + 0x49, + 0x72, + 0x41, + 0x42, + 0x62, + 0x79, + 0x2f, + 0x44, + 0x62, + 0x42, + 0x62, + 0x49, + 0x51, + 0x4a, + 0x58, + 0x4c, + 0x73, + 0x73, + 0x74, + 0x6e, + 0x70, + 0x77, + 0x70, + 0x6c, + 0x5a, + 0x62, + 0x61, + 0x5a, + 0x6b, + 0x59, + 0x71, + 0x50, + 0x76, + 0x4f, + 0x37, + 0x47, + 0x52, + 0x56, + 0x37, + 0x30, + 0x48, + 0x38, + 0x77, + 0x4e, + 0x4f, + 0x35, + 0x4f, + 0x66, + 0x33, + 0x4d, + 0x43, + 0x41, + 0x77, + 0x45, + 0x41, + 0x0a, + 0x41, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x42, + 0x6c, + 0x78, + 0x33, + 0x56, + 0x62, + 0x39, + 0x2b, + 0x53, + 0x30, + 0x73, + 0x4c, + 0x63, + 0x57, + 0x45, + 0x37, + 0x48, + 0x61, + 0x42, + 0x78, + 0x66, + 0x73, + 0x71, + 0x45, + 0x63, + 0x6e, + 0x48, + 0x33, + 0x32, + 0x33, + 0x6c, + 0x49, + 0x46, + 0x55, + 0x66, + 0x56, + 0x75, + 0x4f, + 0x4c, + 0x7a, + 0x6d, + 0x77, + 0x4f, + 0x6a, + 0x69, + 0x35, + 0x39, + 0x64, + 0x6b, + 0x78, + 0x39, + 0x48, + 0x6c, + 0x30, + 0x4e, + 0x2f, + 0x75, + 0x66, + 0x0a, + 0x32, + 0x6c, + 0x66, + 0x48, + 0x6f, + 0x71, + 0x5a, + 0x56, + 0x50, + 0x34, + 0x61, + 0x32, + 0x30, + 0x38, + 0x79, + 0x71, + 0x6a, + 0x4b, + 0x65, + 0x73, + 0x6d, + 0x6d, + 0x6b, + 0x66, + 0x66, + 0x57, + 0x59, + 0x64, + 0x48, + 0x6d, + 0x59, + 0x2b, + 0x74, + 0x74, + 0x55, + 0x72, + 0x79, + 0x72, + 0x35, + 0x61, + 0x62, + 0x2b, + 0x4e, + 0x55, + 0x55, + 0x67, + 0x4c, + 0x57, + 0x33, + 0x62, + 0x38, + 0x75, + 0x4a, + 0x49, + 0x6a, + 0x76, + 0x51, + 0x64, + 0x30, + 0x39, + 0x64, + 0x6c, + 0x63, + 0x55, + 0x0a, + 0x4a, + 0x69, + 0x5a, + 0x75, + 0x30, + 0x6b, + 0x56, + 0x61, + 0x37, + 0x2f, + 0x79, + 0x4d, + 0x4d, + 0x4e, + 0x32, + 0x32, + 0x6d, + 0x5a, + 0x47, + 0x4f, + 0x34, + 0x70, + 0x36, + 0x52, + 0x65, + 0x6b, + 0x50, + 0x64, + 0x63, + 0x73, + 0x41, + 0x58, + 0x55, + 0x44, + 0x6a, + 0x2b, + 0x6d, + 0x48, + 0x6c, + 0x2b, + 0x73, + 0x33, + 0x66, + 0x57, + 0x64, + 0x4a, + 0x49, + 0x69, + 0x58, + 0x67, + 0x49, + 0x53, + 0x36, + 0x6d, + 0x4b, + 0x4c, + 0x5a, + 0x64, + 0x61, + 0x5a, + 0x51, + 0x43, + 0x46, + 0x77, + 0x0a, + 0x57, + 0x72, + 0x31, + 0x2f, + 0x72, + 0x38, + 0x2f, + 0x54, + 0x31, + 0x2b, + 0x64, + 0x49, + 0x52, + 0x61, + 0x76, + 0x33, + 0x5a, + 0x53, + 0x6e, + 0x68, + 0x47, + 0x75, + 0x69, + 0x61, + 0x63, + 0x34, + 0x34, + 0x72, + 0x79, + 0x70, + 0x38, + 0x56, + 0x69, + 0x79, + 0x34, + 0x4e, + 0x4a, + 0x2b, + 0x2f, + 0x5a, + 0x46, + 0x6e, + 0x78, + 0x4f, + 0x46, + 0x70, + 0x76, + 0x38, + 0x4c, + 0x63, + 0x37, + 0x6a, + 0x30, + 0x4d, + 0x7a, + 0x31, + 0x58, + 0x42, + 0x39, + 0x4e, + 0x6e, + 0x38, + 0x78, + 0x41, + 0x0a, + 0x62, + 0x76, + 0x41, + 0x5a, + 0x52, + 0x78, + 0x62, + 0x76, + 0x75, + 0x4a, + 0x4a, + 0x76, + 0x61, + 0x43, + 0x61, + 0x37, + 0x46, + 0x79, + 0x54, + 0x30, + 0x77, + 0x74, + 0x4e, + 0x4a, + 0x79, + 0x64, + 0x55, + 0x36, + 0x31, + 0x6f, + 0x48, + 0x50, + 0x66, + 0x43, + 0x30, + 0x6b, + 0x50, + 0x35, + 0x68, + 0x2b, + 0x76, + 0x4c, + 0x4d, + 0x5a, + 0x32, + 0x69, + 0x73, + 0x6e, + 0x41, + 0x4b, + 0x59, + 0x76, + 0x63, + 0x30, + 0x51, + 0x55, + 0x62, + 0x4b, + 0x58, + 0x4c, + 0x30, + 0x49, + 0x33, + 0x36, + 0x0a, + 0x55, + 0x6e, + 0x6d, + 0x6c, + 0x33, + 0x59, + 0x42, + 0x79, + 0x4e, + 0x4b, + 0x59, + 0x66, + 0x31, + 0x46, + 0x35, + 0x43, + 0x79, + 0x75, + 0x38, + 0x32, + 0x49, + 0x54, + 0x64, + 0x50, + 0x59, + 0x37, + 0x64, + 0x43, + 0x39, + 0x39, + 0x55, + 0x6f, + 0x6e, + 0x2f, + 0x47, + 0x35, + 0x66, + 0x52, + 0x55, + 0x56, + 0x6e, + 0x61, + 0x6d, + 0x58, + 0x34, + 0x41, + 0x2b, + 0x4e, + 0x62, + 0x66, + 0x47, + 0x45, + 0x68, + 0x6c, + 0x4a, + 0x32, + 0x58, + 0x58, + 0x55, + 0x51, + 0x58, + 0x54, + 0x69, + 0x68, + 0x0a, + 0x41, + 0x77, + 0x46, + 0x75, + 0x5a, + 0x48, + 0x72, + 0x54, + 0x48, + 0x41, + 0x4a, + 0x58, + 0x39, + 0x64, + 0x6f, + 0x77, + 0x34, + 0x4c, + 0x51, + 0x51, + 0x75, + 0x49, + 0x68, + 0x44, + 0x50, + 0x4f, + 0x59, + 0x55, + 0x4e, + 0x7a, + 0x5a, + 0x6f, + 0x34, + 0x57, + 0x30, + 0x34, + 0x44, + 0x44, + 0x4a, + 0x2b, + 0x62, + 0x75, + 0x66, + 0x34, + 0x67, + 0x7a, + 0x70, + 0x4e, + 0x68, + 0x31, + 0x39, + 0x59, + 0x52, + 0x65, + 0x42, + 0x44, + 0x75, + 0x7a, + 0x67, + 0x33, + 0x43, + 0x54, + 0x62, + 0x59, + 0x0a, + 0x4b, + 0x38, + 0x52, + 0x45, + 0x6e, + 0x58, + 0x76, + 0x4d, + 0x59, + 0x34, + 0x4b, + 0x44, + 0x36, + 0x63, + 0x69, + 0x4d, + 0x4f, + 0x72, + 0x61, + 0x78, + 0x4a, + 0x53, + 0x31, + 0x43, + 0x49, + 0x6d, + 0x67, + 0x53, + 0x70, + 0x68, + 0x56, + 0x70, + 0x50, + 0x37, + 0x54, + 0x77, + 0x4f, + 0x4f, + 0x6c, + 0x62, + 0x75, + 0x78, + 0x6a, + 0x39, + 0x76, + 0x31, + 0x45, + 0x63, + 0x54, + 0x41, + 0x78, + 0x34, + 0x49, + 0x68, + 0x31, + 0x38, + 0x71, + 0x72, + 0x71, + 0x6c, + 0x36, + 0x32, + 0x32, + 0x56, + 0x0a, + 0x34, + 0x45, + 0x70, + 0x34, + 0x6c, + 0x62, + 0x2f, + 0x4a, + 0x78, + 0x57, + 0x6e, + 0x58, + 0x4e, + 0x48, + 0x77, + 0x2f, + 0x7a, + 0x32, + 0x64, + 0x70, + 0x64, + 0x4d, + 0x55, + 0x6b, + 0x4a, + 0x4a, + 0x66, + 0x33, + 0x6c, + 0x6d, + 0x6e, + 0x2b, + 0x2f, + 0x34, + 0x5a, + 0x56, + 0x2b, + 0x67, + 0x4e, + 0x42, + 0x74, + 0x42, + 0x79, + 0x71, + 0x4c, + 0x52, + 0x49, + 0x50, + 0x54, + 0x47, + 0x32, + 0x6c, + 0x37, + 0x4f, + 0x34, + 0x55, + 0x6e, + 0x52, + 0x2f, + 0x32, + 0x69, + 0x75, + 0x71, + 0x78, + 0x0a, + 0x30, + 0x77, + 0x4b, + 0x70, + 0x59, + 0x37, + 0x54, + 0x68, + 0x34, + 0x49, + 0x66, + 0x58, + 0x6a, + 0x53, + 0x56, + 0x57, + 0x36, + 0x73, + 0x4f, + 0x63, + 0x2b, + 0x4a, + 0x77, + 0x37, + 0x64, + 0x35, + 0x68, + 0x73, + 0x56, + 0x2f, + 0x7a, + 0x37, + 0x41, + 0x55, + 0x52, + 0x45, + 0x45, + 0x4a, + 0x34, + 0x57, + 0x58, + 0x36, + 0x4e, + 0x4e, + 0x70, + 0x42, + 0x77, + 0x51, + 0x5a, + 0x4e, + 0x32, + 0x56, + 0x78, + 0x48, + 0x30, + 0x37, + 0x49, + 0x78, + 0x79, + 0x76, + 0x6b, + 0x33, + 0x5a, + 0x51, + 0x0a, + 0x58, + 0x67, + 0x56, + 0x51, + 0x58, + 0x4e, + 0x68, + 0x72, + 0x79, + 0x4e, + 0x43, + 0x49, + 0x57, + 0x70, + 0x74, + 0x2f, + 0x66, + 0x57, + 0x69, + 0x76, + 0x75, + 0x50, + 0x43, + 0x7a, + 0x77, + 0x74, + 0x32, + 0x55, + 0x74, + 0x4f, + 0x72, + 0x70, + 0x2f, + 0x35, + 0x57, + 0x43, + 0x35, + 0x37, + 0x77, + 0x31, + 0x65, + 0x65, + 0x4d, + 0x73, + 0x58, + 0x63, + 0x68, + 0x72, + 0x41, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x45, + 0x41, + 0x35, + 0x4b, + 0x48, + 0x7a, + 0x4c, + 0x34, + 0x76, + 0x57, + 0x0a, + 0x42, + 0x58, + 0x65, + 0x66, + 0x6f, + 0x36, + 0x2f, + 0x4b, + 0x68, + 0x77, + 0x49, + 0x4a, + 0x41, + 0x37, + 0x79, + 0x35, + 0x54, + 0x53, + 0x67, + 0x41, + 0x62, + 0x44, + 0x68, + 0x59, + 0x70, + 0x51, + 0x69, + 0x34, + 0x32, + 0x4c, + 0x6c, + 0x74, + 0x33, + 0x37, + 0x4e, + 0x4d, + 0x6e, + 0x4a, + 0x63, + 0x79, + 0x4b, + 0x54, + 0x56, + 0x6d, + 0x79, + 0x78, + 0x70, + 0x66, + 0x59, + 0x47, + 0x38, + 0x65, + 0x6e, + 0x70, + 0x31, + 0x46, + 0x71, + 0x7a, + 0x54, + 0x56, + 0x59, + 0x74, + 0x63, + 0x65, + 0x0a, + 0x5a, + 0x49, + 0x77, + 0x33, + 0x65, + 0x39, + 0x54, + 0x35, + 0x48, + 0x47, + 0x4e, + 0x71, + 0x55, + 0x6a, + 0x70, + 0x6f, + 0x78, + 0x43, + 0x70, + 0x35, + 0x33, + 0x4b, + 0x63, + 0x44, + 0x75, + 0x38, + 0x69, + 0x47, + 0x46, + 0x36, + 0x43, + 0x51, + 0x71, + 0x4a, + 0x4d, + 0x46, + 0x72, + 0x6b, + 0x38, + 0x47, + 0x6b, + 0x56, + 0x51, + 0x55, + 0x38, + 0x31, + 0x71, + 0x58, + 0x75, + 0x57, + 0x6f, + 0x41, + 0x50, + 0x32, + 0x79, + 0x4a, + 0x66, + 0x51, + 0x53, + 0x6d, + 0x42, + 0x75, + 0x68, + 0x67, + 0x0a, + 0x50, + 0x64, + 0x48, + 0x4b, + 0x55, + 0x4b, + 0x31, + 0x68, + 0x47, + 0x31, + 0x6f, + 0x36, + 0x32, + 0x44, + 0x48, + 0x32, + 0x67, + 0x57, + 0x33, + 0x65, + 0x6e, + 0x37, + 0x6a, + 0x33, + 0x70, + 0x78, + 0x43, + 0x70, + 0x34, + 0x4e, + 0x68, + 0x70, + 0x39, + 0x49, + 0x70, + 0x2f, + 0x35, + 0x39, + 0x67, + 0x30, + 0x4c, + 0x73, + 0x50, + 0x44, + 0x42, + 0x67, + 0x36, + 0x6e, + 0x59, + 0x69, + 0x38, + 0x78, + 0x35, + 0x61, + 0x44, + 0x4e, + 0x6f, + 0x59, + 0x6d, + 0x76, + 0x45, + 0x56, + 0x45, + 0x41, + 0x0a, + 0x35, + 0x46, + 0x52, + 0x7a, + 0x37, + 0x75, + 0x2f, + 0x77, + 0x77, + 0x67, + 0x79, + 0x6d, + 0x47, + 0x49, + 0x50, + 0x67, + 0x57, + 0x70, + 0x59, + 0x47, + 0x33, + 0x63, + 0x35, + 0x6b, + 0x73, + 0x31, + 0x6e, + 0x51, + 0x41, + 0x5a, + 0x47, + 0x53, + 0x61, + 0x37, + 0x64, + 0x4c, + 0x46, + 0x74, + 0x44, + 0x4d, + 0x45, + 0x59, + 0x4f, + 0x78, + 0x34, + 0x37, + 0x36, + 0x77, + 0x42, + 0x75, + 0x48, + 0x41, + 0x56, + 0x53, + 0x57, + 0x53, + 0x41, + 0x70, + 0x64, + 0x55, + 0x4d, + 0x38, + 0x56, + 0x77, + 0x0a, + 0x6b, + 0x2f, + 0x6b, + 0x63, + 0x66, + 0x2f, + 0x76, + 0x61, + 0x6f, + 0x33, + 0x4f, + 0x7a, + 0x71, + 0x65, + 0x33, + 0x6f, + 0x73, + 0x37, + 0x37, + 0x43, + 0x6f, + 0x46, + 0x52, + 0x77, + 0x5a, + 0x4d, + 0x4e, + 0x61, + 0x7a, + 0x2f, + 0x46, + 0x2f, + 0x7a, + 0x35, + 0x53, + 0x77, + 0x63, + 0x47, + 0x77, + 0x42, + 0x6f, + 0x65, + 0x34, + 0x51, + 0x67, + 0x69, + 0x42, + 0x6f, + 0x33, + 0x39, + 0x4b, + 0x58, + 0x52, + 0x33, + 0x5a, + 0x63, + 0x57, + 0x33, + 0x48, + 0x63, + 0x68, + 0x76, + 0x7a, + 0x7a, + 0x0a, + 0x46, + 0x38, + 0x54, + 0x36, + 0x6d, + 0x38, + 0x30, + 0x78, + 0x61, + 0x7a, + 0x67, + 0x54, + 0x6a, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x45, + 0x41, + 0x77, + 0x2f, + 0x66, + 0x55, + 0x62, + 0x53, + 0x78, + 0x69, + 0x6b, + 0x33, + 0x78, + 0x38, + 0x5a, + 0x70, + 0x59, + 0x50, + 0x71, + 0x53, + 0x4a, + 0x5a, + 0x6f, + 0x62, + 0x30, + 0x58, + 0x76, + 0x4b, + 0x6b, + 0x74, + 0x67, + 0x37, + 0x52, + 0x35, + 0x44, + 0x4e, + 0x32, + 0x57, + 0x2f, + 0x65, + 0x4c, + 0x6e, + 0x46, + 0x34, + 0x63, + 0x55, + 0x0a, + 0x45, + 0x33, + 0x70, + 0x2b, + 0x38, + 0x5a, + 0x33, + 0x38, + 0x4c, + 0x57, + 0x76, + 0x37, + 0x6c, + 0x36, + 0x72, + 0x72, + 0x4e, + 0x47, + 0x4d, + 0x41, + 0x4e, + 0x48, + 0x6a, + 0x59, + 0x44, + 0x47, + 0x53, + 0x5a, + 0x2f, + 0x67, + 0x79, + 0x2b, + 0x48, + 0x31, + 0x6f, + 0x75, + 0x38, + 0x69, + 0x72, + 0x54, + 0x2b, + 0x55, + 0x64, + 0x42, + 0x6c, + 0x6c, + 0x58, + 0x4b, + 0x70, + 0x71, + 0x44, + 0x35, + 0x39, + 0x5a, + 0x56, + 0x55, + 0x73, + 0x2b, + 0x6f, + 0x76, + 0x4e, + 0x73, + 0x7a, + 0x52, + 0x0a, + 0x68, + 0x50, + 0x37, + 0x77, + 0x6c, + 0x4c, + 0x4d, + 0x30, + 0x42, + 0x67, + 0x45, + 0x44, + 0x58, + 0x33, + 0x6b, + 0x6e, + 0x36, + 0x78, + 0x52, + 0x41, + 0x63, + 0x5a, + 0x2b, + 0x74, + 0x6b, + 0x59, + 0x53, + 0x64, + 0x57, + 0x79, + 0x63, + 0x6f, + 0x2b, + 0x5a, + 0x4b, + 0x4f, + 0x48, + 0x63, + 0x66, + 0x71, + 0x75, + 0x54, + 0x2f, + 0x72, + 0x4a, + 0x34, + 0x4b, + 0x35, + 0x42, + 0x43, + 0x65, + 0x75, + 0x33, + 0x31, + 0x48, + 0x6f, + 0x78, + 0x74, + 0x51, + 0x6c, + 0x78, + 0x4a, + 0x44, + 0x51, + 0x0a, + 0x52, + 0x36, + 0x4b, + 0x6a, + 0x42, + 0x4e, + 0x2b, + 0x61, + 0x63, + 0x37, + 0x63, + 0x33, + 0x6c, + 0x32, + 0x57, + 0x52, + 0x47, + 0x7a, + 0x2f, + 0x70, + 0x4b, + 0x6a, + 0x30, + 0x47, + 0x39, + 0x35, + 0x32, + 0x71, + 0x42, + 0x43, + 0x72, + 0x58, + 0x4c, + 0x71, + 0x32, + 0x32, + 0x30, + 0x54, + 0x4e, + 0x71, + 0x44, + 0x4b, + 0x44, + 0x44, + 0x4c, + 0x50, + 0x54, + 0x32, + 0x5a, + 0x46, + 0x41, + 0x6b, + 0x65, + 0x48, + 0x77, + 0x7a, + 0x50, + 0x58, + 0x32, + 0x65, + 0x65, + 0x71, + 0x53, + 0x75, + 0x0a, + 0x62, + 0x34, + 0x6b, + 0x48, + 0x45, + 0x49, + 0x44, + 0x7a, + 0x72, + 0x66, + 0x4d, + 0x55, + 0x44, + 0x35, + 0x63, + 0x6d, + 0x54, + 0x73, + 0x42, + 0x50, + 0x71, + 0x30, + 0x4c, + 0x59, + 0x75, + 0x6c, + 0x67, + 0x75, + 0x42, + 0x59, + 0x78, + 0x5a, + 0x57, + 0x73, + 0x50, + 0x63, + 0x6d, + 0x2b, + 0x66, + 0x57, + 0x63, + 0x53, + 0x6e, + 0x77, + 0x39, + 0x73, + 0x32, + 0x4c, + 0x2b, + 0x45, + 0x4c, + 0x32, + 0x37, + 0x52, + 0x39, + 0x39, + 0x49, + 0x67, + 0x7a, + 0x43, + 0x76, + 0x64, + 0x47, + 0x49, + 0x0a, + 0x71, + 0x52, + 0x45, + 0x75, + 0x2f, + 0x69, + 0x65, + 0x5a, + 0x66, + 0x79, + 0x6d, + 0x31, + 0x6e, + 0x67, + 0x65, + 0x58, + 0x33, + 0x56, + 0x70, + 0x73, + 0x50, + 0x4e, + 0x54, + 0x36, + 0x77, + 0x43, + 0x55, + 0x58, + 0x4c, + 0x61, + 0x63, + 0x70, + 0x79, + 0x4a, + 0x46, + 0x6d, + 0x59, + 0x57, + 0x71, + 0x65, + 0x2f, + 0x77, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x42, + 0x44, + 0x75, + 0x49, + 0x53, + 0x6a, + 0x7a, + 0x4c, + 0x4f, + 0x30, + 0x49, + 0x74, + 0x36, + 0x79, + 0x53, + 0x56, + 0x75, + 0x66, + 0x0a, + 0x36, + 0x63, + 0x5a, + 0x70, + 0x79, + 0x50, + 0x6a, + 0x4b, + 0x46, + 0x64, + 0x4d, + 0x71, + 0x4f, + 0x76, + 0x5a, + 0x6d, + 0x79, + 0x39, + 0x4b, + 0x55, + 0x76, + 0x7a, + 0x67, + 0x41, + 0x54, + 0x73, + 0x65, + 0x65, + 0x69, + 0x6c, + 0x70, + 0x64, + 0x51, + 0x6d, + 0x67, + 0x55, + 0x4f, + 0x4e, + 0x65, + 0x50, + 0x5a, + 0x4e, + 0x71, + 0x59, + 0x2b, + 0x4e, + 0x53, + 0x75, + 0x42, + 0x5a, + 0x51, + 0x2f, + 0x46, + 0x71, + 0x44, + 0x31, + 0x2f, + 0x32, + 0x4a, + 0x66, + 0x31, + 0x35, + 0x47, + 0x43, + 0x0a, + 0x43, + 0x79, + 0x42, + 0x76, + 0x41, + 0x73, + 0x59, + 0x4e, + 0x64, + 0x4e, + 0x64, + 0x72, + 0x75, + 0x44, + 0x79, + 0x75, + 0x33, + 0x70, + 0x4a, + 0x35, + 0x5a, + 0x53, + 0x48, + 0x30, + 0x44, + 0x4c, + 0x68, + 0x65, + 0x44, + 0x53, + 0x4a, + 0x51, + 0x34, + 0x61, + 0x72, + 0x69, + 0x2b, + 0x69, + 0x35, + 0x2b, + 0x79, + 0x52, + 0x73, + 0x52, + 0x72, + 0x6b, + 0x42, + 0x37, + 0x4f, + 0x32, + 0x6c, + 0x43, + 0x47, + 0x6f, + 0x71, + 0x48, + 0x52, + 0x53, + 0x43, + 0x38, + 0x44, + 0x4e, + 0x37, + 0x36, + 0x0a, + 0x6a, + 0x78, + 0x74, + 0x6d, + 0x39, + 0x6b, + 0x57, + 0x68, + 0x79, + 0x4c, + 0x31, + 0x73, + 0x61, + 0x67, + 0x6c, + 0x51, + 0x2f, + 0x75, + 0x71, + 0x53, + 0x6f, + 0x77, + 0x65, + 0x66, + 0x57, + 0x33, + 0x62, + 0x50, + 0x59, + 0x59, + 0x62, + 0x7a, + 0x59, + 0x79, + 0x44, + 0x64, + 0x4a, + 0x6e, + 0x49, + 0x46, + 0x37, + 0x78, + 0x7a, + 0x45, + 0x65, + 0x34, + 0x44, + 0x74, + 0x48, + 0x43, + 0x38, + 0x79, + 0x33, + 0x64, + 0x39, + 0x35, + 0x77, + 0x6c, + 0x6f, + 0x6b, + 0x71, + 0x41, + 0x57, + 0x55, + 0x0a, + 0x4e, + 0x4e, + 0x4c, + 0x74, + 0x36, + 0x41, + 0x49, + 0x49, + 0x55, + 0x75, + 0x2f, + 0x74, + 0x75, + 0x7a, + 0x69, + 0x77, + 0x74, + 0x79, + 0x57, + 0x5a, + 0x6b, + 0x56, + 0x6a, + 0x68, + 0x64, + 0x77, + 0x56, + 0x53, + 0x32, + 0x4c, + 0x33, + 0x5a, + 0x59, + 0x6a, + 0x7a, + 0x4b, + 0x7a, + 0x4b, + 0x79, + 0x76, + 0x48, + 0x53, + 0x63, + 0x76, + 0x45, + 0x56, + 0x58, + 0x53, + 0x56, + 0x71, + 0x69, + 0x6d, + 0x50, + 0x52, + 0x45, + 0x2f, + 0x67, + 0x30, + 0x59, + 0x68, + 0x30, + 0x71, + 0x50, + 0x4d, + 0x0a, + 0x54, + 0x6e, + 0x55, + 0x6c, + 0x48, + 0x45, + 0x63, + 0x56, + 0x46, + 0x71, + 0x66, + 0x6e, + 0x66, + 0x5a, + 0x74, + 0x63, + 0x2f, + 0x56, + 0x42, + 0x6b, + 0x7a, + 0x34, + 0x2b, + 0x67, + 0x47, + 0x4e, + 0x61, + 0x36, + 0x4e, + 0x67, + 0x4f, + 0x31, + 0x78, + 0x79, + 0x49, + 0x30, + 0x71, + 0x51, + 0x7a, + 0x6e, + 0x58, + 0x79, + 0x56, + 0x59, + 0x45, + 0x59, + 0x4d, + 0x42, + 0x77, + 0x78, + 0x51, + 0x6a, + 0x46, + 0x38, + 0x47, + 0x5a, + 0x68, + 0x4d, + 0x73, + 0x47, + 0x59, + 0x78, + 0x30, + 0x45, + 0x0a, + 0x4f, + 0x54, + 0x50, + 0x78, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x51, + 0x43, + 0x36, + 0x67, + 0x31, + 0x72, + 0x4c, + 0x32, + 0x59, + 0x32, + 0x73, + 0x76, + 0x37, + 0x4f, + 0x4b, + 0x30, + 0x39, + 0x48, + 0x74, + 0x38, + 0x51, + 0x4b, + 0x4c, + 0x2f, + 0x48, + 0x50, + 0x6d, + 0x48, + 0x4d, + 0x4a, + 0x7a, + 0x38, + 0x73, + 0x57, + 0x76, + 0x61, + 0x41, + 0x74, + 0x34, + 0x63, + 0x6f, + 0x66, + 0x74, + 0x6a, + 0x61, + 0x4f, + 0x65, + 0x38, + 0x6c, + 0x78, + 0x31, + 0x7a, + 0x42, + 0x36, + 0x69, + 0x0a, + 0x67, + 0x48, + 0x37, + 0x42, + 0x6d, + 0x47, + 0x77, + 0x70, + 0x76, + 0x50, + 0x77, + 0x4a, + 0x39, + 0x4f, + 0x58, + 0x7a, + 0x7a, + 0x5a, + 0x2f, + 0x2b, + 0x2b, + 0x74, + 0x50, + 0x4d, + 0x39, + 0x55, + 0x46, + 0x76, + 0x50, + 0x54, + 0x48, + 0x6f, + 0x74, + 0x46, + 0x67, + 0x4a, + 0x4a, + 0x48, + 0x67, + 0x75, + 0x35, + 0x56, + 0x46, + 0x68, + 0x32, + 0x70, + 0x48, + 0x32, + 0x64, + 0x72, + 0x49, + 0x66, + 0x48, + 0x77, + 0x74, + 0x65, + 0x34, + 0x47, + 0x48, + 0x4c, + 0x74, + 0x55, + 0x2b, + 0x54, + 0x0a, + 0x55, + 0x30, + 0x32, + 0x4a, + 0x68, + 0x62, + 0x39, + 0x6e, + 0x62, + 0x79, + 0x76, + 0x79, + 0x75, + 0x4c, + 0x34, + 0x79, + 0x6e, + 0x4a, + 0x78, + 0x44, + 0x6b, + 0x46, + 0x37, + 0x67, + 0x51, + 0x67, + 0x73, + 0x2b, + 0x37, + 0x76, + 0x4a, + 0x4a, + 0x42, + 0x44, + 0x76, + 0x6b, + 0x71, + 0x2f, + 0x63, + 0x35, + 0x72, + 0x43, + 0x63, + 0x30, + 0x35, + 0x7a, + 0x6c, + 0x38, + 0x57, + 0x35, + 0x66, + 0x69, + 0x6f, + 0x4d, + 0x32, + 0x4c, + 0x45, + 0x4a, + 0x44, + 0x46, + 0x5a, + 0x36, + 0x67, + 0x52, + 0x0a, + 0x38, + 0x46, + 0x52, + 0x5a, + 0x70, + 0x4e, + 0x4a, + 0x5a, + 0x74, + 0x42, + 0x64, + 0x51, + 0x47, + 0x79, + 0x74, + 0x52, + 0x61, + 0x74, + 0x37, + 0x51, + 0x2f, + 0x70, + 0x45, + 0x31, + 0x48, + 0x53, + 0x4b, + 0x39, + 0x73, + 0x34, + 0x69, + 0x41, + 0x6f, + 0x6c, + 0x57, + 0x41, + 0x71, + 0x56, + 0x6b, + 0x45, + 0x6d, + 0x6e, + 0x35, + 0x4c, + 0x71, + 0x6a, + 0x48, + 0x2b, + 0x6d, + 0x56, + 0x76, + 0x48, + 0x63, + 0x49, + 0x75, + 0x52, + 0x48, + 0x6b, + 0x4c, + 0x36, + 0x41, + 0x7a, + 0x46, + 0x70, + 0x0a, + 0x34, + 0x73, + 0x75, + 0x36, + 0x46, + 0x75, + 0x72, + 0x66, + 0x30, + 0x77, + 0x74, + 0x41, + 0x66, + 0x65, + 0x76, + 0x57, + 0x56, + 0x32, + 0x7a, + 0x64, + 0x33, + 0x50, + 0x6d, + 0x43, + 0x65, + 0x4a, + 0x32, + 0x30, + 0x53, + 0x52, + 0x43, + 0x42, + 0x50, + 0x69, + 0x44, + 0x59, + 0x56, + 0x55, + 0x4f, + 0x64, + 0x73, + 0x75, + 0x5a, + 0x66, + 0x51, + 0x7a, + 0x51, + 0x4f, + 0x76, + 0x30, + 0x52, + 0x74, + 0x38, + 0x30, + 0x70, + 0x31, + 0x56, + 0x79, + 0x52, + 0x35, + 0x77, + 0x75, + 0x6a, + 0x46, + 0x0a, + 0x37, + 0x63, + 0x57, + 0x73, + 0x74, + 0x5a, + 0x39, + 0x6c, + 0x6a, + 0x74, + 0x55, + 0x72, + 0x68, + 0x4b, + 0x31, + 0x76, + 0x53, + 0x52, + 0x33, + 0x70, + 0x56, + 0x6f, + 0x74, + 0x66, + 0x47, + 0x48, + 0x76, + 0x76, + 0x78, + 0x64, + 0x47, + 0x7a, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x51, + 0x43, + 0x36, + 0x59, + 0x70, + 0x5a, + 0x64, + 0x71, + 0x4d, + 0x38, + 0x7a, + 0x68, + 0x32, + 0x45, + 0x41, + 0x30, + 0x75, + 0x69, + 0x4e, + 0x55, + 0x4f, + 0x55, + 0x2f, + 0x5a, + 0x63, + 0x6d, + 0x54, + 0x0a, + 0x31, + 0x51, + 0x6c, + 0x4b, + 0x67, + 0x2b, + 0x69, + 0x58, + 0x4b, + 0x66, + 0x57, + 0x5a, + 0x72, + 0x2b, + 0x61, + 0x64, + 0x50, + 0x56, + 0x50, + 0x6b, + 0x31, + 0x64, + 0x54, + 0x39, + 0x79, + 0x55, + 0x78, + 0x2f, + 0x6e, + 0x4e, + 0x77, + 0x6e, + 0x66, + 0x43, + 0x62, + 0x4b, + 0x63, + 0x54, + 0x45, + 0x79, + 0x56, + 0x74, + 0x37, + 0x65, + 0x41, + 0x7a, + 0x69, + 0x69, + 0x4c, + 0x73, + 0x5a, + 0x77, + 0x36, + 0x65, + 0x58, + 0x4f, + 0x50, + 0x4b, + 0x39, + 0x55, + 0x38, + 0x7a, + 0x76, + 0x43, + 0x0a, + 0x4a, + 0x50, + 0x55, + 0x71, + 0x67, + 0x67, + 0x44, + 0x5a, + 0x33, + 0x2b, + 0x6b, + 0x62, + 0x6b, + 0x34, + 0x6a, + 0x50, + 0x44, + 0x52, + 0x41, + 0x73, + 0x30, + 0x4a, + 0x74, + 0x49, + 0x6b, + 0x43, + 0x30, + 0x62, + 0x75, + 0x48, + 0x7a, + 0x6b, + 0x58, + 0x46, + 0x30, + 0x72, + 0x67, + 0x41, + 0x34, + 0x62, + 0x4a, + 0x7a, + 0x75, + 0x55, + 0x47, + 0x30, + 0x44, + 0x45, + 0x73, + 0x58, + 0x4a, + 0x58, + 0x59, + 0x71, + 0x48, + 0x6f, + 0x4f, + 0x6d, + 0x31, + 0x4f, + 0x56, + 0x4a, + 0x66, + 0x73, + 0x0a, + 0x78, + 0x6f, + 0x4c, + 0x50, + 0x74, + 0x68, + 0x71, + 0x55, + 0x44, + 0x30, + 0x44, + 0x32, + 0x73, + 0x43, + 0x59, + 0x37, + 0x46, + 0x62, + 0x4a, + 0x58, + 0x67, + 0x4e, + 0x4d, + 0x61, + 0x33, + 0x76, + 0x59, + 0x62, + 0x31, + 0x39, + 0x6b, + 0x78, + 0x65, + 0x4a, + 0x37, + 0x76, + 0x37, + 0x70, + 0x69, + 0x42, + 0x72, + 0x69, + 0x68, + 0x4c, + 0x6b, + 0x35, + 0x4c, + 0x57, + 0x64, + 0x41, + 0x78, + 0x32, + 0x6c, + 0x53, + 0x56, + 0x30, + 0x6c, + 0x61, + 0x46, + 0x45, + 0x39, + 0x4f, + 0x38, + 0x43, + 0x0a, + 0x68, + 0x36, + 0x48, + 0x61, + 0x5a, + 0x65, + 0x52, + 0x39, + 0x4a, + 0x49, + 0x69, + 0x42, + 0x79, + 0x63, + 0x5a, + 0x39, + 0x71, + 0x48, + 0x49, + 0x58, + 0x50, + 0x50, + 0x43, + 0x79, + 0x5a, + 0x57, + 0x51, + 0x4f, + 0x6c, + 0x6c, + 0x43, + 0x49, + 0x56, + 0x69, + 0x31, + 0x71, + 0x53, + 0x51, + 0x6b, + 0x70, + 0x51, + 0x4e, + 0x34, + 0x76, + 0x59, + 0x4e, + 0x68, + 0x63, + 0x2f, + 0x49, + 0x45, + 0x37, + 0x4d, + 0x76, + 0x51, + 0x31, + 0x4a, + 0x42, + 0x61, + 0x4f, + 0x62, + 0x6c, + 0x4a, + 0x48, + 0x0a, + 0x63, + 0x73, + 0x63, + 0x49, + 0x4c, + 0x43, + 0x38, + 0x61, + 0x69, + 0x4a, + 0x56, + 0x46, + 0x4f, + 0x53, + 0x5a, + 0x6e, + 0x4e, + 0x6a, + 0x4b, + 0x2f, + 0x66, + 0x6c, + 0x6f, + 0x72, + 0x78, + 0x74, + 0x54, + 0x4f, + 0x44, + 0x44, + 0x39, + 0x49, + 0x47, + 0x33, + 0x67, + 0x48, + 0x66, + 0x78, + 0x54, + 0x52, + 0x31, + 0x73, + 0x66, + 0x61, + 0x4c, + 0x76, + 0x35, + 0x7a, + 0x43, + 0x51, + 0x66, + 0x39, + 0x77, + 0x4f, + 0x49, + 0x35, + 0x52, + 0x49, + 0x2f, + 0x78, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char node_key_der[] = { + 0x30, 0x82, 0x09, 0x29, 0x02, 0x01, 0x00, 0x02, 0x82, 0x02, 0x01, 0x00, + 0xAF, 0x04, 0xB2, 0x36, 0x78, 0x5F, 0xBC, 0x38, 0x9C, 0x18, 0x93, 0x81, + 0x7D, 0x44, 0x0B, 0x18, 0x4A, 0x4A, 0x0B, 0x6F, 0xCD, 0xDB, 0x94, 0xE8, + 0xD8, 0xA7, 0xFA, 0xEB, 0x39, 0x03, 0x6F, 0x79, 0xB8, 0x20, 0xFC, 0x0B, + 0x71, 0xCC, 0x46, 0x58, 0x75, 0xAE, 0x7B, 0x18, 0xA9, 0x9A, 0xF0, 0x30, + 0x17, 0xE5, 0xF6, 0xE8, 0x51, 0xB3, 0xE5, 0xF2, 0x5D, 0xAB, 0xC1, 0x61, + 0xC2, 0x5A, 0xBE, 0x9E, 0xA8, 0xF8, 0x3E, 0x19, 0x7F, 0x43, 0xEC, 0x14, + 0x06, 0x54, 0x3E, 0x5C, 0xC8, 0xEA, 0x22, 0x9C, 0x32, 0xCE, 0x96, 0x66, + 0xE0, 0x79, 0xBE, 0x38, 0xE0, 0x8A, 0x7E, 0xCC, 0x97, 0x76, 0x62, 0xCB, + 0xA4, 0x5C, 0x95, 0x93, 0x8A, 0xEB, 0xD8, 0x64, 0x38, 0x28, 0x54, 0x90, + 0x5D, 0x05, 0xF2, 0x67, 0x1C, 0x5B, 0x34, 0x57, 0x60, 0x9C, 0x2A, 0xEF, + 0x9A, 0xD9, 0x08, 0x05, 0x06, 0xEF, 0x9A, 0x70, 0x72, 0x3F, 0xEC, 0x27, + 0x7B, 0x38, 0x54, 0x40, 0xA2, 0xE4, 0x99, 0x03, 0xF6, 0x4A, 0x3E, 0xDE, + 0x47, 0x32, 0xE8, 0xC9, 0x7D, 0xAA, 0x00, 0x8B, 0x82, 0x15, 0x86, 0xD6, + 0x03, 0xA0, 0xD6, 0x4B, 0xD3, 0xCB, 0x56, 0xE8, 0x2A, 0x4A, 0xF2, 0xDE, + 0x42, 0xAC, 0xF5, 0xB6, 0x9D, 0x9F, 0x66, 0x98, 0x41, 0x76, 0x37, 0xC8, + 0x95, 0xE0, 0xC1, 0xBB, 0x36, 0x20, 0x07, 0x46, 0x9C, 0xF7, 0x20, 0xC7, + 0xD6, 0x67, 0x9E, 0x40, 0xE0, 0x8D, 0x3D, 0xAC, 0xD1, 0x01, 0xC5, 0x96, + 0xC2, 0xEF, 0xA8, 0x55, 0x69, 0x84, 0x04, 0x49, 0x0F, 0x0C, 0x8D, 0x69, + 0x86, 0xE9, 0xC3, 0xF7, 0x7D, 0xC6, 0xD5, 0x17, 0x16, 0x76, 0x64, 0x14, + 0xAF, 0xB9, 0x07, 0x78, 0xF3, 0x0F, 0xC3, 0x9A, 0x77, 0x07, 0x3F, 0xAA, + 0x8E, 0xA6, 0x87, 0xFB, 0xAA, 0x02, 0x4E, 0x16, 0x42, 0xA3, 0xE6, 0x14, + 0xF6, 0x14, 0x3F, 0xE3, 0xE1, 0xF0, 0x30, 0xDC, 0xBC, 0x42, 0xCC, 0xA3, + 0x6A, 0x67, 0xFB, 0xD7, 0x3E, 0x60, 0xE0, 0x28, 0x5E, 0xDB, 0x84, 0x0F, + 0x7D, 0xC9, 0xF4, 0xDD, 0x6C, 0xBF, 0x65, 0x16, 0xAE, 0x4A, 0x0B, 0x10, + 0x9F, 0x2C, 0xE1, 0xAD, 0x9C, 0xFD, 0x58, 0x9E, 0x7E, 0xF3, 0x05, 0x2A, + 0x8F, 0x8E, 0x10, 0xF0, 0x4E, 0xA9, 0x61, 0x12, 0xF4, 0x87, 0x8C, 0x5E, + 0x22, 0xC1, 0x0E, 0xBF, 0xE2, 0x30, 0x35, 0xB5, 0x58, 0x9B, 0xC5, 0xB5, + 0x6C, 0x76, 0x5B, 0x75, 0x6D, 0x52, 0x78, 0xCC, 0x29, 0xB4, 0x46, 0xFB, + 0x18, 0xF8, 0x14, 0x68, 0xEF, 0xF2, 0x27, 0x3F, 0xE1, 0x2A, 0xA7, 0x2B, + 0xA4, 0x82, 0x7B, 0xFC, 0x8A, 0x1A, 0x01, 0x32, 0xBE, 0x8F, 0xDA, 0x7C, + 0xD9, 0xBD, 0xD0, 0x8B, 0x6F, 0xD4, 0x4E, 0x9A, 0x88, 0x0C, 0x88, 0x32, + 0xC9, 0x90, 0x0B, 0x52, 0x4E, 0xA1, 0x58, 0x7F, 0x7B, 0x4A, 0x83, 0x1A, + 0xAA, 0x48, 0x54, 0x8E, 0xC0, 0xFC, 0xEB, 0x33, 0xF6, 0xD4, 0xA1, 0xC4, + 0x37, 0x91, 0xB9, 0x91, 0x7D, 0xFA, 0x03, 0x0C, 0x9F, 0x48, 0x40, 0x26, + 0x46, 0xF9, 0x47, 0x73, 0x31, 0xA3, 0xC5, 0xFA, 0x90, 0x3C, 0x1A, 0xB3, + 0x31, 0xC7, 0xBD, 0x15, 0x2C, 0x7D, 0x37, 0xE4, 0x7D, 0x6F, 0x0F, 0xD5, + 0x06, 0xC7, 0xC2, 0x73, 0x31, 0x79, 0x2C, 0xA9, 0xBB, 0x46, 0x88, 0xC7, + 0x0F, 0x9C, 0xF0, 0x39, 0xB3, 0xB7, 0xE9, 0x6C, 0xED, 0xB8, 0xF6, 0x9B, + 0x6E, 0x87, 0xF3, 0xD2, 0x61, 0x62, 0x57, 0x2B, 0x85, 0xDD, 0xC3, 0x9B, + 0xFE, 0xDA, 0x86, 0x55, 0x5F, 0x77, 0x9C, 0x8A, 0xC0, 0x05, 0xBC, 0xBF, + 0x0D, 0xB0, 0x5B, 0x21, 0x02, 0x57, 0x2E, 0xCB, 0x2D, 0x9E, 0x9C, 0x29, + 0x95, 0x96, 0xDA, 0x66, 0x46, 0x2A, 0x3E, 0xF3, 0xBB, 0x19, 0x15, 0x7B, + 0xD0, 0x7F, 0x30, 0x34, 0xEE, 0x4E, 0x7F, 0x73, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x02, 0x00, 0x65, 0xC7, 0x75, 0x5B, 0xF7, 0xE4, 0xB4, + 0xB0, 0xB7, 0x16, 0x13, 0xB1, 0xDA, 0x07, 0x17, 0xEC, 0xA8, 0x47, 0x27, + 0x1F, 0x7D, 0xB7, 0x94, 0x81, 0x54, 0x7D, 0x5B, 0x8E, 0x2F, 0x39, 0xB0, + 0x3A, 0x38, 0xB9, 0xF5, 0xD9, 0x31, 0xF4, 0x79, 0x74, 0x37, 0xFB, 0x9F, + 0xDA, 0x57, 0xC7, 0xA2, 0xA6, 0x55, 0x3F, 0x86, 0xB6, 0xD3, 0xCC, 0xAA, + 0x8C, 0xA7, 0xAC, 0x9A, 0x69, 0x1F, 0x7D, 0x66, 0x1D, 0x1E, 0x66, 0x3E, + 0xB6, 0xD5, 0x2B, 0xCA, 0xBE, 0x5A, 0x6F, 0xE3, 0x54, 0x52, 0x02, 0xD6, + 0xDD, 0xBF, 0x2E, 0x24, 0x88, 0xEF, 0x41, 0xDD, 0x3D, 0x76, 0x57, 0x14, + 0x26, 0x26, 0x6E, 0xD2, 0x45, 0x5A, 0xEF, 0xFC, 0x8C, 0x30, 0xDD, 0xB6, + 0x99, 0x91, 0x8E, 0xE2, 0x9E, 0x91, 0x7A, 0x43, 0xDD, 0x72, 0xC0, 0x17, + 0x50, 0x38, 0xFE, 0x98, 0x79, 0x7E, 0xB3, 0x77, 0xD6, 0x74, 0x92, 0x22, + 0x5E, 0x02, 0x12, 0xEA, 0x62, 0x8B, 0x65, 0xD6, 0x99, 0x40, 0x21, 0x70, + 0x5A, 0xBD, 0x7F, 0xAF, 0xCF, 0xD3, 0xD7, 0xE7, 0x48, 0x45, 0xAB, 0xF7, + 0x65, 0x29, 0xE1, 0x1A, 0xE8, 0x9A, 0x73, 0x8E, 0x2B, 0xCA, 0x9F, 0x15, + 0x8B, 0x2E, 0x0D, 0x27, 0xEF, 0xD9, 0x16, 0x7C, 0x4E, 0x16, 0x9B, 0xFC, + 0x2D, 0xCE, 0xE3, 0xD0, 0xCC, 0xF5, 0x5C, 0x1F, 0x4D, 0x9F, 0xCC, 0x40, + 0x6E, 0xF0, 0x19, 0x47, 0x16, 0xEF, 0xB8, 0x92, 0x6F, 0x68, 0x26, 0xBB, + 0x17, 0x24, 0xF4, 0xC2, 0xD3, 0x49, 0xC9, 0xD5, 0x3A, 0xD6, 0x81, 0xCF, + 0x7C, 0x2D, 0x24, 0x3F, 0x98, 0x7E, 0xBC, 0xB3, 0x19, 0xDA, 0x2B, 0x27, + 0x00, 0xA6, 0x2F, 0x73, 0x44, 0x14, 0x6C, 0xA5, 0xCB, 0xD0, 0x8D, 0xFA, + 0x52, 0x79, 0xA5, 0xDD, 0x80, 0x72, 0x34, 0xA6, 0x1F, 0xD4, 0x5E, 0x42, + 0xCA, 0xEF, 0x36, 0x21, 0x37, 0x4F, 0x63, 0xB7, 0x42, 0xF7, 0xD5, 0x28, + 0x9F, 0xF1, 0xB9, 0x7D, 0x15, 0x15, 0x9D, 0xA9, 0x97, 0xE0, 0x0F, 0x8D, + 0x6D, 0xF1, 0x84, 0x86, 0x52, 0x76, 0x5D, 0x75, 0x10, 0x5D, 0x38, 0xA1, + 0x03, 0x01, 0x6E, 0x64, 0x7A, 0xD3, 0x1C, 0x02, 0x57, 0xF5, 0xDA, 0x30, + 0xE0, 0xB4, 0x10, 0xB8, 0x88, 0x43, 0x3C, 0xE6, 0x14, 0x37, 0x36, 0x68, + 0xE1, 0x6D, 0x38, 0x0C, 0x32, 0x7E, 0x6E, 0xE7, 0xF8, 0x83, 0x3A, 0x4D, + 0x87, 0x5F, 0x58, 0x45, 0xE0, 0x43, 0xBB, 0x38, 0x37, 0x09, 0x36, 0xD8, + 0x2B, 0xC4, 0x44, 0x9D, 0x7B, 0xCC, 0x63, 0x82, 0x83, 0xE9, 0xC8, 0x8C, + 0x3A, 0xB6, 0xB1, 0x25, 0x2D, 0x42, 0x22, 0x68, 0x12, 0xA6, 0x15, 0x69, + 0x3F, 0xB4, 0xF0, 0x38, 0xE9, 0x5B, 0xBB, 0x18, 0xFD, 0xBF, 0x51, 0x1C, + 0x4C, 0x0C, 0x78, 0x22, 0x1D, 0x7C, 0xAA, 0xBA, 0xA5, 0xEB, 0x6D, 0x95, + 0xE0, 0x4A, 0x78, 0x95, 0xBF, 0xC9, 0xC5, 0x69, 0xD7, 0x34, 0x7C, 0x3F, + 0xCF, 0x67, 0x69, 0x74, 0xC5, 0x24, 0x24, 0x97, 0xF7, 0x96, 0x69, 0xFE, + 0xFF, 0x86, 0x55, 0xFA, 0x03, 0x41, 0xB4, 0x1C, 0xAA, 0x2D, 0x12, 0x0F, + 0x4C, 0x6D, 0xA5, 0xEC, 0xEE, 0x14, 0x9D, 0x1F, 0xF6, 0x8A, 0xEA, 0xB1, + 0xD3, 0x02, 0xA9, 0x63, 0xB4, 0xE1, 0xE0, 0x87, 0xD7, 0x8D, 0x25, 0x56, + 0xEA, 0xC3, 0x9C, 0xF8, 0x9C, 0x3B, 0x77, 0x98, 0x6C, 0x57, 0xFC, 0xFB, + 0x01, 0x44, 0x44, 0x10, 0x9E, 0x16, 0x5F, 0xA3, 0x4D, 0xA4, 0x1C, 0x10, + 0x64, 0xDD, 0x95, 0xC4, 0x7D, 0x3B, 0x23, 0x1C, 0xAF, 0x93, 0x76, 0x50, + 0x5E, 0x05, 0x50, 0x5C, 0xD8, 0x6B, 0xC8, 0xD0, 0x88, 0x5A, 0x9B, 0x7F, + 0x7D, 0x68, 0xAF, 0xB8, 0xF0, 0xB3, 0xC2, 0xDD, 0x94, 0xB4, 0xEA, 0xE9, + 0xFF, 0x95, 0x82, 0xE7, 0xBC, 0x35, 0x79, 0xE3, 0x2C, 0x5D, 0xC8, 0x6B, + 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE4, 0xA1, 0xF3, 0x2F, 0x8B, 0xD6, + 0x05, 0x77, 0x9F, 0xA3, 0xAF, 0xCA, 0x87, 0x02, 0x09, 0x03, 0xBC, 0xB9, + 0x4D, 0x28, 0x00, 0x6C, 0x38, 0x58, 0xA5, 0x08, 0xB8, 0xD8, 0xB9, 0x6D, + 0xDF, 0xB3, 0x4C, 0x9C, 0x97, 0x32, 0x29, 0x35, 0x66, 0xCB, 0x1A, 0x5F, + 0x60, 0x6F, 0x1E, 0x9E, 0x9D, 0x45, 0xAB, 0x34, 0xD5, 0x62, 0xD7, 0x1E, + 0x64, 0x8C, 0x37, 0x7B, 0xD4, 0xF9, 0x1C, 0x63, 0x6A, 0x52, 0x3A, 0x68, + 0xC4, 0x2A, 0x79, 0xDC, 0xA7, 0x03, 0xBB, 0xC8, 0x86, 0x17, 0xA0, 0x90, + 0xA8, 0x93, 0x05, 0xAE, 0x4F, 0x06, 0x91, 0x54, 0x14, 0xF3, 0x5A, 0x97, + 0xB9, 0x6A, 0x00, 0x3F, 0x6C, 0x89, 0x7D, 0x04, 0xA6, 0x06, 0xE8, 0x60, + 0x3D, 0xD1, 0xCA, 0x50, 0xAD, 0x61, 0x1B, 0x5A, 0x3A, 0xD8, 0x31, 0xF6, + 0x81, 0x6D, 0xDE, 0x9F, 0xB8, 0xF7, 0xA7, 0x10, 0xA9, 0xE0, 0xD8, 0x69, + 0xF4, 0x8A, 0x7F, 0xE7, 0xD8, 0x34, 0x2E, 0xC3, 0xC3, 0x06, 0x0E, 0xA7, + 0x62, 0x2F, 0x31, 0xE5, 0xA0, 0xCD, 0xA1, 0x89, 0xAF, 0x11, 0x51, 0x00, + 0xE4, 0x54, 0x73, 0xEE, 0xEF, 0xF0, 0xC2, 0x0C, 0xA6, 0x18, 0x83, 0xE0, + 0x5A, 0x96, 0x06, 0xDD, 0xCE, 0x64, 0xB3, 0x59, 0xD0, 0x01, 0x91, 0x92, + 0x6B, 0xB7, 0x4B, 0x16, 0xD0, 0xCC, 0x11, 0x83, 0xB1, 0xE3, 0xBE, 0xB0, + 0x06, 0xE1, 0xC0, 0x55, 0x25, 0x92, 0x02, 0x97, 0x54, 0x33, 0xC5, 0x70, + 0x93, 0xF9, 0x1C, 0x7F, 0xFB, 0xDA, 0xA3, 0x73, 0xB3, 0xA9, 0xED, 0xE8, + 0xB3, 0xBE, 0xC2, 0xA0, 0x54, 0x70, 0x64, 0xC3, 0x5A, 0xCF, 0xF1, 0x7F, + 0xCF, 0x94, 0xB0, 0x70, 0x6C, 0x01, 0xA1, 0xEE, 0x10, 0x82, 0x20, 0x68, + 0xDF, 0xD2, 0x97, 0x47, 0x76, 0x5C, 0x5B, 0x71, 0xDC, 0x86, 0xFC, 0xF3, + 0x17, 0xC4, 0xFA, 0x9B, 0xCD, 0x31, 0x6B, 0x38, 0x13, 0x8D, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xC3, 0xF7, 0xD4, 0x6D, 0x2C, 0x62, 0x93, 0x7C, 0x7C, + 0x66, 0x96, 0x0F, 0xA9, 0x22, 0x59, 0xA1, 0xBD, 0x17, 0xBC, 0xA9, 0x2D, + 0x83, 0xB4, 0x79, 0x0C, 0xDD, 0x96, 0xFD, 0xE2, 0xE7, 0x17, 0x87, 0x14, + 0x13, 0x7A, 0x7E, 0xF1, 0x9D, 0xFC, 0x2D, 0x6B, 0xFB, 0x97, 0xAA, 0xEB, + 0x34, 0x63, 0x00, 0x34, 0x78, 0xD8, 0x0C, 0x64, 0x99, 0xFE, 0x0C, 0xBE, + 0x1F, 0x5A, 0x2E, 0xF2, 0x2A, 0xD3, 0xF9, 0x47, 0x41, 0x96, 0x55, 0xCA, + 0xA6, 0xA0, 0xF9, 0xF5, 0x95, 0x54, 0xB3, 0xEA, 0x2F, 0x36, 0xCC, 0xD1, + 0x84, 0xFE, 0xF0, 0x94, 0xB3, 0x34, 0x06, 0x01, 0x03, 0x5F, 0x79, 0x27, + 0xEB, 0x14, 0x40, 0x71, 0x9F, 0xAD, 0x91, 0x84, 0x9D, 0x5B, 0x27, 0x28, + 0xF9, 0x92, 0x8E, 0x1D, 0xC7, 0xEA, 0xB9, 0x3F, 0xEB, 0x27, 0x82, 0xB9, + 0x04, 0x27, 0xAE, 0xDF, 0x51, 0xE8, 0xC6, 0xD4, 0x25, 0xC4, 0x90, 0xD0, + 0x47, 0xA2, 0xA3, 0x04, 0xDF, 0x9A, 0x73, 0xB7, 0x37, 0x97, 0x65, 0x91, + 0x1B, 0x3F, 0xE9, 0x2A, 0x3D, 0x06, 0xF7, 0x9D, 0xAA, 0x04, 0x2A, 0xD7, + 0x2E, 0xAD, 0xB6, 0xD1, 0x33, 0x6A, 0x0C, 0xA0, 0xC3, 0x2C, 0xF4, 0xF6, + 0x64, 0x50, 0x24, 0x78, 0x7C, 0x33, 0x3D, 0x7D, 0x9E, 0x7A, 0xA4, 0xAE, + 0x6F, 0x89, 0x07, 0x10, 0x80, 0xF3, 0xAD, 0xF3, 0x14, 0x0F, 0x97, 0x26, + 0x4E, 0xC0, 0x4F, 0xAB, 0x42, 0xD8, 0xBA, 0x58, 0x2E, 0x05, 0x8C, 0x59, + 0x5A, 0xC3, 0xDC, 0x9B, 0xE7, 0xD6, 0x71, 0x29, 0xF0, 0xF6, 0xCD, 0x8B, + 0xF8, 0x42, 0xF6, 0xED, 0x1F, 0x7D, 0x22, 0x0C, 0xC2, 0xBD, 0xD1, 0x88, + 0xA9, 0x11, 0x2E, 0xFE, 0x27, 0x99, 0x7F, 0x29, 0xB5, 0x9E, 0x07, 0x97, + 0xDD, 0x5A, 0x6C, 0x3C, 0xD4, 0xFA, 0xC0, 0x25, 0x17, 0x2D, 0xA7, 0x29, + 0xC8, 0x91, 0x66, 0x61, 0x6A, 0x9E, 0xFF, 0x02, 0x82, 0x01, 0x00, 0x43, + 0xB8, 0x84, 0xA3, 0xCC, 0xB3, 0xB4, 0x22, 0xDE, 0xB2, 0x49, 0x5B, 0x9F, + 0xE9, 0xC6, 0x69, 0xC8, 0xF8, 0xCA, 0x15, 0xD3, 0x2A, 0x3A, 0xF6, 0x66, + 0xCB, 0xD2, 0x94, 0xBF, 0x38, 0x00, 0x4E, 0xC7, 0x9E, 0x8A, 0x5A, 0x5D, + 0x42, 0x68, 0x14, 0x38, 0xD7, 0x8F, 0x64, 0xDA, 0x98, 0xF8, 0xD4, 0xAE, + 0x05, 0x94, 0x3F, 0x16, 0xA0, 0xF5, 0xFF, 0x62, 0x5F, 0xD7, 0x91, 0x82, + 0x0B, 0x20, 0x6F, 0x02, 0xC6, 0x0D, 0x74, 0xD7, 0x6B, 0xB8, 0x3C, 0xAE, + 0xDE, 0x92, 0x79, 0x65, 0x21, 0xF4, 0x0C, 0xB8, 0x5E, 0x0D, 0x22, 0x50, + 0xE1, 0xAA, 0xE2, 0xFA, 0x2E, 0x7E, 0xC9, 0x1B, 0x11, 0xAE, 0x40, 0x7B, + 0x3B, 0x69, 0x42, 0x1A, 0x8A, 0x87, 0x45, 0x20, 0xBC, 0x0C, 0xDE, 0xFA, + 0x8F, 0x1B, 0x66, 0xF6, 0x45, 0xA1, 0xC8, 0xBD, 0x6C, 0x6A, 0x09, 0x50, + 0xFE, 0xEA, 0x92, 0xA3, 0x07, 0x9F, 0x5B, 0x76, 0xCF, 0x61, 0x86, 0xF3, + 0x63, 0x20, 0xDD, 0x26, 0x72, 0x05, 0xEF, 0x1C, 0xC4, 0x7B, 0x80, 0xED, + 0x1C, 0x2F, 0x32, 0xDD, 0xDF, 0x79, 0xC2, 0x5A, 0x24, 0xA8, 0x05, 0x94, + 0x34, 0xD2, 0xED, 0xE8, 0x02, 0x08, 0x52, 0xEF, 0xED, 0xBB, 0x38, 0xB0, + 0xB7, 0x25, 0x99, 0x91, 0x58, 0xE1, 0x77, 0x05, 0x52, 0xD8, 0xBD, 0xD9, + 0x62, 0x3C, 0xCA, 0xCC, 0xAC, 0xAF, 0x1D, 0x27, 0x2F, 0x11, 0x55, 0xD2, + 0x56, 0xA8, 0xA6, 0x3D, 0x11, 0x3F, 0x83, 0x46, 0x21, 0xD2, 0xA3, 0xCC, + 0x4E, 0x75, 0x25, 0x1C, 0x47, 0x15, 0x16, 0xA7, 0xE7, 0x7D, 0x9B, 0x5C, + 0xFD, 0x50, 0x64, 0xCF, 0x8F, 0xA0, 0x18, 0xD6, 0xBA, 0x36, 0x03, 0xB5, + 0xC7, 0x22, 0x34, 0xA9, 0x0C, 0xE7, 0x5F, 0x25, 0x58, 0x11, 0x83, 0x01, + 0xC3, 0x14, 0x23, 0x17, 0xC1, 0x99, 0x84, 0xCB, 0x06, 0x63, 0x1D, 0x04, + 0x39, 0x33, 0xF1, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x83, 0x5A, 0xCB, + 0xD9, 0x8D, 0xAC, 0xBF, 0xB3, 0x8A, 0xD3, 0xD1, 0xED, 0xF1, 0x02, 0x8B, + 0xFC, 0x73, 0xE6, 0x1C, 0xC2, 0x73, 0xF2, 0xC5, 0xAF, 0x68, 0x0B, 0x78, + 0x72, 0x87, 0xED, 0x8D, 0xA3, 0x9E, 0xF2, 0x5C, 0x75, 0xCC, 0x1E, 0xA2, + 0x80, 0x7E, 0xC1, 0x98, 0x6C, 0x29, 0xBC, 0xFC, 0x09, 0xF4, 0xE5, 0xF3, + 0xCD, 0x9F, 0xFE, 0xFA, 0xD3, 0xCC, 0xF5, 0x41, 0x6F, 0x3D, 0x31, 0xE8, + 0xB4, 0x58, 0x09, 0x24, 0x78, 0x2E, 0xE5, 0x51, 0x61, 0xDA, 0x91, 0xF6, + 0x76, 0xB2, 0x1F, 0x1F, 0x0B, 0x5E, 0xE0, 0x61, 0xCB, 0xB5, 0x4F, 0x93, + 0x53, 0x4D, 0x89, 0x85, 0xBF, 0x67, 0x6F, 0x2B, 0xF2, 0xB8, 0xBE, 0x32, + 0x9C, 0x9C, 0x43, 0x90, 0x5E, 0xE0, 0x42, 0x0B, 0x3E, 0xEE, 0xF2, 0x49, + 0x04, 0x3B, 0xE4, 0xAB, 0xF7, 0x39, 0xAC, 0x27, 0x34, 0xE7, 0x39, 0x7C, + 0x5B, 0x97, 0xE2, 0xA0, 0xCD, 0x8B, 0x10, 0x90, 0xC5, 0x67, 0xA8, 0x11, + 0xF0, 0x54, 0x59, 0xA4, 0xD2, 0x59, 0xB4, 0x17, 0x50, 0x1B, 0x2B, 0x51, + 0x6A, 0xDE, 0xD0, 0xFE, 0x91, 0x35, 0x1D, 0x22, 0xBD, 0xB3, 0x88, 0x80, + 0xA2, 0x55, 0x80, 0xA9, 0x59, 0x04, 0x9A, 0x7E, 0x4B, 0xAA, 0x31, 0xFE, + 0x99, 0x5B, 0xC7, 0x70, 0x8B, 0x91, 0x1E, 0x42, 0xFA, 0x03, 0x31, 0x69, + 0xE2, 0xCB, 0xBA, 0x16, 0xEA, 0xDF, 0xD3, 0x0B, 0x40, 0x7D, 0xEB, 0xD6, + 0x57, 0x6C, 0xDD, 0xDC, 0xF9, 0x82, 0x78, 0x9D, 0xB4, 0x49, 0x10, 0x81, + 0x3E, 0x20, 0xD8, 0x55, 0x43, 0x9D, 0xB2, 0xE6, 0x5F, 0x43, 0x34, 0x0E, + 0xBF, 0x44, 0x6D, 0xF3, 0x4A, 0x75, 0x57, 0x24, 0x79, 0xC2, 0xE8, 0xC5, + 0xED, 0xC5, 0xAC, 0xB5, 0x9F, 0x65, 0x8E, 0xD5, 0x2B, 0x84, 0xAD, 0x6F, + 0x49, 0x1D, 0xE9, 0x56, 0x8B, 0x5F, 0x18, 0x7B, 0xEF, 0xC5, 0xD1, 0xB3, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x62, 0x96, 0x5D, 0xA8, 0xCF, 0x33, + 0x87, 0x61, 0x00, 0xD2, 0xE8, 0x8D, 0x50, 0xE5, 0x3F, 0x65, 0xC9, 0x93, + 0xD5, 0x09, 0x4A, 0x83, 0xE8, 0x97, 0x29, 0xF5, 0x99, 0xAF, 0xE6, 0x9D, + 0x3D, 0x53, 0xE4, 0xD5, 0xD4, 0xFD, 0xC9, 0x4C, 0x7F, 0x9C, 0xDC, 0x27, + 0x7C, 0x26, 0xCA, 0x71, 0x31, 0x32, 0x56, 0xDE, 0xDE, 0x03, 0x38, 0xA2, + 0x2E, 0xC6, 0x70, 0xE9, 0xE5, 0xCE, 0x3C, 0xAF, 0x54, 0xF3, 0x3B, 0xC2, + 0x24, 0xF5, 0x2A, 0x82, 0x00, 0xD9, 0xDF, 0xE9, 0x1B, 0x93, 0x88, 0xCF, + 0x0D, 0x10, 0x2C, 0xD0, 0x9B, 0x48, 0x90, 0x2D, 0x1B, 0xB8, 0x7C, 0xE4, + 0x5C, 0x5D, 0x2B, 0x80, 0x0E, 0x1B, 0x27, 0x3B, 0x94, 0x1B, 0x40, 0xC4, + 0xB1, 0x72, 0x57, 0x62, 0xA1, 0xE8, 0x3A, 0x6D, 0x4E, 0x54, 0x97, 0xEC, + 0xC6, 0x82, 0xCF, 0xB6, 0x1A, 0x94, 0x0F, 0x40, 0xF6, 0xB0, 0x26, 0x3B, + 0x15, 0xB2, 0x57, 0x80, 0xD3, 0x1A, 0xDE, 0xF6, 0x1B, 0xD7, 0xD9, 0x31, + 0x78, 0x9E, 0xEF, 0xEE, 0x98, 0x81, 0xAE, 0x28, 0x4B, 0x93, 0x92, 0xD6, + 0x74, 0x0C, 0x76, 0x95, 0x25, 0x74, 0x95, 0xA1, 0x44, 0xF4, 0xEF, 0x02, + 0x87, 0xA1, 0xDA, 0x65, 0xE4, 0x7D, 0x24, 0x88, 0x81, 0xC9, 0xC6, 0x7D, + 0xA8, 0x72, 0x17, 0x3C, 0xF0, 0xB2, 0x65, 0x64, 0x0E, 0x96, 0x50, 0x88, + 0x56, 0x2D, 0x6A, 0x49, 0x09, 0x29, 0x40, 0xDE, 0x2F, 0x60, 0xD8, 0x5C, + 0xFC, 0x81, 0x3B, 0x32, 0xF4, 0x35, 0x24, 0x16, 0x8E, 0x6E, 0x52, 0x47, + 0x72, 0xC7, 0x08, 0x2C, 0x2F, 0x1A, 0x88, 0x95, 0x45, 0x39, 0x26, 0x67, + 0x36, 0x32, 0xBF, 0x7E, 0x5A, 0x2B, 0xC6, 0xD4, 0xCE, 0x0C, 0x3F, 0x48, + 0x1B, 0x78, 0x07, 0x7F, 0x14, 0xD1, 0xD6, 0xC7, 0xDA, 0x2E, 0xFE, 0x73, + 0x09, 0x07, 0xFD, 0xC0, 0xE2, 0x39, 0x44, 0x8F, 0xF1 +}; + +unsigned char node_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x44, + 0x43, + 0x43, + 0x41, + 0x77, + 0x43, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x45, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x78, + 0x4f, + 0x46, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x45, + 0x34, + 0x57, + 0x6a, + 0x42, + 0x4d, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x42, + 0x6d, + 0x4e, + 0x73, + 0x61, + 0x57, + 0x56, + 0x75, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x0a, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4b, + 0x38, + 0x45, + 0x73, + 0x6a, + 0x5a, + 0x34, + 0x58, + 0x37, + 0x77, + 0x34, + 0x6e, + 0x42, + 0x69, + 0x54, + 0x67, + 0x58, + 0x31, + 0x45, + 0x43, + 0x78, + 0x68, + 0x4b, + 0x53, + 0x67, + 0x74, + 0x76, + 0x7a, + 0x64, + 0x75, + 0x55, + 0x36, + 0x4e, + 0x69, + 0x6e, + 0x2b, + 0x75, + 0x73, + 0x35, + 0x0a, + 0x41, + 0x32, + 0x39, + 0x35, + 0x75, + 0x43, + 0x44, + 0x38, + 0x43, + 0x33, + 0x48, + 0x4d, + 0x52, + 0x6c, + 0x68, + 0x31, + 0x72, + 0x6e, + 0x73, + 0x59, + 0x71, + 0x5a, + 0x72, + 0x77, + 0x4d, + 0x42, + 0x66, + 0x6c, + 0x39, + 0x75, + 0x68, + 0x52, + 0x73, + 0x2b, + 0x58, + 0x79, + 0x58, + 0x61, + 0x76, + 0x42, + 0x59, + 0x63, + 0x4a, + 0x61, + 0x76, + 0x70, + 0x36, + 0x6f, + 0x2b, + 0x44, + 0x34, + 0x5a, + 0x66, + 0x30, + 0x50, + 0x73, + 0x46, + 0x41, + 0x5a, + 0x55, + 0x50, + 0x6c, + 0x7a, + 0x49, + 0x0a, + 0x36, + 0x69, + 0x4b, + 0x63, + 0x4d, + 0x73, + 0x36, + 0x57, + 0x5a, + 0x75, + 0x42, + 0x35, + 0x76, + 0x6a, + 0x6a, + 0x67, + 0x69, + 0x6e, + 0x37, + 0x4d, + 0x6c, + 0x33, + 0x5a, + 0x69, + 0x79, + 0x36, + 0x52, + 0x63, + 0x6c, + 0x5a, + 0x4f, + 0x4b, + 0x36, + 0x39, + 0x68, + 0x6b, + 0x4f, + 0x43, + 0x68, + 0x55, + 0x6b, + 0x46, + 0x30, + 0x46, + 0x38, + 0x6d, + 0x63, + 0x63, + 0x57, + 0x7a, + 0x52, + 0x58, + 0x59, + 0x4a, + 0x77, + 0x71, + 0x37, + 0x35, + 0x72, + 0x5a, + 0x43, + 0x41, + 0x55, + 0x47, + 0x0a, + 0x37, + 0x35, + 0x70, + 0x77, + 0x63, + 0x6a, + 0x2f, + 0x73, + 0x4a, + 0x33, + 0x73, + 0x34, + 0x56, + 0x45, + 0x43, + 0x69, + 0x35, + 0x4a, + 0x6b, + 0x44, + 0x39, + 0x6b, + 0x6f, + 0x2b, + 0x33, + 0x6b, + 0x63, + 0x79, + 0x36, + 0x4d, + 0x6c, + 0x39, + 0x71, + 0x67, + 0x43, + 0x4c, + 0x67, + 0x68, + 0x57, + 0x47, + 0x31, + 0x67, + 0x4f, + 0x67, + 0x31, + 0x6b, + 0x76, + 0x54, + 0x79, + 0x31, + 0x62, + 0x6f, + 0x4b, + 0x6b, + 0x72, + 0x79, + 0x33, + 0x6b, + 0x4b, + 0x73, + 0x39, + 0x62, + 0x61, + 0x64, + 0x0a, + 0x6e, + 0x32, + 0x61, + 0x59, + 0x51, + 0x58, + 0x59, + 0x33, + 0x79, + 0x4a, + 0x58, + 0x67, + 0x77, + 0x62, + 0x73, + 0x32, + 0x49, + 0x41, + 0x64, + 0x47, + 0x6e, + 0x50, + 0x63, + 0x67, + 0x78, + 0x39, + 0x5a, + 0x6e, + 0x6e, + 0x6b, + 0x44, + 0x67, + 0x6a, + 0x54, + 0x32, + 0x73, + 0x30, + 0x51, + 0x48, + 0x46, + 0x6c, + 0x73, + 0x4c, + 0x76, + 0x71, + 0x46, + 0x56, + 0x70, + 0x68, + 0x41, + 0x52, + 0x4a, + 0x44, + 0x77, + 0x79, + 0x4e, + 0x61, + 0x59, + 0x62, + 0x70, + 0x77, + 0x2f, + 0x64, + 0x39, + 0x0a, + 0x78, + 0x74, + 0x55, + 0x58, + 0x46, + 0x6e, + 0x5a, + 0x6b, + 0x46, + 0x4b, + 0x2b, + 0x35, + 0x42, + 0x33, + 0x6a, + 0x7a, + 0x44, + 0x38, + 0x4f, + 0x61, + 0x64, + 0x77, + 0x63, + 0x2f, + 0x71, + 0x6f, + 0x36, + 0x6d, + 0x68, + 0x2f, + 0x75, + 0x71, + 0x41, + 0x6b, + 0x34, + 0x57, + 0x51, + 0x71, + 0x50, + 0x6d, + 0x46, + 0x50, + 0x59, + 0x55, + 0x50, + 0x2b, + 0x50, + 0x68, + 0x38, + 0x44, + 0x44, + 0x63, + 0x76, + 0x45, + 0x4c, + 0x4d, + 0x6f, + 0x32, + 0x70, + 0x6e, + 0x2b, + 0x39, + 0x63, + 0x2b, + 0x0a, + 0x59, + 0x4f, + 0x41, + 0x6f, + 0x58, + 0x74, + 0x75, + 0x45, + 0x44, + 0x33, + 0x33, + 0x4a, + 0x39, + 0x4e, + 0x31, + 0x73, + 0x76, + 0x32, + 0x55, + 0x57, + 0x72, + 0x6b, + 0x6f, + 0x4c, + 0x45, + 0x4a, + 0x38, + 0x73, + 0x34, + 0x61, + 0x32, + 0x63, + 0x2f, + 0x56, + 0x69, + 0x65, + 0x66, + 0x76, + 0x4d, + 0x46, + 0x4b, + 0x6f, + 0x2b, + 0x4f, + 0x45, + 0x50, + 0x42, + 0x4f, + 0x71, + 0x57, + 0x45, + 0x53, + 0x39, + 0x49, + 0x65, + 0x4d, + 0x58, + 0x69, + 0x4c, + 0x42, + 0x44, + 0x72, + 0x2f, + 0x69, + 0x0a, + 0x4d, + 0x44, + 0x57, + 0x31, + 0x57, + 0x4a, + 0x76, + 0x46, + 0x74, + 0x57, + 0x78, + 0x32, + 0x57, + 0x33, + 0x56, + 0x74, + 0x55, + 0x6e, + 0x6a, + 0x4d, + 0x4b, + 0x62, + 0x52, + 0x47, + 0x2b, + 0x78, + 0x6a, + 0x34, + 0x46, + 0x47, + 0x6a, + 0x76, + 0x38, + 0x69, + 0x63, + 0x2f, + 0x34, + 0x53, + 0x71, + 0x6e, + 0x4b, + 0x36, + 0x53, + 0x43, + 0x65, + 0x2f, + 0x79, + 0x4b, + 0x47, + 0x67, + 0x45, + 0x79, + 0x76, + 0x6f, + 0x2f, + 0x61, + 0x66, + 0x4e, + 0x6d, + 0x39, + 0x30, + 0x49, + 0x74, + 0x76, + 0x0a, + 0x31, + 0x45, + 0x36, + 0x61, + 0x69, + 0x41, + 0x79, + 0x49, + 0x4d, + 0x73, + 0x6d, + 0x51, + 0x43, + 0x31, + 0x4a, + 0x4f, + 0x6f, + 0x56, + 0x68, + 0x2f, + 0x65, + 0x30, + 0x71, + 0x44, + 0x47, + 0x71, + 0x70, + 0x49, + 0x56, + 0x49, + 0x37, + 0x41, + 0x2f, + 0x4f, + 0x73, + 0x7a, + 0x39, + 0x74, + 0x53, + 0x68, + 0x78, + 0x44, + 0x65, + 0x52, + 0x75, + 0x5a, + 0x46, + 0x39, + 0x2b, + 0x67, + 0x4d, + 0x4d, + 0x6e, + 0x30, + 0x68, + 0x41, + 0x4a, + 0x6b, + 0x62, + 0x35, + 0x52, + 0x33, + 0x4d, + 0x78, + 0x0a, + 0x6f, + 0x38, + 0x58, + 0x36, + 0x6b, + 0x44, + 0x77, + 0x61, + 0x73, + 0x7a, + 0x48, + 0x48, + 0x76, + 0x52, + 0x55, + 0x73, + 0x66, + 0x54, + 0x66, + 0x6b, + 0x66, + 0x57, + 0x38, + 0x50, + 0x31, + 0x51, + 0x62, + 0x48, + 0x77, + 0x6e, + 0x4d, + 0x78, + 0x65, + 0x53, + 0x79, + 0x70, + 0x75, + 0x30, + 0x61, + 0x49, + 0x78, + 0x77, + 0x2b, + 0x63, + 0x38, + 0x44, + 0x6d, + 0x7a, + 0x74, + 0x2b, + 0x6c, + 0x73, + 0x37, + 0x62, + 0x6a, + 0x32, + 0x6d, + 0x32, + 0x36, + 0x48, + 0x38, + 0x39, + 0x4a, + 0x68, + 0x0a, + 0x59, + 0x6c, + 0x63, + 0x72, + 0x68, + 0x64, + 0x33, + 0x44, + 0x6d, + 0x2f, + 0x37, + 0x61, + 0x68, + 0x6c, + 0x56, + 0x66, + 0x64, + 0x35, + 0x79, + 0x4b, + 0x77, + 0x41, + 0x57, + 0x38, + 0x76, + 0x77, + 0x32, + 0x77, + 0x57, + 0x79, + 0x45, + 0x43, + 0x56, + 0x79, + 0x37, + 0x4c, + 0x4c, + 0x5a, + 0x36, + 0x63, + 0x4b, + 0x5a, + 0x57, + 0x57, + 0x32, + 0x6d, + 0x5a, + 0x47, + 0x4b, + 0x6a, + 0x37, + 0x7a, + 0x75, + 0x78, + 0x6b, + 0x56, + 0x65, + 0x39, + 0x42, + 0x2f, + 0x4d, + 0x44, + 0x54, + 0x75, + 0x0a, + 0x54, + 0x6e, + 0x39, + 0x7a, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4d, + 0x4c, + 0x70, + 0x6f, + 0x2b, + 0x53, + 0x58, + 0x43, + 0x46, + 0x2b, + 0x35, + 0x73, + 0x4d, + 0x35, + 0x68, + 0x57, + 0x62, + 0x4a, + 0x6d, + 0x38, + 0x77, + 0x52, + 0x64, + 0x4e, + 0x36, + 0x45, + 0x63, + 0x0a, + 0x4d, + 0x48, + 0x6a, + 0x54, + 0x33, + 0x74, + 0x61, + 0x2b, + 0x7a, + 0x79, + 0x74, + 0x6d, + 0x33, + 0x6f, + 0x79, + 0x71, + 0x59, + 0x63, + 0x53, + 0x2f, + 0x62, + 0x53, + 0x43, + 0x56, + 0x74, + 0x30, + 0x6a, + 0x38, + 0x65, + 0x71, + 0x72, + 0x5a, + 0x54, + 0x33, + 0x73, + 0x63, + 0x43, + 0x77, + 0x77, + 0x69, + 0x77, + 0x53, + 0x79, + 0x32, + 0x73, + 0x37, + 0x5a, + 0x63, + 0x65, + 0x74, + 0x35, + 0x75, + 0x6d, + 0x6b, + 0x46, + 0x55, + 0x48, + 0x62, + 0x36, + 0x4a, + 0x65, + 0x6e, + 0x30, + 0x51, + 0x0a, + 0x6c, + 0x58, + 0x4b, + 0x42, + 0x6f, + 0x61, + 0x53, + 0x47, + 0x54, + 0x57, + 0x42, + 0x4f, + 0x5a, + 0x62, + 0x6b, + 0x69, + 0x55, + 0x36, + 0x4f, + 0x36, + 0x6d, + 0x36, + 0x52, + 0x4c, + 0x4d, + 0x72, + 0x4d, + 0x6d, + 0x62, + 0x70, + 0x32, + 0x6b, + 0x68, + 0x4e, + 0x61, + 0x67, + 0x36, + 0x59, + 0x71, + 0x39, + 0x70, + 0x33, + 0x73, + 0x66, + 0x73, + 0x59, + 0x7a, + 0x38, + 0x69, + 0x45, + 0x31, + 0x38, + 0x7a, + 0x36, + 0x43, + 0x31, + 0x76, + 0x70, + 0x75, + 0x4b, + 0x63, + 0x39, + 0x51, + 0x54, + 0x0a, + 0x50, + 0x51, + 0x4c, + 0x51, + 0x65, + 0x7a, + 0x6c, + 0x68, + 0x77, + 0x5a, + 0x5a, + 0x57, + 0x52, + 0x4e, + 0x77, + 0x6b, + 0x43, + 0x79, + 0x4e, + 0x52, + 0x42, + 0x6a, + 0x69, + 0x39, + 0x4e, + 0x37, + 0x55, + 0x2f, + 0x75, + 0x4f, + 0x52, + 0x4f, + 0x49, + 0x38, + 0x64, + 0x6b, + 0x36, + 0x4b, + 0x48, + 0x6e, + 0x4d, + 0x5a, + 0x52, + 0x32, + 0x69, + 0x52, + 0x34, + 0x65, + 0x43, + 0x53, + 0x42, + 0x32, + 0x67, + 0x74, + 0x6b, + 0x76, + 0x68, + 0x50, + 0x65, + 0x4c, + 0x55, + 0x58, + 0x77, + 0x58, + 0x0a, + 0x38, + 0x4a, + 0x79, + 0x6d, + 0x32, + 0x51, + 0x48, + 0x58, + 0x35, + 0x43, + 0x59, + 0x37, + 0x2f, + 0x59, + 0x4d, + 0x66, + 0x73, + 0x34, + 0x44, + 0x30, + 0x49, + 0x38, + 0x66, + 0x70, + 0x6a, + 0x53, + 0x4f, + 0x65, + 0x65, + 0x52, + 0x59, + 0x33, + 0x63, + 0x6e, + 0x4a, + 0x76, + 0x33, + 0x37, + 0x4c, + 0x75, + 0x51, + 0x4c, + 0x35, + 0x72, + 0x4a, + 0x70, + 0x75, + 0x76, + 0x49, + 0x5a, + 0x78, + 0x46, + 0x4a, + 0x33, + 0x6e, + 0x56, + 0x72, + 0x37, + 0x56, + 0x2b, + 0x44, + 0x54, + 0x4d, + 0x72, + 0x0a, + 0x4e, + 0x71, + 0x4e, + 0x45, + 0x34, + 0x69, + 0x57, + 0x48, + 0x4d, + 0x56, + 0x58, + 0x71, + 0x63, + 0x45, + 0x62, + 0x4d, + 0x53, + 0x2f, + 0x59, + 0x4a, + 0x6a, + 0x38, + 0x34, + 0x69, + 0x63, + 0x47, + 0x65, + 0x31, + 0x70, + 0x43, + 0x55, + 0x30, + 0x31, + 0x65, + 0x49, + 0x2f, + 0x61, + 0x33, + 0x34, + 0x72, + 0x79, + 0x53, + 0x67, + 0x55, + 0x75, + 0x2b, + 0x33, + 0x53, + 0x43, + 0x55, + 0x55, + 0x61, + 0x58, + 0x5a, + 0x73, + 0x2f, + 0x2b, + 0x51, + 0x41, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char node_key2[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x4a, + 0x4b, + 0x41, + 0x49, + 0x42, + 0x41, + 0x41, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x79, + 0x31, + 0x65, + 0x71, + 0x47, + 0x4a, + 0x69, + 0x45, + 0x61, + 0x4e, + 0x55, + 0x76, + 0x42, + 0x57, + 0x35, + 0x72, + 0x4b, + 0x38, + 0x77, + 0x77, + 0x69, + 0x75, + 0x4d, + 0x72, + 0x74, + 0x4c, + 0x67, + 0x74, + 0x74, + 0x45, + 0x65, + 0x55, + 0x31, + 0x51, + 0x4f, + 0x30, + 0x6e, + 0x4d, + 0x57, + 0x6a, + 0x59, + 0x43, + 0x4f, + 0x34, + 0x75, + 0x36, + 0x61, + 0x31, + 0x0a, + 0x34, + 0x58, + 0x34, + 0x6f, + 0x75, + 0x7a, + 0x68, + 0x57, + 0x4e, + 0x6a, + 0x50, + 0x47, + 0x35, + 0x31, + 0x6f, + 0x4a, + 0x43, + 0x76, + 0x4b, + 0x38, + 0x78, + 0x71, + 0x58, + 0x52, + 0x33, + 0x2f, + 0x7a, + 0x62, + 0x79, + 0x36, + 0x66, + 0x66, + 0x48, + 0x69, + 0x42, + 0x61, + 0x4e, + 0x68, + 0x4d, + 0x34, + 0x4c, + 0x6e, + 0x6c, + 0x78, + 0x44, + 0x30, + 0x77, + 0x4d, + 0x31, + 0x33, + 0x36, + 0x6d, + 0x59, + 0x77, + 0x51, + 0x54, + 0x6a, + 0x35, + 0x33, + 0x57, + 0x36, + 0x64, + 0x4e, + 0x2b, + 0x0a, + 0x6a, + 0x33, + 0x7a, + 0x7a, + 0x61, + 0x4d, + 0x7a, + 0x78, + 0x36, + 0x4f, + 0x55, + 0x30, + 0x56, + 0x4e, + 0x77, + 0x4f, + 0x68, + 0x4d, + 0x2b, + 0x47, + 0x67, + 0x33, + 0x76, + 0x43, + 0x70, + 0x2f, + 0x45, + 0x37, + 0x58, + 0x6b, + 0x57, + 0x65, + 0x4a, + 0x50, + 0x42, + 0x4d, + 0x4a, + 0x62, + 0x33, + 0x52, + 0x75, + 0x62, + 0x50, + 0x76, + 0x61, + 0x33, + 0x72, + 0x4d, + 0x73, + 0x36, + 0x62, + 0x63, + 0x4b, + 0x6c, + 0x6d, + 0x36, + 0x35, + 0x30, + 0x64, + 0x4b, + 0x64, + 0x63, + 0x65, + 0x71, + 0x0a, + 0x32, + 0x41, + 0x37, + 0x50, + 0x71, + 0x64, + 0x4a, + 0x4a, + 0x74, + 0x4f, + 0x4d, + 0x37, + 0x56, + 0x42, + 0x48, + 0x63, + 0x73, + 0x33, + 0x34, + 0x4f, + 0x38, + 0x33, + 0x54, + 0x68, + 0x73, + 0x78, + 0x63, + 0x46, + 0x39, + 0x37, + 0x2f, + 0x51, + 0x31, + 0x55, + 0x54, + 0x39, + 0x43, + 0x6c, + 0x34, + 0x34, + 0x34, + 0x59, + 0x39, + 0x6c, + 0x49, + 0x59, + 0x49, + 0x36, + 0x30, + 0x71, + 0x38, + 0x4e, + 0x6e, + 0x67, + 0x36, + 0x37, + 0x49, + 0x71, + 0x47, + 0x62, + 0x5a, + 0x47, + 0x69, + 0x73, + 0x0a, + 0x59, + 0x6d, + 0x63, + 0x75, + 0x5a, + 0x66, + 0x45, + 0x4d, + 0x55, + 0x65, + 0x59, + 0x45, + 0x79, + 0x6e, + 0x54, + 0x52, + 0x30, + 0x42, + 0x78, + 0x36, + 0x73, + 0x45, + 0x52, + 0x45, + 0x73, + 0x4f, + 0x51, + 0x74, + 0x47, + 0x6c, + 0x31, + 0x62, + 0x6a, + 0x77, + 0x72, + 0x61, + 0x4c, + 0x6d, + 0x30, + 0x46, + 0x43, + 0x38, + 0x42, + 0x77, + 0x6c, + 0x6e, + 0x4f, + 0x55, + 0x76, + 0x78, + 0x44, + 0x34, + 0x66, + 0x5a, + 0x2b, + 0x43, + 0x35, + 0x4a, + 0x73, + 0x48, + 0x62, + 0x5a, + 0x35, + 0x54, + 0x0a, + 0x4b, + 0x30, + 0x46, + 0x74, + 0x73, + 0x32, + 0x65, + 0x43, + 0x2f, + 0x71, + 0x48, + 0x4a, + 0x48, + 0x4c, + 0x52, + 0x73, + 0x41, + 0x6a, + 0x4e, + 0x70, + 0x67, + 0x66, + 0x42, + 0x71, + 0x6c, + 0x45, + 0x6a, + 0x47, + 0x73, + 0x74, + 0x4e, + 0x61, + 0x64, + 0x30, + 0x79, + 0x49, + 0x48, + 0x76, + 0x49, + 0x36, + 0x39, + 0x32, + 0x4b, + 0x79, + 0x56, + 0x54, + 0x37, + 0x56, + 0x4b, + 0x67, + 0x56, + 0x59, + 0x42, + 0x74, + 0x39, + 0x58, + 0x6e, + 0x4b, + 0x36, + 0x48, + 0x41, + 0x36, + 0x66, + 0x42, + 0x0a, + 0x4d, + 0x78, + 0x4b, + 0x68, + 0x31, + 0x48, + 0x54, + 0x30, + 0x37, + 0x47, + 0x42, + 0x52, + 0x71, + 0x34, + 0x44, + 0x62, + 0x4b, + 0x73, + 0x41, + 0x76, + 0x5a, + 0x44, + 0x73, + 0x45, + 0x7a, + 0x6b, + 0x61, + 0x64, + 0x67, + 0x75, + 0x6c, + 0x53, + 0x6a, + 0x6d, + 0x72, + 0x6b, + 0x57, + 0x41, + 0x44, + 0x43, + 0x53, + 0x77, + 0x6d, + 0x58, + 0x7a, + 0x44, + 0x4a, + 0x49, + 0x4f, + 0x6c, + 0x68, + 0x78, + 0x43, + 0x76, + 0x73, + 0x57, + 0x76, + 0x4e, + 0x4b, + 0x4f, + 0x70, + 0x7a, + 0x57, + 0x6a, + 0x0a, + 0x31, + 0x53, + 0x7a, + 0x75, + 0x45, + 0x51, + 0x53, + 0x57, + 0x53, + 0x38, + 0x30, + 0x46, + 0x6d, + 0x54, + 0x58, + 0x35, + 0x55, + 0x39, + 0x4e, + 0x6f, + 0x2f, + 0x6e, + 0x45, + 0x52, + 0x6a, + 0x4b, + 0x6c, + 0x78, + 0x75, + 0x44, + 0x36, + 0x37, + 0x35, + 0x67, + 0x4c, + 0x4e, + 0x79, + 0x30, + 0x6c, + 0x61, + 0x63, + 0x6b, + 0x70, + 0x73, + 0x49, + 0x48, + 0x50, + 0x5a, + 0x36, + 0x7a, + 0x6f, + 0x71, + 0x53, + 0x6c, + 0x38, + 0x4c, + 0x68, + 0x70, + 0x5a, + 0x7a, + 0x78, + 0x51, + 0x67, + 0x75, + 0x0a, + 0x56, + 0x51, + 0x72, + 0x39, + 0x62, + 0x69, + 0x4e, + 0x45, + 0x57, + 0x77, + 0x30, + 0x62, + 0x64, + 0x53, + 0x45, + 0x44, + 0x57, + 0x2b, + 0x63, + 0x68, + 0x6e, + 0x41, + 0x72, + 0x7a, + 0x71, + 0x4b, + 0x71, + 0x62, + 0x65, + 0x54, + 0x34, + 0x38, + 0x52, + 0x74, + 0x71, + 0x46, + 0x77, + 0x54, + 0x45, + 0x44, + 0x69, + 0x6f, + 0x48, + 0x38, + 0x73, + 0x47, + 0x47, + 0x69, + 0x43, + 0x2b, + 0x7a, + 0x6a, + 0x50, + 0x31, + 0x76, + 0x59, + 0x78, + 0x44, + 0x66, + 0x6d, + 0x32, + 0x49, + 0x69, + 0x68, + 0x0a, + 0x41, + 0x4f, + 0x50, + 0x74, + 0x48, + 0x6e, + 0x45, + 0x33, + 0x78, + 0x7a, + 0x6b, + 0x44, + 0x4c, + 0x41, + 0x50, + 0x4a, + 0x76, + 0x7a, + 0x45, + 0x61, + 0x2f, + 0x5a, + 0x69, + 0x61, + 0x54, + 0x69, + 0x4d, + 0x61, + 0x30, + 0x4f, + 0x52, + 0x32, + 0x71, + 0x74, + 0x75, + 0x30, + 0x68, + 0x54, + 0x6a, + 0x62, + 0x34, + 0x63, + 0x7a, + 0x58, + 0x49, + 0x36, + 0x54, + 0x79, + 0x37, + 0x78, + 0x6b, + 0x53, + 0x4c, + 0x45, + 0x74, + 0x44, + 0x4f, + 0x6a, + 0x43, + 0x61, + 0x70, + 0x53, + 0x71, + 0x74, + 0x0a, + 0x34, + 0x45, + 0x4b, + 0x47, + 0x67, + 0x43, + 0x69, + 0x78, + 0x7a, + 0x63, + 0x55, + 0x35, + 0x45, + 0x36, + 0x5a, + 0x4d, + 0x49, + 0x4f, + 0x56, + 0x6a, + 0x72, + 0x66, + 0x6a, + 0x39, + 0x70, + 0x77, + 0x67, + 0x67, + 0x2f, + 0x66, + 0x56, + 0x31, + 0x35, + 0x30, + 0x63, + 0x31, + 0x68, + 0x6e, + 0x6e, + 0x56, + 0x47, + 0x75, + 0x36, + 0x51, + 0x42, + 0x30, + 0x44, + 0x72, + 0x59, + 0x4e, + 0x73, + 0x59, + 0x50, + 0x43, + 0x44, + 0x44, + 0x50, + 0x70, + 0x4d, + 0x43, + 0x41, + 0x77, + 0x45, + 0x41, + 0x0a, + 0x41, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x74, + 0x69, + 0x33, + 0x49, + 0x65, + 0x31, + 0x6a, + 0x4a, + 0x36, + 0x4a, + 0x4a, + 0x33, + 0x47, + 0x6b, + 0x71, + 0x66, + 0x51, + 0x68, + 0x49, + 0x69, + 0x75, + 0x34, + 0x78, + 0x6b, + 0x4c, + 0x2f, + 0x6d, + 0x44, + 0x47, + 0x53, + 0x32, + 0x34, + 0x72, + 0x6c, + 0x41, + 0x76, + 0x6c, + 0x50, + 0x57, + 0x4e, + 0x4e, + 0x69, + 0x6c, + 0x4b, + 0x37, + 0x50, + 0x53, + 0x6a, + 0x70, + 0x32, + 0x38, + 0x38, + 0x56, + 0x75, + 0x42, + 0x57, + 0x0a, + 0x66, + 0x53, + 0x46, + 0x4f, + 0x5a, + 0x79, + 0x42, + 0x48, + 0x55, + 0x63, + 0x50, + 0x41, + 0x56, + 0x63, + 0x6c, + 0x69, + 0x69, + 0x32, + 0x63, + 0x6b, + 0x46, + 0x51, + 0x5a, + 0x46, + 0x34, + 0x39, + 0x76, + 0x67, + 0x64, + 0x75, + 0x6f, + 0x70, + 0x50, + 0x35, + 0x6f, + 0x57, + 0x4c, + 0x54, + 0x70, + 0x5a, + 0x6c, + 0x77, + 0x67, + 0x6c, + 0x55, + 0x6a, + 0x6a, + 0x6f, + 0x76, + 0x5a, + 0x63, + 0x51, + 0x59, + 0x56, + 0x58, + 0x57, + 0x34, + 0x66, + 0x39, + 0x30, + 0x70, + 0x4d, + 0x52, + 0x5a, + 0x0a, + 0x52, + 0x2b, + 0x74, + 0x79, + 0x51, + 0x30, + 0x55, + 0x55, + 0x7a, + 0x50, + 0x43, + 0x51, + 0x39, + 0x32, + 0x50, + 0x2b, + 0x4e, + 0x6a, + 0x44, + 0x75, + 0x6d, + 0x37, + 0x75, + 0x4c, + 0x77, + 0x46, + 0x52, + 0x36, + 0x4a, + 0x4b, + 0x59, + 0x34, + 0x4c, + 0x45, + 0x53, + 0x46, + 0x6b, + 0x4b, + 0x6e, + 0x6f, + 0x6c, + 0x46, + 0x77, + 0x2b, + 0x6a, + 0x4d, + 0x4c, + 0x76, + 0x72, + 0x52, + 0x34, + 0x32, + 0x47, + 0x2b, + 0x66, + 0x54, + 0x72, + 0x4e, + 0x63, + 0x34, + 0x50, + 0x6c, + 0x48, + 0x6c, + 0x0a, + 0x4c, + 0x32, + 0x63, + 0x57, + 0x56, + 0x47, + 0x2f, + 0x63, + 0x61, + 0x41, + 0x50, + 0x63, + 0x71, + 0x55, + 0x70, + 0x69, + 0x6d, + 0x2b, + 0x31, + 0x59, + 0x72, + 0x32, + 0x72, + 0x76, + 0x55, + 0x75, + 0x51, + 0x49, + 0x51, + 0x53, + 0x33, + 0x42, + 0x6b, + 0x48, + 0x54, + 0x45, + 0x69, + 0x6f, + 0x7a, + 0x2b, + 0x33, + 0x70, + 0x74, + 0x76, + 0x65, + 0x39, + 0x68, + 0x2b, + 0x32, + 0x4c, + 0x77, + 0x70, + 0x57, + 0x49, + 0x42, + 0x66, + 0x55, + 0x5a, + 0x70, + 0x58, + 0x2b, + 0x34, + 0x56, + 0x48, + 0x0a, + 0x49, + 0x47, + 0x6b, + 0x51, + 0x69, + 0x63, + 0x4b, + 0x33, + 0x35, + 0x39, + 0x68, + 0x63, + 0x49, + 0x32, + 0x68, + 0x47, + 0x6b, + 0x71, + 0x55, + 0x62, + 0x73, + 0x36, + 0x39, + 0x49, + 0x78, + 0x4a, + 0x4e, + 0x59, + 0x66, + 0x70, + 0x65, + 0x32, + 0x47, + 0x5a, + 0x31, + 0x45, + 0x51, + 0x63, + 0x63, + 0x7a, + 0x30, + 0x53, + 0x73, + 0x48, + 0x4e, + 0x71, + 0x57, + 0x65, + 0x77, + 0x2b, + 0x6f, + 0x52, + 0x63, + 0x61, + 0x69, + 0x54, + 0x6f, + 0x77, + 0x46, + 0x76, + 0x6f, + 0x33, + 0x68, + 0x74, + 0x0a, + 0x36, + 0x47, + 0x78, + 0x53, + 0x6e, + 0x71, + 0x45, + 0x57, + 0x30, + 0x37, + 0x48, + 0x30, + 0x4b, + 0x54, + 0x4b, + 0x59, + 0x69, + 0x6c, + 0x6e, + 0x5a, + 0x69, + 0x34, + 0x58, + 0x49, + 0x57, + 0x33, + 0x48, + 0x35, + 0x30, + 0x51, + 0x61, + 0x75, + 0x61, + 0x68, + 0x62, + 0x77, + 0x48, + 0x30, + 0x75, + 0x56, + 0x73, + 0x64, + 0x32, + 0x55, + 0x39, + 0x6f, + 0x34, + 0x6c, + 0x37, + 0x54, + 0x38, + 0x5a, + 0x4d, + 0x65, + 0x34, + 0x52, + 0x50, + 0x6d, + 0x66, + 0x39, + 0x77, + 0x46, + 0x6e, + 0x2f, + 0x0a, + 0x45, + 0x7a, + 0x34, + 0x46, + 0x74, + 0x68, + 0x62, + 0x6e, + 0x51, + 0x4f, + 0x57, + 0x42, + 0x6f, + 0x31, + 0x44, + 0x78, + 0x61, + 0x42, + 0x6d, + 0x78, + 0x39, + 0x42, + 0x66, + 0x6e, + 0x35, + 0x6e, + 0x43, + 0x35, + 0x49, + 0x36, + 0x52, + 0x79, + 0x72, + 0x36, + 0x58, + 0x4d, + 0x6e, + 0x6e, + 0x4a, + 0x4a, + 0x2b, + 0x65, + 0x41, + 0x74, + 0x53, + 0x6a, + 0x58, + 0x53, + 0x41, + 0x56, + 0x37, + 0x2f, + 0x36, + 0x33, + 0x43, + 0x72, + 0x70, + 0x63, + 0x42, + 0x2b, + 0x73, + 0x42, + 0x74, + 0x46, + 0x0a, + 0x69, + 0x7a, + 0x46, + 0x33, + 0x79, + 0x45, + 0x51, + 0x38, + 0x72, + 0x74, + 0x57, + 0x41, + 0x43, + 0x71, + 0x78, + 0x6a, + 0x4d, + 0x55, + 0x50, + 0x43, + 0x33, + 0x4a, + 0x71, + 0x39, + 0x6c, + 0x39, + 0x33, + 0x39, + 0x38, + 0x2f, + 0x6a, + 0x7a, + 0x43, + 0x70, + 0x5a, + 0x5a, + 0x4c, + 0x76, + 0x7a, + 0x34, + 0x4e, + 0x55, + 0x6a, + 0x6a, + 0x74, + 0x66, + 0x4e, + 0x68, + 0x33, + 0x69, + 0x36, + 0x44, + 0x50, + 0x36, + 0x49, + 0x68, + 0x41, + 0x4b, + 0x31, + 0x33, + 0x54, + 0x56, + 0x37, + 0x49, + 0x0a, + 0x34, + 0x47, + 0x34, + 0x30, + 0x62, + 0x72, + 0x5a, + 0x68, + 0x6d, + 0x58, + 0x53, + 0x56, + 0x4b, + 0x42, + 0x6e, + 0x78, + 0x47, + 0x37, + 0x44, + 0x35, + 0x69, + 0x59, + 0x36, + 0x50, + 0x62, + 0x67, + 0x56, + 0x57, + 0x48, + 0x46, + 0x44, + 0x45, + 0x6a, + 0x50, + 0x44, + 0x77, + 0x6f, + 0x4a, + 0x6b, + 0x74, + 0x4b, + 0x31, + 0x42, + 0x50, + 0x61, + 0x64, + 0x33, + 0x71, + 0x57, + 0x66, + 0x39, + 0x34, + 0x66, + 0x6d, + 0x74, + 0x35, + 0x79, + 0x6b, + 0x5a, + 0x64, + 0x2f, + 0x74, + 0x65, + 0x71, + 0x0a, + 0x65, + 0x52, + 0x49, + 0x45, + 0x76, + 0x55, + 0x7a, + 0x4a, + 0x47, + 0x35, + 0x50, + 0x46, + 0x47, + 0x77, + 0x4f, + 0x7a, + 0x78, + 0x56, + 0x52, + 0x48, + 0x44, + 0x4e, + 0x38, + 0x51, + 0x51, + 0x6d, + 0x4e, + 0x4a, + 0x63, + 0x43, + 0x4a, + 0x75, + 0x79, + 0x74, + 0x77, + 0x34, + 0x44, + 0x6e, + 0x73, + 0x78, + 0x6f, + 0x43, + 0x38, + 0x33, + 0x69, + 0x7a, + 0x66, + 0x68, + 0x2b, + 0x6e, + 0x45, + 0x71, + 0x45, + 0x50, + 0x50, + 0x51, + 0x69, + 0x44, + 0x46, + 0x6f, + 0x54, + 0x4c, + 0x79, + 0x44, + 0x0a, + 0x2b, + 0x34, + 0x56, + 0x52, + 0x67, + 0x46, + 0x42, + 0x62, + 0x36, + 0x61, + 0x69, + 0x72, + 0x56, + 0x63, + 0x79, + 0x58, + 0x57, + 0x64, + 0x72, + 0x32, + 0x30, + 0x43, + 0x64, + 0x4f, + 0x47, + 0x67, + 0x58, + 0x6f, + 0x42, + 0x4d, + 0x72, + 0x4e, + 0x42, + 0x6c, + 0x2b, + 0x45, + 0x36, + 0x32, + 0x4d, + 0x59, + 0x41, + 0x65, + 0x32, + 0x71, + 0x63, + 0x36, + 0x37, + 0x63, + 0x51, + 0x59, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4f, + 0x39, + 0x32, + 0x58, + 0x4e, + 0x41, + 0x46, + 0x0a, + 0x52, + 0x41, + 0x77, + 0x5a, + 0x79, + 0x64, + 0x7a, + 0x59, + 0x63, + 0x68, + 0x35, + 0x45, + 0x35, + 0x65, + 0x6a, + 0x7a, + 0x39, + 0x54, + 0x5a, + 0x62, + 0x48, + 0x66, + 0x65, + 0x66, + 0x34, + 0x7a, + 0x4e, + 0x42, + 0x48, + 0x55, + 0x2f, + 0x6d, + 0x56, + 0x31, + 0x76, + 0x68, + 0x38, + 0x39, + 0x64, + 0x39, + 0x4b, + 0x6a, + 0x53, + 0x46, + 0x2f, + 0x4f, + 0x69, + 0x4c, + 0x33, + 0x45, + 0x6e, + 0x6f, + 0x59, + 0x78, + 0x50, + 0x6b, + 0x48, + 0x67, + 0x55, + 0x58, + 0x79, + 0x6e, + 0x39, + 0x41, + 0x0a, + 0x34, + 0x43, + 0x34, + 0x75, + 0x31, + 0x72, + 0x6c, + 0x64, + 0x5a, + 0x36, + 0x64, + 0x77, + 0x43, + 0x56, + 0x41, + 0x62, + 0x5a, + 0x4a, + 0x6e, + 0x54, + 0x38, + 0x55, + 0x46, + 0x32, + 0x6e, + 0x4c, + 0x52, + 0x46, + 0x4d, + 0x58, + 0x68, + 0x61, + 0x71, + 0x79, + 0x4a, + 0x45, + 0x6d, + 0x54, + 0x37, + 0x6a, + 0x43, + 0x59, + 0x52, + 0x79, + 0x37, + 0x65, + 0x72, + 0x64, + 0x37, + 0x56, + 0x4c, + 0x39, + 0x35, + 0x67, + 0x56, + 0x68, + 0x55, + 0x4f, + 0x73, + 0x4a, + 0x59, + 0x77, + 0x4c, + 0x6d, + 0x0a, + 0x74, + 0x59, + 0x66, + 0x63, + 0x69, + 0x6b, + 0x30, + 0x37, + 0x51, + 0x6d, + 0x44, + 0x53, + 0x50, + 0x56, + 0x75, + 0x47, + 0x68, + 0x56, + 0x32, + 0x4b, + 0x65, + 0x6b, + 0x6e, + 0x7a, + 0x54, + 0x61, + 0x31, + 0x4e, + 0x53, + 0x54, + 0x55, + 0x38, + 0x76, + 0x77, + 0x51, + 0x46, + 0x2b, + 0x77, + 0x4b, + 0x48, + 0x69, + 0x38, + 0x36, + 0x34, + 0x64, + 0x6d, + 0x7a, + 0x6b, + 0x73, + 0x6c, + 0x59, + 0x6e, + 0x51, + 0x71, + 0x62, + 0x46, + 0x6f, + 0x54, + 0x56, + 0x52, + 0x5a, + 0x74, + 0x4e, + 0x65, + 0x0a, + 0x72, + 0x4c, + 0x62, + 0x36, + 0x67, + 0x36, + 0x6e, + 0x63, + 0x77, + 0x36, + 0x4a, + 0x57, + 0x78, + 0x39, + 0x79, + 0x65, + 0x42, + 0x30, + 0x37, + 0x56, + 0x64, + 0x70, + 0x4a, + 0x70, + 0x36, + 0x78, + 0x39, + 0x54, + 0x59, + 0x54, + 0x2b, + 0x48, + 0x47, + 0x6a, + 0x33, + 0x6b, + 0x62, + 0x35, + 0x61, + 0x63, + 0x75, + 0x49, + 0x6e, + 0x50, + 0x2b, + 0x6a, + 0x67, + 0x37, + 0x47, + 0x5a, + 0x7a, + 0x6b, + 0x72, + 0x48, + 0x4a, + 0x4d, + 0x6e, + 0x48, + 0x4c, + 0x77, + 0x65, + 0x46, + 0x76, + 0x62, + 0x0a, + 0x4e, + 0x57, + 0x5a, + 0x66, + 0x6c, + 0x50, + 0x66, + 0x69, + 0x41, + 0x4e, + 0x6d, + 0x77, + 0x4d, + 0x38, + 0x77, + 0x4d, + 0x52, + 0x2b, + 0x6b, + 0x66, + 0x50, + 0x32, + 0x76, + 0x6f, + 0x67, + 0x47, + 0x61, + 0x6d, + 0x4b, + 0x43, + 0x69, + 0x65, + 0x6f, + 0x6a, + 0x56, + 0x61, + 0x54, + 0x69, + 0x38, + 0x76, + 0x67, + 0x71, + 0x71, + 0x6a, + 0x78, + 0x34, + 0x55, + 0x47, + 0x50, + 0x64, + 0x35, + 0x39, + 0x36, + 0x46, + 0x4e, + 0x46, + 0x65, + 0x69, + 0x5a, + 0x52, + 0x76, + 0x58, + 0x4e, + 0x6b, + 0x0a, + 0x30, + 0x64, + 0x42, + 0x30, + 0x41, + 0x4b, + 0x32, + 0x51, + 0x2f, + 0x31, + 0x32, + 0x36, + 0x53, + 0x6c, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4e, + 0x6c, + 0x69, + 0x74, + 0x64, + 0x78, + 0x48, + 0x74, + 0x74, + 0x78, + 0x68, + 0x2f, + 0x57, + 0x71, + 0x77, + 0x46, + 0x4b, + 0x33, + 0x35, + 0x62, + 0x63, + 0x4f, + 0x54, + 0x58, + 0x61, + 0x68, + 0x36, + 0x50, + 0x61, + 0x70, + 0x50, + 0x64, + 0x67, + 0x2f, + 0x37, + 0x32, + 0x50, + 0x75, + 0x4f, + 0x6c, + 0x33, + 0x57, + 0x74, + 0x0a, + 0x37, + 0x57, + 0x6e, + 0x38, + 0x41, + 0x74, + 0x36, + 0x46, + 0x49, + 0x47, + 0x51, + 0x33, + 0x58, + 0x5a, + 0x4f, + 0x4f, + 0x50, + 0x66, + 0x39, + 0x76, + 0x35, + 0x48, + 0x66, + 0x71, + 0x50, + 0x70, + 0x53, + 0x6c, + 0x4e, + 0x56, + 0x39, + 0x33, + 0x41, + 0x58, + 0x58, + 0x61, + 0x4f, + 0x36, + 0x49, + 0x54, + 0x7a, + 0x77, + 0x49, + 0x45, + 0x62, + 0x38, + 0x49, + 0x47, + 0x33, + 0x69, + 0x45, + 0x4f, + 0x74, + 0x54, + 0x4c, + 0x77, + 0x32, + 0x39, + 0x32, + 0x4b, + 0x55, + 0x38, + 0x6c, + 0x4a, + 0x0a, + 0x4c, + 0x6c, + 0x38, + 0x58, + 0x70, + 0x68, + 0x4f, + 0x6b, + 0x56, + 0x55, + 0x72, + 0x50, + 0x6b, + 0x7a, + 0x47, + 0x47, + 0x36, + 0x64, + 0x66, + 0x56, + 0x4e, + 0x70, + 0x36, + 0x49, + 0x32, + 0x68, + 0x54, + 0x70, + 0x59, + 0x4a, + 0x49, + 0x53, + 0x77, + 0x41, + 0x55, + 0x35, + 0x48, + 0x76, + 0x4b, + 0x70, + 0x63, + 0x39, + 0x6f, + 0x42, + 0x30, + 0x59, + 0x4b, + 0x68, + 0x56, + 0x4c, + 0x66, + 0x43, + 0x2b, + 0x69, + 0x64, + 0x61, + 0x6b, + 0x56, + 0x43, + 0x35, + 0x66, + 0x5a, + 0x7a, + 0x36, + 0x0a, + 0x7a, + 0x46, + 0x41, + 0x6f, + 0x32, + 0x37, + 0x46, + 0x65, + 0x6f, + 0x49, + 0x45, + 0x64, + 0x48, + 0x74, + 0x39, + 0x32, + 0x5a, + 0x65, + 0x36, + 0x51, + 0x33, + 0x34, + 0x4e, + 0x4a, + 0x67, + 0x70, + 0x56, + 0x62, + 0x67, + 0x6d, + 0x54, + 0x30, + 0x2b, + 0x77, + 0x74, + 0x75, + 0x75, + 0x72, + 0x71, + 0x6f, + 0x5a, + 0x2f, + 0x7a, + 0x52, + 0x74, + 0x6b, + 0x78, + 0x34, + 0x35, + 0x77, + 0x75, + 0x71, + 0x49, + 0x33, + 0x45, + 0x2b, + 0x54, + 0x44, + 0x61, + 0x31, + 0x53, + 0x75, + 0x71, + 0x33, + 0x0a, + 0x34, + 0x4f, + 0x51, + 0x73, + 0x65, + 0x32, + 0x57, + 0x63, + 0x67, + 0x6a, + 0x7a, + 0x53, + 0x66, + 0x64, + 0x4c, + 0x4a, + 0x74, + 0x54, + 0x66, + 0x79, + 0x65, + 0x4c, + 0x31, + 0x70, + 0x34, + 0x56, + 0x68, + 0x47, + 0x79, + 0x63, + 0x78, + 0x67, + 0x2b, + 0x4a, + 0x45, + 0x35, + 0x48, + 0x34, + 0x6a, + 0x69, + 0x33, + 0x7a, + 0x35, + 0x33, + 0x46, + 0x78, + 0x72, + 0x4d, + 0x6e, + 0x41, + 0x30, + 0x6d, + 0x53, + 0x45, + 0x6c, + 0x79, + 0x45, + 0x6a, + 0x69, + 0x38, + 0x38, + 0x78, + 0x54, + 0x47, + 0x0a, + 0x48, + 0x43, + 0x59, + 0x47, + 0x49, + 0x6e, + 0x4a, + 0x70, + 0x5a, + 0x72, + 0x2b, + 0x51, + 0x7a, + 0x67, + 0x50, + 0x54, + 0x7a, + 0x57, + 0x66, + 0x35, + 0x52, + 0x34, + 0x36, + 0x6b, + 0x30, + 0x38, + 0x47, + 0x4b, + 0x73, + 0x6d, + 0x56, + 0x67, + 0x6a, + 0x31, + 0x50, + 0x32, + 0x7a, + 0x39, + 0x4f, + 0x65, + 0x33, + 0x61, + 0x4d, + 0x43, + 0x67, + 0x67, + 0x45, + 0x41, + 0x48, + 0x42, + 0x6b, + 0x64, + 0x5a, + 0x75, + 0x4e, + 0x58, + 0x53, + 0x72, + 0x77, + 0x7a, + 0x37, + 0x5a, + 0x41, + 0x51, + 0x0a, + 0x51, + 0x2f, + 0x44, + 0x39, + 0x73, + 0x55, + 0x6e, + 0x2b, + 0x2b, + 0x66, + 0x50, + 0x54, + 0x48, + 0x6c, + 0x31, + 0x4b, + 0x67, + 0x5a, + 0x63, + 0x67, + 0x59, + 0x32, + 0x47, + 0x35, + 0x32, + 0x6e, + 0x51, + 0x32, + 0x38, + 0x70, + 0x41, + 0x6a, + 0x52, + 0x61, + 0x70, + 0x37, + 0x4e, + 0x4b, + 0x5a, + 0x45, + 0x6f, + 0x50, + 0x39, + 0x39, + 0x73, + 0x4c, + 0x58, + 0x52, + 0x74, + 0x2f, + 0x4e, + 0x45, + 0x74, + 0x59, + 0x33, + 0x64, + 0x51, + 0x45, + 0x34, + 0x4b, + 0x73, + 0x42, + 0x46, + 0x2f, + 0x0a, + 0x75, + 0x69, + 0x76, + 0x78, + 0x53, + 0x38, + 0x38, + 0x4c, + 0x44, + 0x4f, + 0x6e, + 0x4c, + 0x6f, + 0x30, + 0x7a, + 0x52, + 0x73, + 0x6d, + 0x31, + 0x30, + 0x45, + 0x70, + 0x56, + 0x42, + 0x41, + 0x33, + 0x4a, + 0x64, + 0x4d, + 0x50, + 0x33, + 0x65, + 0x2f, + 0x67, + 0x57, + 0x6d, + 0x57, + 0x53, + 0x72, + 0x56, + 0x55, + 0x43, + 0x6d, + 0x75, + 0x74, + 0x65, + 0x37, + 0x6e, + 0x57, + 0x63, + 0x7a, + 0x75, + 0x4b, + 0x30, + 0x62, + 0x37, + 0x41, + 0x67, + 0x67, + 0x6b, + 0x79, + 0x6b, + 0x38, + 0x72, + 0x0a, + 0x79, + 0x4d, + 0x53, + 0x69, + 0x6f, + 0x6e, + 0x79, + 0x30, + 0x5a, + 0x58, + 0x64, + 0x38, + 0x52, + 0x66, + 0x55, + 0x67, + 0x70, + 0x6a, + 0x63, + 0x74, + 0x59, + 0x65, + 0x76, + 0x51, + 0x31, + 0x68, + 0x34, + 0x46, + 0x69, + 0x42, + 0x52, + 0x7a, + 0x6d, + 0x54, + 0x77, + 0x58, + 0x32, + 0x55, + 0x73, + 0x30, + 0x69, + 0x64, + 0x74, + 0x74, + 0x66, + 0x4c, + 0x67, + 0x76, + 0x35, + 0x46, + 0x75, + 0x36, + 0x33, + 0x77, + 0x35, + 0x36, + 0x34, + 0x62, + 0x57, + 0x66, + 0x67, + 0x57, + 0x30, + 0x41, + 0x0a, + 0x48, + 0x36, + 0x70, + 0x6a, + 0x4a, + 0x55, + 0x58, + 0x39, + 0x77, + 0x59, + 0x68, + 0x57, + 0x73, + 0x33, + 0x4e, + 0x75, + 0x50, + 0x57, + 0x7a, + 0x42, + 0x4f, + 0x6e, + 0x30, + 0x56, + 0x33, + 0x54, + 0x46, + 0x53, + 0x7a, + 0x71, + 0x39, + 0x78, + 0x44, + 0x2b, + 0x71, + 0x78, + 0x68, + 0x36, + 0x75, + 0x58, + 0x6f, + 0x38, + 0x74, + 0x4a, + 0x57, + 0x4e, + 0x67, + 0x59, + 0x65, + 0x33, + 0x77, + 0x77, + 0x41, + 0x30, + 0x64, + 0x44, + 0x69, + 0x74, + 0x47, + 0x6f, + 0x58, + 0x7a, + 0x58, + 0x4c, + 0x0a, + 0x74, + 0x37, + 0x39, + 0x76, + 0x4e, + 0x49, + 0x41, + 0x30, + 0x5a, + 0x2b, + 0x78, + 0x63, + 0x75, + 0x73, + 0x4b, + 0x37, + 0x58, + 0x6c, + 0x41, + 0x63, + 0x34, + 0x6a, + 0x64, + 0x4b, + 0x2f, + 0x55, + 0x75, + 0x74, + 0x5a, + 0x4f, + 0x67, + 0x45, + 0x51, + 0x78, + 0x57, + 0x59, + 0x73, + 0x63, + 0x6b, + 0x37, + 0x74, + 0x55, + 0x47, + 0x38, + 0x34, + 0x78, + 0x69, + 0x42, + 0x37, + 0x73, + 0x45, + 0x6b, + 0x6f, + 0x38, + 0x65, + 0x75, + 0x76, + 0x76, + 0x44, + 0x39, + 0x71, + 0x32, + 0x41, + 0x61, + 0x0a, + 0x4e, + 0x74, + 0x74, + 0x48, + 0x49, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x42, + 0x44, + 0x77, + 0x30, + 0x5a, + 0x6e, + 0x56, + 0x52, + 0x2b, + 0x47, + 0x55, + 0x54, + 0x46, + 0x61, + 0x71, + 0x57, + 0x71, + 0x34, + 0x31, + 0x4a, + 0x44, + 0x48, + 0x72, + 0x30, + 0x38, + 0x35, + 0x72, + 0x6d, + 0x6b, + 0x38, + 0x68, + 0x71, + 0x71, + 0x4a, + 0x7a, + 0x76, + 0x72, + 0x54, + 0x5a, + 0x35, + 0x36, + 0x45, + 0x2b, + 0x32, + 0x4f, + 0x50, + 0x62, + 0x78, + 0x52, + 0x42, + 0x34, + 0x77, + 0x4f, + 0x32, + 0x0a, + 0x32, + 0x5a, + 0x39, + 0x63, + 0x32, + 0x4c, + 0x45, + 0x77, + 0x78, + 0x2b, + 0x4e, + 0x54, + 0x45, + 0x66, + 0x7a, + 0x52, + 0x2f, + 0x7a, + 0x48, + 0x46, + 0x41, + 0x57, + 0x61, + 0x2b, + 0x49, + 0x76, + 0x6f, + 0x46, + 0x79, + 0x75, + 0x44, + 0x7a, + 0x67, + 0x77, + 0x69, + 0x48, + 0x56, + 0x66, + 0x61, + 0x32, + 0x48, + 0x55, + 0x79, + 0x55, + 0x68, + 0x59, + 0x6f, + 0x7a, + 0x34, + 0x2b, + 0x4f, + 0x38, + 0x55, + 0x67, + 0x69, + 0x58, + 0x63, + 0x64, + 0x71, + 0x2b, + 0x4d, + 0x72, + 0x4b, + 0x33, + 0x0a, + 0x73, + 0x44, + 0x37, + 0x30, + 0x36, + 0x46, + 0x5a, + 0x4b, + 0x68, + 0x6c, + 0x63, + 0x52, + 0x55, + 0x68, + 0x32, + 0x51, + 0x2b, + 0x54, + 0x4b, + 0x32, + 0x4e, + 0x67, + 0x77, + 0x50, + 0x56, + 0x4c, + 0x2f, + 0x31, + 0x36, + 0x71, + 0x61, + 0x48, + 0x5a, + 0x5a, + 0x75, + 0x32, + 0x65, + 0x61, + 0x33, + 0x62, + 0x33, + 0x6e, + 0x5a, + 0x32, + 0x41, + 0x41, + 0x42, + 0x65, + 0x52, + 0x6f, + 0x51, + 0x47, + 0x68, + 0x42, + 0x4e, + 0x65, + 0x52, + 0x57, + 0x34, + 0x2b, + 0x4f, + 0x76, + 0x34, + 0x54, + 0x0a, + 0x59, + 0x55, + 0x31, + 0x33, + 0x59, + 0x51, + 0x50, + 0x6a, + 0x4f, + 0x74, + 0x76, + 0x42, + 0x70, + 0x41, + 0x2b, + 0x46, + 0x6d, + 0x56, + 0x48, + 0x72, + 0x51, + 0x49, + 0x5a, + 0x6c, + 0x43, + 0x4f, + 0x35, + 0x43, + 0x4d, + 0x79, + 0x36, + 0x2f, + 0x47, + 0x32, + 0x77, + 0x56, + 0x4b, + 0x48, + 0x62, + 0x31, + 0x4a, + 0x75, + 0x4d, + 0x48, + 0x63, + 0x4f, + 0x44, + 0x33, + 0x43, + 0x4d, + 0x70, + 0x43, + 0x6f, + 0x6e, + 0x45, + 0x44, + 0x44, + 0x32, + 0x36, + 0x6a, + 0x76, + 0x51, + 0x62, + 0x49, + 0x0a, + 0x43, + 0x4f, + 0x46, + 0x5a, + 0x37, + 0x4c, + 0x43, + 0x46, + 0x73, + 0x52, + 0x71, + 0x38, + 0x43, + 0x4a, + 0x35, + 0x4c, + 0x45, + 0x7a, + 0x33, + 0x69, + 0x62, + 0x4c, + 0x50, + 0x76, + 0x4b, + 0x50, + 0x78, + 0x51, + 0x67, + 0x6c, + 0x41, + 0x4f, + 0x75, + 0x37, + 0x62, + 0x44, + 0x44, + 0x74, + 0x62, + 0x79, + 0x4c, + 0x6d, + 0x58, + 0x49, + 0x4a, + 0x7a, + 0x76, + 0x51, + 0x33, + 0x65, + 0x31, + 0x30, + 0x49, + 0x31, + 0x45, + 0x77, + 0x39, + 0x53, + 0x61, + 0x56, + 0x76, + 0x59, + 0x39, + 0x65, + 0x0a, + 0x31, + 0x62, + 0x65, + 0x79, + 0x62, + 0x37, + 0x71, + 0x4f, + 0x31, + 0x36, + 0x44, + 0x62, + 0x73, + 0x54, + 0x75, + 0x6c, + 0x45, + 0x43, + 0x51, + 0x4e, + 0x2f, + 0x2b, + 0x79, + 0x4e, + 0x77, + 0x74, + 0x6b, + 0x44, + 0x34, + 0x58, + 0x69, + 0x37, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x41, + 0x63, + 0x73, + 0x6f, + 0x4f, + 0x7a, + 0x75, + 0x50, + 0x6d, + 0x37, + 0x35, + 0x4a, + 0x4f, + 0x32, + 0x42, + 0x33, + 0x76, + 0x32, + 0x45, + 0x64, + 0x2b, + 0x37, + 0x47, + 0x4b, + 0x31, + 0x53, + 0x63, + 0x0a, + 0x6c, + 0x76, + 0x56, + 0x4e, + 0x55, + 0x33, + 0x43, + 0x7a, + 0x30, + 0x4e, + 0x66, + 0x4c, + 0x50, + 0x4a, + 0x56, + 0x79, + 0x73, + 0x56, + 0x53, + 0x48, + 0x65, + 0x67, + 0x61, + 0x53, + 0x5a, + 0x38, + 0x33, + 0x5a, + 0x6f, + 0x4e, + 0x50, + 0x68, + 0x6d, + 0x54, + 0x42, + 0x47, + 0x62, + 0x72, + 0x56, + 0x45, + 0x72, + 0x31, + 0x62, + 0x33, + 0x42, + 0x55, + 0x59, + 0x30, + 0x6c, + 0x6b, + 0x46, + 0x49, + 0x4f, + 0x6f, + 0x33, + 0x6d, + 0x66, + 0x30, + 0x32, + 0x6c, + 0x37, + 0x57, + 0x48, + 0x59, + 0x0a, + 0x78, + 0x4d, + 0x51, + 0x38, + 0x48, + 0x63, + 0x32, + 0x61, + 0x6e, + 0x55, + 0x38, + 0x71, + 0x64, + 0x4f, + 0x58, + 0x76, + 0x6f, + 0x58, + 0x62, + 0x51, + 0x34, + 0x2f, + 0x59, + 0x59, + 0x4b, + 0x31, + 0x57, + 0x42, + 0x76, + 0x4b, + 0x75, + 0x2f, + 0x43, + 0x6c, + 0x42, + 0x53, + 0x45, + 0x63, + 0x61, + 0x4f, + 0x39, + 0x30, + 0x62, + 0x71, + 0x79, + 0x44, + 0x73, + 0x37, + 0x33, + 0x6d, + 0x69, + 0x46, + 0x57, + 0x2f, + 0x43, + 0x31, + 0x54, + 0x66, + 0x78, + 0x50, + 0x58, + 0x6e, + 0x6b, + 0x53, + 0x0a, + 0x54, + 0x2b, + 0x72, + 0x4f, + 0x4d, + 0x6d, + 0x77, + 0x52, + 0x51, + 0x67, + 0x4c, + 0x46, + 0x50, + 0x6c, + 0x35, + 0x46, + 0x41, + 0x62, + 0x48, + 0x4f, + 0x6b, + 0x31, + 0x41, + 0x58, + 0x5a, + 0x45, + 0x34, + 0x46, + 0x46, + 0x47, + 0x50, + 0x75, + 0x55, + 0x62, + 0x52, + 0x74, + 0x6b, + 0x4f, + 0x45, + 0x72, + 0x64, + 0x77, + 0x34, + 0x74, + 0x5a, + 0x68, + 0x53, + 0x77, + 0x66, + 0x58, + 0x74, + 0x2f, + 0x46, + 0x41, + 0x36, + 0x2b, + 0x32, + 0x4c, + 0x6e, + 0x48, + 0x79, + 0x37, + 0x38, + 0x38, + 0x0a, + 0x58, + 0x4f, + 0x46, + 0x62, + 0x71, + 0x30, + 0x46, + 0x4b, + 0x6e, + 0x63, + 0x6e, + 0x68, + 0x32, + 0x6a, + 0x61, + 0x2b, + 0x64, + 0x37, + 0x65, + 0x4e, + 0x6c, + 0x48, + 0x77, + 0x4c, + 0x4b, + 0x63, + 0x53, + 0x43, + 0x34, + 0x43, + 0x46, + 0x4e, + 0x46, + 0x4b, + 0x51, + 0x6d, + 0x6c, + 0x45, + 0x68, + 0x5a, + 0x5a, + 0x4a, + 0x48, + 0x63, + 0x65, + 0x6e, + 0x2f, + 0x42, + 0x30, + 0x6d, + 0x7a, + 0x72, + 0x48, + 0x6c, + 0x35, + 0x76, + 0x53, + 0x6c, + 0x59, + 0x79, + 0x37, + 0x50, + 0x37, + 0x73, + 0x0a, + 0x63, + 0x32, + 0x52, + 0x34, + 0x33, + 0x54, + 0x6c, + 0x45, + 0x41, + 0x4d, + 0x77, + 0x72, + 0x32, + 0x4a, + 0x30, + 0x4c, + 0x37, + 0x58, + 0x65, + 0x34, + 0x2b, + 0x33, + 0x45, + 0x36, + 0x31, + 0x4f, + 0x64, + 0x43, + 0x4f, + 0x30, + 0x46, + 0x37, + 0x4e, + 0x46, + 0x30, + 0x76, + 0x33, + 0x66, + 0x2f, + 0x34, + 0x5a, + 0x6f, + 0x4a, + 0x49, + 0x6d, + 0x4c, + 0x74, + 0x4e, + 0x7a, + 0x4d, + 0x4f, + 0x31, + 0x68, + 0x69, + 0x61, + 0x51, + 0x34, + 0x69, + 0x67, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char node_cert2[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x7a, + 0x43, + 0x43, + 0x41, + 0x77, + 0x4f, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x44, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x77, + 0x4d, + 0x31, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x41, + 0x7a, + 0x57, + 0x6a, + 0x42, + 0x50, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x43, + 0x57, + 0x78, + 0x76, + 0x59, + 0x32, + 0x46, + 0x73, + 0x61, + 0x47, + 0x39, + 0x7a, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x0a, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4d, + 0x74, + 0x58, + 0x71, + 0x68, + 0x69, + 0x59, + 0x68, + 0x47, + 0x6a, + 0x56, + 0x4c, + 0x77, + 0x56, + 0x75, + 0x61, + 0x79, + 0x76, + 0x4d, + 0x4d, + 0x49, + 0x72, + 0x6a, + 0x4b, + 0x37, + 0x53, + 0x34, + 0x4c, + 0x62, + 0x52, + 0x48, + 0x6c, + 0x4e, + 0x55, + 0x44, + 0x0a, + 0x74, + 0x4a, + 0x7a, + 0x46, + 0x6f, + 0x32, + 0x41, + 0x6a, + 0x75, + 0x4c, + 0x75, + 0x6d, + 0x74, + 0x65, + 0x46, + 0x2b, + 0x4b, + 0x4c, + 0x73, + 0x34, + 0x56, + 0x6a, + 0x59, + 0x7a, + 0x78, + 0x75, + 0x64, + 0x61, + 0x43, + 0x51, + 0x72, + 0x79, + 0x76, + 0x4d, + 0x61, + 0x6c, + 0x30, + 0x64, + 0x2f, + 0x38, + 0x32, + 0x38, + 0x75, + 0x6e, + 0x33, + 0x78, + 0x34, + 0x67, + 0x57, + 0x6a, + 0x59, + 0x54, + 0x4f, + 0x43, + 0x35, + 0x35, + 0x63, + 0x51, + 0x39, + 0x4d, + 0x44, + 0x4e, + 0x64, + 0x2b, + 0x0a, + 0x70, + 0x6d, + 0x4d, + 0x45, + 0x45, + 0x34, + 0x2b, + 0x64, + 0x31, + 0x75, + 0x6e, + 0x54, + 0x66, + 0x6f, + 0x39, + 0x38, + 0x38, + 0x32, + 0x6a, + 0x4d, + 0x38, + 0x65, + 0x6a, + 0x6c, + 0x4e, + 0x46, + 0x54, + 0x63, + 0x44, + 0x6f, + 0x54, + 0x50, + 0x68, + 0x6f, + 0x4e, + 0x37, + 0x77, + 0x71, + 0x66, + 0x78, + 0x4f, + 0x31, + 0x35, + 0x46, + 0x6e, + 0x69, + 0x54, + 0x77, + 0x54, + 0x43, + 0x57, + 0x39, + 0x30, + 0x62, + 0x6d, + 0x7a, + 0x37, + 0x32, + 0x74, + 0x36, + 0x7a, + 0x4c, + 0x4f, + 0x6d, + 0x0a, + 0x33, + 0x43, + 0x70, + 0x5a, + 0x75, + 0x75, + 0x64, + 0x48, + 0x53, + 0x6e, + 0x58, + 0x48, + 0x71, + 0x74, + 0x67, + 0x4f, + 0x7a, + 0x36, + 0x6e, + 0x53, + 0x53, + 0x62, + 0x54, + 0x6a, + 0x4f, + 0x31, + 0x51, + 0x52, + 0x33, + 0x4c, + 0x4e, + 0x2b, + 0x44, + 0x76, + 0x4e, + 0x30, + 0x34, + 0x62, + 0x4d, + 0x58, + 0x42, + 0x66, + 0x65, + 0x2f, + 0x30, + 0x4e, + 0x56, + 0x45, + 0x2f, + 0x51, + 0x70, + 0x65, + 0x4f, + 0x4f, + 0x47, + 0x50, + 0x5a, + 0x53, + 0x47, + 0x43, + 0x4f, + 0x74, + 0x4b, + 0x76, + 0x0a, + 0x44, + 0x5a, + 0x34, + 0x4f, + 0x75, + 0x79, + 0x4b, + 0x68, + 0x6d, + 0x32, + 0x52, + 0x6f, + 0x72, + 0x47, + 0x4a, + 0x6e, + 0x4c, + 0x6d, + 0x58, + 0x78, + 0x44, + 0x46, + 0x48, + 0x6d, + 0x42, + 0x4d, + 0x70, + 0x30, + 0x30, + 0x64, + 0x41, + 0x63, + 0x65, + 0x72, + 0x42, + 0x45, + 0x52, + 0x4c, + 0x44, + 0x6b, + 0x4c, + 0x52, + 0x70, + 0x64, + 0x57, + 0x34, + 0x38, + 0x4b, + 0x32, + 0x69, + 0x35, + 0x74, + 0x42, + 0x51, + 0x76, + 0x41, + 0x63, + 0x4a, + 0x5a, + 0x7a, + 0x6c, + 0x4c, + 0x38, + 0x51, + 0x0a, + 0x2b, + 0x48, + 0x32, + 0x66, + 0x67, + 0x75, + 0x53, + 0x62, + 0x42, + 0x32, + 0x32, + 0x65, + 0x55, + 0x79, + 0x74, + 0x42, + 0x62, + 0x62, + 0x4e, + 0x6e, + 0x67, + 0x76, + 0x36, + 0x68, + 0x79, + 0x52, + 0x79, + 0x30, + 0x62, + 0x41, + 0x49, + 0x7a, + 0x61, + 0x59, + 0x48, + 0x77, + 0x61, + 0x70, + 0x52, + 0x49, + 0x78, + 0x72, + 0x4c, + 0x54, + 0x57, + 0x6e, + 0x64, + 0x4d, + 0x69, + 0x42, + 0x37, + 0x79, + 0x4f, + 0x76, + 0x64, + 0x69, + 0x73, + 0x6c, + 0x55, + 0x2b, + 0x31, + 0x53, + 0x6f, + 0x46, + 0x0a, + 0x57, + 0x41, + 0x62, + 0x66, + 0x56, + 0x35, + 0x79, + 0x75, + 0x68, + 0x77, + 0x4f, + 0x6e, + 0x77, + 0x54, + 0x4d, + 0x53, + 0x6f, + 0x64, + 0x52, + 0x30, + 0x39, + 0x4f, + 0x78, + 0x67, + 0x55, + 0x61, + 0x75, + 0x41, + 0x32, + 0x79, + 0x72, + 0x41, + 0x4c, + 0x32, + 0x51, + 0x37, + 0x42, + 0x4d, + 0x35, + 0x47, + 0x6e, + 0x59, + 0x4c, + 0x70, + 0x55, + 0x6f, + 0x35, + 0x71, + 0x35, + 0x46, + 0x67, + 0x41, + 0x77, + 0x6b, + 0x73, + 0x4a, + 0x6c, + 0x38, + 0x77, + 0x79, + 0x53, + 0x44, + 0x70, + 0x59, + 0x0a, + 0x63, + 0x51, + 0x72, + 0x37, + 0x46, + 0x72, + 0x7a, + 0x53, + 0x6a, + 0x71, + 0x63, + 0x31, + 0x6f, + 0x39, + 0x55, + 0x73, + 0x37, + 0x68, + 0x45, + 0x45, + 0x6c, + 0x6b, + 0x76, + 0x4e, + 0x42, + 0x5a, + 0x6b, + 0x31, + 0x2b, + 0x56, + 0x50, + 0x54, + 0x61, + 0x50, + 0x35, + 0x78, + 0x45, + 0x59, + 0x79, + 0x70, + 0x63, + 0x62, + 0x67, + 0x2b, + 0x75, + 0x2b, + 0x59, + 0x43, + 0x7a, + 0x63, + 0x74, + 0x4a, + 0x57, + 0x6e, + 0x4a, + 0x4b, + 0x62, + 0x43, + 0x42, + 0x7a, + 0x32, + 0x65, + 0x73, + 0x36, + 0x0a, + 0x4b, + 0x6b, + 0x70, + 0x66, + 0x43, + 0x34, + 0x61, + 0x57, + 0x63, + 0x38, + 0x55, + 0x49, + 0x4c, + 0x6c, + 0x55, + 0x4b, + 0x2f, + 0x57, + 0x34, + 0x6a, + 0x52, + 0x46, + 0x73, + 0x4e, + 0x47, + 0x33, + 0x55, + 0x68, + 0x41, + 0x31, + 0x76, + 0x6e, + 0x49, + 0x5a, + 0x77, + 0x4b, + 0x38, + 0x36, + 0x69, + 0x71, + 0x6d, + 0x33, + 0x6b, + 0x2b, + 0x50, + 0x45, + 0x62, + 0x61, + 0x68, + 0x63, + 0x45, + 0x78, + 0x41, + 0x34, + 0x71, + 0x42, + 0x2f, + 0x4c, + 0x42, + 0x68, + 0x6f, + 0x67, + 0x76, + 0x73, + 0x0a, + 0x34, + 0x7a, + 0x39, + 0x62, + 0x32, + 0x4d, + 0x51, + 0x33, + 0x35, + 0x74, + 0x69, + 0x49, + 0x6f, + 0x51, + 0x44, + 0x6a, + 0x37, + 0x52, + 0x35, + 0x78, + 0x4e, + 0x38, + 0x63, + 0x35, + 0x41, + 0x79, + 0x77, + 0x44, + 0x79, + 0x62, + 0x38, + 0x78, + 0x47, + 0x76, + 0x32, + 0x59, + 0x6d, + 0x6b, + 0x34, + 0x6a, + 0x47, + 0x74, + 0x44, + 0x6b, + 0x64, + 0x71, + 0x72, + 0x62, + 0x74, + 0x49, + 0x55, + 0x34, + 0x32, + 0x2b, + 0x48, + 0x4d, + 0x31, + 0x79, + 0x4f, + 0x6b, + 0x38, + 0x75, + 0x38, + 0x5a, + 0x0a, + 0x45, + 0x69, + 0x78, + 0x4c, + 0x51, + 0x7a, + 0x6f, + 0x77, + 0x6d, + 0x71, + 0x55, + 0x71, + 0x72, + 0x65, + 0x42, + 0x43, + 0x68, + 0x6f, + 0x41, + 0x6f, + 0x73, + 0x63, + 0x33, + 0x46, + 0x4f, + 0x52, + 0x4f, + 0x6d, + 0x54, + 0x43, + 0x44, + 0x6c, + 0x59, + 0x36, + 0x33, + 0x34, + 0x2f, + 0x61, + 0x63, + 0x49, + 0x49, + 0x50, + 0x33, + 0x31, + 0x64, + 0x65, + 0x64, + 0x48, + 0x4e, + 0x59, + 0x5a, + 0x35, + 0x31, + 0x52, + 0x72, + 0x75, + 0x6b, + 0x41, + 0x64, + 0x41, + 0x36, + 0x32, + 0x44, + 0x62, + 0x0a, + 0x47, + 0x44, + 0x77, + 0x67, + 0x77, + 0x7a, + 0x36, + 0x54, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4b, + 0x56, + 0x76, + 0x2f, + 0x53, + 0x43, + 0x50, + 0x4f, + 0x67, + 0x77, + 0x50, + 0x41, + 0x59, + 0x30, + 0x47, + 0x34, + 0x37, + 0x79, + 0x63, + 0x76, + 0x55, + 0x48, + 0x67, + 0x0a, + 0x6b, + 0x33, + 0x44, + 0x51, + 0x67, + 0x6d, + 0x62, + 0x78, + 0x56, + 0x54, + 0x46, + 0x46, + 0x57, + 0x51, + 0x79, + 0x72, + 0x50, + 0x36, + 0x58, + 0x76, + 0x4b, + 0x48, + 0x4f, + 0x6c, + 0x33, + 0x65, + 0x5a, + 0x55, + 0x32, + 0x52, + 0x65, + 0x4a, + 0x78, + 0x65, + 0x54, + 0x78, + 0x43, + 0x4d, + 0x55, + 0x4b, + 0x63, + 0x4e, + 0x6c, + 0x57, + 0x34, + 0x2b, + 0x65, + 0x4d, + 0x32, + 0x6f, + 0x43, + 0x5a, + 0x37, + 0x57, + 0x31, + 0x76, + 0x69, + 0x74, + 0x61, + 0x75, + 0x70, + 0x6e, + 0x4f, + 0x30, + 0x0a, + 0x7a, + 0x4c, + 0x57, + 0x36, + 0x47, + 0x42, + 0x46, + 0x53, + 0x56, + 0x58, + 0x55, + 0x34, + 0x54, + 0x2b, + 0x59, + 0x5a, + 0x63, + 0x69, + 0x49, + 0x48, + 0x6e, + 0x78, + 0x6b, + 0x71, + 0x6c, + 0x48, + 0x33, + 0x58, + 0x78, + 0x63, + 0x6b, + 0x67, + 0x7a, + 0x4e, + 0x56, + 0x4c, + 0x79, + 0x57, + 0x44, + 0x41, + 0x41, + 0x4a, + 0x6f, + 0x61, + 0x77, + 0x32, + 0x63, + 0x2b, + 0x58, + 0x31, + 0x45, + 0x39, + 0x42, + 0x7a, + 0x59, + 0x62, + 0x49, + 0x54, + 0x2b, + 0x7a, + 0x4d, + 0x59, + 0x6a, + 0x5a, + 0x0a, + 0x31, + 0x62, + 0x59, + 0x41, + 0x61, + 0x76, + 0x79, + 0x51, + 0x4f, + 0x6f, + 0x4d, + 0x50, + 0x64, + 0x2f, + 0x31, + 0x7a, + 0x30, + 0x64, + 0x59, + 0x6d, + 0x49, + 0x39, + 0x46, + 0x72, + 0x77, + 0x78, + 0x5a, + 0x32, + 0x4f, + 0x42, + 0x6e, + 0x76, + 0x2b, + 0x63, + 0x58, + 0x41, + 0x72, + 0x69, + 0x30, + 0x2f, + 0x4a, + 0x4d, + 0x47, + 0x54, + 0x63, + 0x7a, + 0x58, + 0x36, + 0x66, + 0x56, + 0x67, + 0x53, + 0x45, + 0x50, + 0x47, + 0x66, + 0x48, + 0x48, + 0x63, + 0x6f, + 0x36, + 0x53, + 0x4b, + 0x4a, + 0x0a, + 0x52, + 0x5a, + 0x57, + 0x38, + 0x56, + 0x49, + 0x39, + 0x67, + 0x49, + 0x4f, + 0x52, + 0x75, + 0x57, + 0x68, + 0x79, + 0x39, + 0x76, + 0x78, + 0x55, + 0x57, + 0x41, + 0x43, + 0x4a, + 0x6f, + 0x30, + 0x4f, + 0x6b, + 0x4a, + 0x44, + 0x75, + 0x55, + 0x37, + 0x2b, + 0x57, + 0x54, + 0x61, + 0x39, + 0x65, + 0x44, + 0x66, + 0x6a, + 0x46, + 0x2b, + 0x62, + 0x62, + 0x68, + 0x2b, + 0x2f, + 0x49, + 0x73, + 0x38, + 0x36, + 0x66, + 0x72, + 0x4c, + 0x32, + 0x67, + 0x76, + 0x38, + 0x79, + 0x39, + 0x69, + 0x4d, + 0x55, + 0x0a, + 0x74, + 0x32, + 0x36, + 0x37, + 0x75, + 0x58, + 0x39, + 0x52, + 0x52, + 0x64, + 0x30, + 0x46, + 0x70, + 0x69, + 0x4b, + 0x70, + 0x2b, + 0x70, + 0x4b, + 0x4d, + 0x48, + 0x6d, + 0x50, + 0x77, + 0x47, + 0x33, + 0x70, + 0x75, + 0x6b, + 0x78, + 0x57, + 0x64, + 0x38, + 0x76, + 0x6b, + 0x4f, + 0x72, + 0x67, + 0x6a, + 0x4a, + 0x44, + 0x46, + 0x2b, + 0x54, + 0x56, + 0x48, + 0x67, + 0x67, + 0x4c, + 0x31, + 0x4a, + 0x59, + 0x58, + 0x5a, + 0x37, + 0x45, + 0x4a, + 0x39, + 0x79, + 0x6a, + 0x50, + 0x64, + 0x77, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +#ifndef BSC_NETWORK_IFACE +#define BSC_NETWORK_IFACE "127.0.0.1" +#endif + +#define BACNET_CLOSED_PORT 65500 +#define BACNET_LOCALHOST "127.0.0.1" +#define BACNET_NODE_LOCAL_HUB_PORT 39005 +#define BACNET_NODE_LOCAL_DIRECT_PORT 39006 +#define BACNET_NODE_LOCAL_HUB_PORT2 39007 +#define BACNET_NODE_LOCAL_DIRECT_PORT2 39008 +#define BACNET_NODE_LOCAL_HUB_PORT3 39009 +#define BACNET_NODE_LOCAL_DIRECT_PORT3 39010 + +#define BACNET_TIMEOUT 3 +#define BACNET_INFINITE_TIMEOUT 0XFFFF + +#define MAX_BVLC_LEN 1500 +#define MAX_NDPU_LEN 1500 +#define MAX_SERVER_SOCKETS BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM +#define MAX_CLIENT_SOCKETS BSC_CONF_CLIENT_CONNECTIONS_NUM +#define WAIT_EVENT_MS 500 + +// MbedTLS expects a key in DER format +#ifdef CONFIG_MBEDTLS +#define NODE_KEY node_key_der +#else +#define NODE_KEY node_key +#endif + +typedef struct { + BSC_NODE_EVENT ev; + BSC_NODE *node; + uint8_t pdu[MAX_BVLC_LEN]; + size_t pdu_len; + BSC_EVENT *e; + BACNET_SC_VMAC_ADDRESS dest; +} node_ev_t; + +static void call_maintenance_timer(bool reset, int time_passed_ms) +{ + static int total_ms; + if (reset) { + total_ms = 0; + } + + total_ms += time_passed_ms; + + if (total_ms >= 1000) { + bsc_node_maintenance_timer(1); + total_ms = 0; + } +} + +static void wait_sec(int seconds) +{ + while (seconds >= 0) { + bsc_wait(1); + bsc_node_maintenance_timer(1); + seconds--; + } +} + +static node_ev_t node_ev; +static node_ev_t node_ev2; +static node_ev_t node_ev3; + +static void init_node_ev(node_ev_t *ev) +{ + memset(ev, 0, sizeof(*ev)); + ev->e = bsc_event_init(); + ev->ev = -1; + zassert_not_equal(ev->e, NULL, 0); +} + +static void deinit_node_ev(node_ev_t *ev) +{ + bsc_event_deinit(ev->e); +} + +static bool wait_node_ev(node_ev_t *ev, BSC_NODE_EVENT wait_ev, BSC_NODE *node) +{ + debug_printf( + "wait_node_ev() >>> ev = %p ev->e = %p ev->ev = %d wait_ev = " + "%d, node = %p\n", + ev, ev->e, ev->ev, wait_ev, node); + call_maintenance_timer(1, 0); + while (!bsc_event_timedwait(ev->e, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + bws_dispatch_lock(); + if (ev->ev == wait_ev && ev->node == node) { + debug_printf( + "wait_node_ev() got event %d and reset ev %p ev->e %p\n", ev->ev, + ev, ev->e); + ev->ev = -1; + // reset event if it was signalled while we were blocked in call + // bws_dispatch_lock(). + // (in that case ev->ev contains code of last event.) + // that's tricky but that allows to avoid using mutexes which are + // platform specific in test code + bsc_event_timedwait(ev->e, 1); + bws_dispatch_unlock(); + debug_printf( + "wait_node_ev() <<< ret = true, ev = %p ev->ev = %d " + "wait_ev = %d, node = %p\n", + ev, ev->ev, wait_ev, node); + return true; + } else { + debug_printf( + "wait_node_ev() got event %d but wait for %d\n", ev->ev, wait_ev); + bws_dispatch_unlock(); + debug_printf( + "wait_node_ev() <<< ret = false, ev = %p ev->e %p ev->ev " + "= %d wait_ev = %d, node = %p\n", + ev, ev->e, ev->ev, wait_ev, node); + return false; + } +} + +static void +wait_specific_node_ev(node_ev_t *ev, BSC_NODE_EVENT wait_ev, BSC_NODE *node) +{ + debug_printf( + "wait_specific_node_ev() >>> ev = %p, wait_ev %d, node = %p\n", ev, + wait_ev, node); + call_maintenance_timer(1, 0); + while (1) { + while (!bsc_event_timedwait(ev->e, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + bws_dispatch_lock(); + if (ev->ev == wait_ev && ev->node == node) { + debug_printf( + "wait_specific_node_ev() got event %d and reset ev %p " + "ev->e %p\n", + ev->ev, ev, ev->e); + ev->ev = -1; + // reset event if it was signalled while we were blocked in call + // bws_dispatch_lock(). (in that case ev->ev contains code of last + // event.) that's tricky but that allows to avoid using mutexes + // which are platform specific in test code + bsc_event_timedwait(ev->e, 1); + bws_dispatch_unlock(); + break; + } else { + bws_dispatch_unlock(); + debug_printf("wait_specific_node_ev() skip event %d\n", ev->ev); + } + } + debug_printf( + "wait_specific_node_ev() <<< ev = %p, wait_ev %d, node = %p\n", ev, + wait_ev, node); +} + +static void wait_for_connection_to_hub(node_ev_t *ev, BSC_NODE *node) +{ + BACNET_SC_HUB_CONNECTION_STATUS *st1; + BACNET_SC_HUB_CONNECTION_STATUS *st2; + call_maintenance_timer(1, 0); + while (1) { + bsc_event_timedwait(ev->e, WAIT_EVENT_MS); + call_maintenance_timer(0, WAIT_EVENT_MS); + + st1 = bsc_node_hub_connector_status(node, true); + st2 = bsc_node_hub_connector_status(node, false); + if (st1 && st1->State == BACNET_SC_CONNECTION_STATE_CONNECTED) { + break; + } + if (st2 && st2->State == BACNET_SC_CONNECTION_STATE_CONNECTED) { + break; + } + } +} +/* +static void wait_for_direct_connection_established(node_ev_t *ev, + BSC_NODE *node, + BACNET_SC_VMAC_ADDRESS *dest, + char **urls, + size_t urls_cnt) +{ + call_maintenance_timer(1, 0); + while (1) { + bsc_event_timedwait(ev->e, WAIT_EVENT_MS); + call_maintenance_timer(0, WAIT_EVENT_MS); + if (bsc_node_direct_connection_established( + node, dest, urls, urls_cnt)) { + break; + } + } +} +*/ +static void signal_node_ev( + node_ev_t *e, + BSC_NODE_EVENT ev, + BSC_NODE *node, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + debug_printf( + "signal_node_ev() >>> e = %p, ev %d, node = %p\n", e, ev, node); + e->ev = ev; + e->node = node; + + zassert_equal(true, pdu_len <= MAX_BVLC_LEN, NULL); + + if (pdu) { + memcpy(e->pdu, pdu, pdu_len); + e->pdu_len = pdu_len; + } + + if (dest) { + debug_printf("dest = %s\n", bsc_vmac_to_string(dest)); + memcpy(&e->dest.address[0], &dest->address[0], sizeof(dest->address)); + debug_printf("e->dest = %s\n", bsc_vmac_to_string(&e->dest)); + } + + bsc_event_signal(e->e); + debug_printf( + "signal_node_ev() <<< e = %p. ev %d, node = %p\n", e, ev, node); +} + +static void node_event( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + bws_dispatch_lock(); + debug_printf( + "node_event() ev = %p, ev->ev = %d ev->e = %p node = %p\n", &node_ev, + ev, node_ev.e, node); + signal_node_ev(&node_ev, ev, node, dest, pdu, pdu_len); + bws_dispatch_unlock(); +} + +static void node_event2( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + bws_dispatch_lock(); + debug_printf( + "node_event2() ev = %p, ev->ev = %d ev->e = %p node = %p\n", &node_ev2, + ev, node_ev2.e, node); + signal_node_ev(&node_ev2, ev, node, dest, pdu, pdu_len); + bws_dispatch_unlock(); +} + +static void node_event3( + BSC_NODE *node, + BSC_NODE_EVENT ev, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len) +{ + bws_dispatch_lock(); + debug_printf( + "node_event3() ev = %p, ev->ev = %d ev->e = %p node = %p\n", &node_ev3, + ev, node_ev3.e, node); + signal_node_ev(&node_ev3, ev, node, dest, pdu, pdu_len); + bws_dispatch_unlock(); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(node_test_1, test_node_start_stop) +#else +static void test_node_start_stop(void) +#endif +{ + BACNET_SC_UUID node_uuid = { 0 }; + BACNET_SC_VMAC_ADDRESS node_vmac = { 0 }; + BSC_NODE_CONF conf = { 0 }; + BSC_SC_RET ret; + BSC_NODE *node = NULL; + BSC_NODE *node1 = NULL; + BSC_NODE *node2 = NULL; + BSC_NODE *node3 = NULL; + + char primary_url[128] = { 0 }; + char secondary_url[128] = { 0 }; + + sprintf(primary_url, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf(secondary_url, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + + conf.ca_cert_chain = ca_cert; + conf.ca_cert_chain_size = sizeof(ca_cert); + conf.cert_chain = node_cert; + conf.cert_chain_size = sizeof(node_cert); + conf.key = node_key; + conf.key_size = sizeof(node_key); + conf.local_uuid = &node_uuid; + conf.local_vmac = node_vmac; + conf.max_local_bvlc_len = MAX_BVLC_LEN; + conf.max_local_npdu_len = MAX_NDPU_LEN; + conf.connect_timeout_s = BACNET_TIMEOUT; + conf.heartbeat_timeout_s = BACNET_TIMEOUT; + conf.disconnect_timeout_s = BACNET_TIMEOUT; + conf.reconnnect_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf.primaryURL = primary_url; + conf.failoverURL = secondary_url; + conf.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf.hub_iface = BSC_NETWORK_IFACE; + conf.direct_iface = BSC_NETWORK_IFACE; + conf.direct_connect_accept_enable = true; + conf.direct_connect_initiate_enable = true; + conf.hub_function_enabled = true; + conf.direct_connection_accept_uris = NULL; + conf.direct_connection_accept_uris_len = 0; + conf.event_func = node_event; + + init_node_ev(&node_ev); + ret = bsc_node_init(NULL, NULL); + zassert_equal(ret, BSC_SC_BAD_PARAM, NULL); + conf.ca_cert_chain = NULL; + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + conf.ca_cert_chain = ca_cert; + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + ret = bsc_node_init(&conf, &node1); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf, &node3); + zassert_equal(ret == BSC_SC_NO_RESOURCES, true, 0); + ret = bsc_node_deinit(node1); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_send(node, (uint8_t *)&node_uuid, sizeof(node_uuid)); + zassert_equal(ret == BSC_SC_INVALID_OPERATION, true, 0); + ret = bsc_node_start(NULL); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + ret = bsc_node_send(NULL, NULL, 0); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + ret = bsc_node_hub_connector_send(NULL, NULL, 0); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + ret = bsc_node_hub_connector_send( + node, (uint8_t *)&node_uuid, sizeof(node_uuid)); + zassert_equal(ret == BSC_SC_INVALID_OPERATION, true, 0); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_INVALID_OPERATION, true, 0); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_INVALID_OPERATION, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + bsc_node_stop(node); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node), true, 0); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(node_test_2, test_node_duplicated_vmac) +#else +static void test_node_duplicated_vmac(void) +#endif +{ + BACNET_SC_UUID node_uuid; + BACNET_SC_VMAC_ADDRESS node_vmac; + BACNET_SC_UUID node_uuid2; + BACNET_SC_VMAC_ADDRESS node_vmac2; + BSC_NODE_CONF conf; + BSC_NODE_CONF conf2; + BSC_SC_RET ret; + BSC_NODE *node; + BSC_NODE *node2; + char node_primary_url[128]; + char node_secondary_url[128]; + char node_primary_url2[128]; + char node_secondary_url2[128]; + char url1[256]; + char url2[256]; + char *urls[2] = { url1, url2 }; + + memset(&node_uuid, 0x1, sizeof(node_uuid)); + memset(&node_vmac, 0x2, sizeof(node_vmac)); + memset(&node_uuid2, 0x3, sizeof(node_uuid2)); + memset(&node_vmac2, 0x2, sizeof(node_vmac2)); + + sprintf( + node_primary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT2); + sprintf( + node_secondary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT2); + sprintf( + node_primary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + + conf.ca_cert_chain = ca_cert; + conf.ca_cert_chain_size = sizeof(ca_cert); + conf.cert_chain = node_cert; + conf.cert_chain_size = sizeof(node_cert); + conf.key = node_key; + conf.key_size = sizeof(node_key); + conf.local_uuid = &node_uuid; + conf.local_vmac = node_vmac; + conf.max_local_bvlc_len = MAX_BVLC_LEN; + conf.max_local_npdu_len = MAX_NDPU_LEN; + conf.connect_timeout_s = BACNET_TIMEOUT; + conf.heartbeat_timeout_s = BACNET_TIMEOUT; + conf.disconnect_timeout_s = BACNET_TIMEOUT; + conf.reconnnect_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf.primaryURL = node_primary_url; + conf.failoverURL = node_secondary_url; + conf.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf.hub_iface = BSC_NETWORK_IFACE; + conf.direct_iface = BSC_NETWORK_IFACE; + conf.direct_connect_accept_enable = true; + conf.direct_connect_initiate_enable = true; + conf.hub_function_enabled = true; + conf.direct_connection_accept_uris = NULL; + conf.direct_connection_accept_uris_len = 0; + conf.event_func = node_event; + + conf2.ca_cert_chain = ca_cert; + conf2.ca_cert_chain_size = sizeof(ca_cert); + conf2.cert_chain = node_cert; + conf2.cert_chain_size = sizeof(node_cert); + conf2.key = node_key; + conf2.key_size = sizeof(node_key); + conf2.local_uuid = &node_uuid2; + conf2.local_vmac = node_vmac2; + conf2.max_local_bvlc_len = MAX_BVLC_LEN; + conf2.max_local_npdu_len = MAX_NDPU_LEN; + conf2.connect_timeout_s = BACNET_TIMEOUT; + conf2.heartbeat_timeout_s = BACNET_TIMEOUT; + conf2.disconnect_timeout_s = BACNET_TIMEOUT; + conf2.reconnnect_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf2.primaryURL = node_primary_url2; + conf2.failoverURL = node_secondary_url2; + conf2.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT2; + conf2.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT2; + conf2.hub_iface = BSC_NETWORK_IFACE; + conf2.direct_iface = BSC_NETWORK_IFACE; + conf2.direct_connect_accept_enable = true; + conf2.direct_connect_initiate_enable = true; + conf2.hub_function_enabled = true; + conf2.direct_connection_accept_uris = NULL; + conf2.direct_connection_accept_uris_len = 0; + conf2.event_func = node_event2; + + init_node_ev(&node_ev); + init_node_ev(&node_ev2); + + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_RESTARTED, node); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_RESTARTED, node2); + bsc_node_stop(node); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + // node switch duplicated vmac + memset(&node_uuid, 0x1, sizeof(node_uuid)); + memset(&node_vmac, 0x2, sizeof(node_vmac)); + memset(&node_uuid2, 0x3, sizeof(node_uuid2)); + memset(&node_vmac2, 0x2, sizeof(node_vmac2)); + + sprintf( + url1, "wss://%s:%d", BACNET_LOCALHOST, BACNET_NODE_LOCAL_DIRECT_PORT); + sprintf( + url2, "wss://%s:%d", BACNET_LOCALHOST, BACNET_NODE_LOCAL_DIRECT_PORT); + + conf.primaryURL = NULL; + conf.failoverURL = NULL; + conf2.primaryURL = NULL; + conf2.failoverURL = NULL; + + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_connect_direct(node2, NULL, urls, 2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_RESTARTED, node); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_RESTARTED, node2); + ret = bsc_node_connect_direct(node2, NULL, urls, 2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_DIRECT_CONNECTED, node2), true, + 0); + bsc_node_stop(node); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev); + deinit_node_ev(&node_ev2); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(node_test_3, test_node_send) +#else +static void test_node_send(void) +#endif +{ + BACNET_SC_UUID node_uuid; + BACNET_SC_VMAC_ADDRESS node_vmac; + BACNET_SC_UUID node_uuid2; + BACNET_SC_VMAC_ADDRESS node_vmac2; + BACNET_SC_UUID node_uuid3; + BACNET_SC_VMAC_ADDRESS node_vmac3; + BSC_NODE_CONF conf; + BSC_NODE_CONF conf2; + BSC_NODE_CONF conf3; + BSC_SC_RET ret; + BSC_NODE *node; + BSC_NODE *node2; + BSC_NODE *node3; + char node_primary_url[128]; + char node_secondary_url[128]; + char node_primary_url2[128]; + char node_secondary_url2[128]; + uint8_t buf[256]; + size_t len; + uint8_t npdu[128]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc; + uint8_t optbuf[128]; + size_t optlen; + + memset(npdu, 0x33, sizeof(npdu)); + memset(&node_uuid, 0x1, sizeof(node_uuid)); + memset(&node_vmac, 0x2, sizeof(node_vmac)); + memset(&node_uuid2, 0x3, sizeof(node_uuid2)); + memset(&node_vmac2, 0x4, sizeof(node_vmac2)); + memset(&node_uuid3, 0x5, sizeof(node_uuid3)); + memset(&node_vmac3, 0x6, sizeof(node_vmac3)); + + sprintf( + node_primary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT2); + sprintf( + node_primary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT2); + + conf.ca_cert_chain = ca_cert; + conf.ca_cert_chain_size = sizeof(ca_cert); + conf.cert_chain = node_cert; + conf.cert_chain_size = sizeof(node_cert); + conf.key = node_key; + conf.key_size = sizeof(node_key); + conf.local_uuid = &node_uuid; + conf.local_vmac = node_vmac; + conf.max_local_bvlc_len = MAX_BVLC_LEN; + conf.max_local_npdu_len = MAX_NDPU_LEN; + conf.connect_timeout_s = BACNET_TIMEOUT; + conf.heartbeat_timeout_s = BACNET_TIMEOUT; + conf.disconnect_timeout_s = BACNET_TIMEOUT; + conf.reconnnect_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf.primaryURL = node_primary_url; + conf.failoverURL = node_secondary_url; + conf.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf.hub_iface = BSC_NETWORK_IFACE; + conf.direct_iface = BSC_NETWORK_IFACE; + conf.direct_connect_accept_enable = true; + conf.direct_connect_initiate_enable = true; + conf.hub_function_enabled = true; + conf.direct_connection_accept_uris = NULL; + conf.direct_connection_accept_uris_len = 0; + conf.event_func = node_event; + + conf2.ca_cert_chain = ca_cert; + conf2.ca_cert_chain_size = sizeof(ca_cert); + conf2.cert_chain = node_cert; + conf2.cert_chain_size = sizeof(node_cert); + conf2.key = NODE_KEY; + conf2.key_size = sizeof(NODE_KEY); + conf2.local_uuid = &node_uuid2; + conf2.local_vmac = node_vmac2; + conf2.max_local_bvlc_len = MAX_BVLC_LEN; + conf2.max_local_npdu_len = MAX_NDPU_LEN; + conf2.connect_timeout_s = BACNET_TIMEOUT; + conf2.heartbeat_timeout_s = BACNET_TIMEOUT; + conf2.disconnect_timeout_s = BACNET_TIMEOUT; + conf2.reconnnect_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf2.primaryURL = node_primary_url2; + conf2.failoverURL = node_secondary_url2; + conf2.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT2; + conf2.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT2; + conf2.hub_iface = BSC_NETWORK_IFACE; + conf2.direct_iface = BSC_NETWORK_IFACE; + conf2.direct_connect_accept_enable = false; + conf2.direct_connect_initiate_enable = false; + conf2.hub_function_enabled = false; + conf2.direct_connection_accept_uris = NULL; + conf2.direct_connection_accept_uris_len = 0; + conf2.event_func = node_event2; + conf3 = conf2; + conf3.event_func = node_event3; + conf3.local_uuid = &node_uuid3; + conf3.local_vmac = node_vmac3; + + init_node_ev(&node_ev); + init_node_ev(&node_ev2); + init_node_ev(&node_ev3); + + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + // wait until hub connectors connects + wait_for_connection_to_hub(&node_ev3, node3); + wait_for_connection_to_hub(&node_ev2, node2); + + // send encapsulated npdu packet + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 111, NULL, &node_vmac2, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + + // send encapsulated npdu packet with proprietary option with must + // understand flag + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, 0x222, 12, NULL, 0); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 111, NULL, &node_vmac2, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_RECEIVED_RESULT, node3), true, + 0); + ret = bvlc_sc_decode_message( + node_ev3.pdu, node_ev3.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.bvlc_function, BVLC_SC_RESULT, NULL); + zassert_equal( + message.payload.result.error_class, ERROR_CLASS_COMMUNICATION, NULL); + zassert_equal( + message.payload.result.error_code, ERROR_CODE_HEADER_NOT_UNDERSTOOD, + NULL); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), false, 0x222, 12, NULL, 0); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 222, NULL, &node_vmac2, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + + // send advertisiment solicitation + + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), 234, NULL, &node_vmac2); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_RECEIVED_ADVERTISIMENT, node3), + true, 0); + ret = bvlc_sc_decode_message( + node_ev3.pdu, node_ev3.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.bvlc_function, BVLC_SC_ADVERTISIMENT, NULL); + zassert_equal( + message.payload.advertisiment.support, + BVLC_SC_DIRECT_CONNECTION_ACCEPT_UNSUPPORTED, NULL); + + bsc_node_stop(node); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node), true, 0); + bsc_node_stop(node2); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2), true, 0); + bsc_node_stop(node3); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3), true, 0); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev); + deinit_node_ev(&node_ev2); + deinit_node_ev(&node_ev3); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(node_test_4, test_node_local_hub_function) +#else +static void test_node_local_hub_function(void) +#endif +{ + BACNET_SC_UUID node_uuid; + BACNET_SC_VMAC_ADDRESS node_vmac; + BACNET_SC_UUID node_uuid2; + BACNET_SC_VMAC_ADDRESS node_vmac2; + BACNET_SC_UUID node_uuid3; + BACNET_SC_VMAC_ADDRESS node_vmac3; + BSC_NODE_CONF conf; + BSC_NODE_CONF conf2; + BSC_NODE_CONF conf3; + BSC_SC_RET ret; + BSC_NODE *node; + BSC_NODE *node2; + BSC_NODE *node3; + char node_primary_url[128]; + char node_secondary_url[128]; + uint8_t buf[256]; + size_t len; + uint8_t npdu[128]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc; + BACNET_SC_VMAC_ADDRESS broadcast; + + memset(&node_uuid, 0x1, sizeof(node_uuid)); + memset(&node_vmac, 0x2, sizeof(node_vmac)); + memset(&node_uuid2, 0x3, sizeof(node_uuid2)); + memset(&node_vmac2, 0x4, sizeof(node_vmac2)); + memset(&node_uuid3, 0x5, sizeof(node_uuid3)); + memset(&node_vmac3, 0x6, sizeof(node_vmac3)); + memset(&broadcast.address[0], 0xff, sizeof(broadcast.address)); + + sprintf( + node_primary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + + conf.ca_cert_chain = ca_cert; + conf.ca_cert_chain_size = sizeof(ca_cert); + conf.cert_chain = node_cert; + conf.cert_chain_size = sizeof(node_cert); + conf.key = node_key; + conf.key_size = sizeof(node_key); + conf.local_uuid = &node_uuid; + conf.local_vmac = node_vmac; + conf.max_local_bvlc_len = MAX_BVLC_LEN; + conf.max_local_npdu_len = MAX_NDPU_LEN; + conf.connect_timeout_s = BACNET_TIMEOUT; + conf.heartbeat_timeout_s = BACNET_TIMEOUT; + conf.disconnect_timeout_s = BACNET_TIMEOUT; + conf.reconnnect_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf.primaryURL = node_primary_url; + conf.failoverURL = node_secondary_url; + conf.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf.hub_iface = BSC_NETWORK_IFACE; + conf.direct_iface = BSC_NETWORK_IFACE; + conf.direct_connect_accept_enable = false; + conf.direct_connect_initiate_enable = false; + conf.hub_function_enabled = true; + conf.direct_connection_accept_uris = NULL; + conf.direct_connection_accept_uris_len = 0; + conf.event_func = node_event; + + conf2.ca_cert_chain = ca_cert; + conf2.ca_cert_chain_size = sizeof(ca_cert); + conf2.cert_chain = node_cert; + conf2.cert_chain_size = sizeof(node_cert); + conf2.key = NODE_KEY; + conf2.key_size = sizeof(NODE_KEY); + conf2.local_uuid = &node_uuid2; + conf2.local_vmac = node_vmac2; + conf2.max_local_bvlc_len = MAX_BVLC_LEN; + conf2.max_local_npdu_len = MAX_NDPU_LEN; + conf2.connect_timeout_s = BACNET_TIMEOUT; + conf2.heartbeat_timeout_s = BACNET_TIMEOUT; + conf2.disconnect_timeout_s = BACNET_TIMEOUT; + conf2.reconnnect_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf2.primaryURL = node_primary_url; + conf2.failoverURL = node_secondary_url; + conf2.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf2.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf2.hub_iface = BSC_NETWORK_IFACE; + conf2.direct_iface = BSC_NETWORK_IFACE; + conf2.direct_connect_accept_enable = false; + conf2.direct_connect_initiate_enable = false; + conf2.hub_function_enabled = false; + conf2.direct_connection_accept_uris = NULL; + conf2.direct_connection_accept_uris_len = 0; + conf2.event_func = node_event2; + conf3 = conf2; + conf3.event_func = node_event3; + conf3.local_uuid = &node_uuid3; + conf3.local_vmac = node_vmac3; + + init_node_ev(&node_ev); + init_node_ev(&node_ev2); + init_node_ev(&node_ev3); + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + + // wait until hub connectors connects + wait_sec(BACNET_TIMEOUT); + // now let's node3 sends broadcast and ensure that node2 and node1 + // receives it. If node 1 is able to receive broadcast that means that + // it's hub connector is connected to local hub function and works + // in proper way + memset(npdu, 0x55, sizeof(npdu)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 7777, NULL, &broadcast, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_RECEIVED_NPDU, node), true, 0); + ret = bvlc_sc_decode_message( + node_ev.pdu, node_ev.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + + bsc_node_stop(node); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node), true, 0); + bsc_node_stop(node2); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2), true, 0); + bsc_node_stop(node3); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3), true, 0); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev); + deinit_node_ev(&node_ev2); + deinit_node_ev(&node_ev3); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(node_test_5, test_node_direct_connection) +#else +static void test_node_direct_connection(void) +#endif +{ + BACNET_SC_UUID node_uuid; + BACNET_SC_VMAC_ADDRESS node_vmac; + BACNET_SC_UUID node_uuid2; + BACNET_SC_VMAC_ADDRESS node_vmac2; + BACNET_SC_UUID node_uuid3; + BACNET_SC_VMAC_ADDRESS node_vmac3; + BSC_NODE_CONF conf; + BSC_NODE_CONF conf2; + BSC_NODE_CONF conf3; + BSC_SC_RET ret; + BSC_NODE *node; + BSC_NODE *node2; + BSC_NODE *node3; + char node_primary_url[128]; + char node_secondary_url[128]; + char node_primary_url2[128]; + char node_secondary_url2[128]; + char node_primary_url3[128]; + char node_secondary_url3[128]; + uint8_t buf[256]; + size_t len; + uint8_t npdu[128]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc; + char uris[256]; + char url1[256]; + char url2[256]; + char *urls[2] = { url1, url2 }; + BACNET_SC_VMAC_ADDRESS broadcast; + uint8_t optbuf[128]; + size_t optlen; + + // test configuration + // node is a just a hub + // node2 connected to node using hub connector is configured + // to accept direct connections + // node3 connected to node using hub connector and initiate + // direct connection to node 2 and then sends data packet + + memset(npdu, 0x33, sizeof(npdu)); + memset(&node_uuid, 0x1, sizeof(node_uuid)); + memset(&node_vmac, 0x2, sizeof(node_vmac)); + memset(&node_uuid2, 0x3, sizeof(node_uuid2)); + memset(&node_vmac2, 0x4, sizeof(node_vmac2)); + memset(&node_uuid3, 0x5, sizeof(node_uuid2)); + memset(&node_vmac3, 0x6, sizeof(node_vmac2)); + memset(&broadcast.address[0], 0xff, sizeof(broadcast.address)); + + sprintf( + node_primary_url, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + node_secondary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + sprintf( + node_primary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + sprintf( + node_primary_url3, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url3, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + + conf.ca_cert_chain = ca_cert; + conf.ca_cert_chain_size = sizeof(ca_cert); + conf.cert_chain = node_cert; + conf.cert_chain_size = sizeof(node_cert); + conf.key = node_key; + conf.key_size = sizeof(node_key); + conf.local_uuid = &node_uuid; + conf.local_vmac = node_vmac; + conf.max_local_bvlc_len = MAX_BVLC_LEN; + conf.max_local_npdu_len = MAX_NDPU_LEN; + conf.connect_timeout_s = BACNET_TIMEOUT; + conf.heartbeat_timeout_s = BACNET_INFINITE_TIMEOUT; + conf.disconnect_timeout_s = BACNET_TIMEOUT; + conf.reconnnect_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf.primaryURL = node_primary_url; + conf.failoverURL = node_secondary_url; + conf.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf.hub_iface = BSC_NETWORK_IFACE; + conf.direct_iface = BSC_NETWORK_IFACE; + conf.direct_connect_accept_enable = false; + conf.direct_connect_initiate_enable = false; + conf.hub_function_enabled = true; + conf.direct_connection_accept_uris = NULL; + conf.direct_connection_accept_uris_len = 0; + conf.event_func = node_event; + + sprintf( + uris, "wss://%s:%d wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_DIRECT_PORT2, BACNET_LOCALHOST, + BACNET_NODE_LOCAL_DIRECT_PORT2); + conf2.ca_cert_chain = ca_cert; + conf2.ca_cert_chain_size = sizeof(ca_cert); + conf2.cert_chain = node_cert; + conf2.cert_chain_size = sizeof(node_cert); + conf2.key = node_key; + conf2.key_size = sizeof(node_key); + conf2.local_uuid = &node_uuid2; + conf2.local_vmac = node_vmac2; + conf2.max_local_bvlc_len = MAX_BVLC_LEN; + conf2.max_local_npdu_len = MAX_NDPU_LEN; + conf2.connect_timeout_s = BACNET_TIMEOUT; + conf2.heartbeat_timeout_s = BACNET_INFINITE_TIMEOUT; + conf2.disconnect_timeout_s = BACNET_TIMEOUT; + conf2.reconnnect_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf2.primaryURL = node_primary_url2; + conf2.failoverURL = node_secondary_url2; + conf2.hub_server_port = 0; + conf2.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT2; + conf2.hub_iface = BSC_NETWORK_IFACE; + conf2.direct_iface = BSC_NETWORK_IFACE; + conf2.direct_connect_accept_enable = true; + conf2.direct_connect_initiate_enable = true; + conf2.hub_function_enabled = false; + conf2.direct_connection_accept_uris = uris; + conf2.direct_connection_accept_uris_len = strlen(uris); + conf2.event_func = node_event2; + + conf3.ca_cert_chain = ca_cert; + conf3.ca_cert_chain_size = sizeof(ca_cert); + conf3.cert_chain = node_cert; + conf3.cert_chain_size = sizeof(node_cert); + conf3.key = NODE_KEY; + conf3.key_size = sizeof(NODE_KEY); + conf3.local_uuid = &node_uuid3; + conf3.local_vmac = node_vmac3; + conf3.max_local_bvlc_len = MAX_BVLC_LEN; + conf3.max_local_npdu_len = MAX_NDPU_LEN; + conf3.connect_timeout_s = BACNET_TIMEOUT; + conf3.heartbeat_timeout_s = BACNET_INFINITE_TIMEOUT; + conf3.disconnect_timeout_s = BACNET_TIMEOUT; + conf3.reconnnect_timeout_s = BACNET_TIMEOUT; + conf3.address_resolution_timeout_s = BACNET_TIMEOUT; + conf3.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf3.primaryURL = node_primary_url3; + conf3.failoverURL = node_secondary_url3; + conf3.hub_server_port = 0; + conf3.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT3; + conf3.hub_iface = BSC_NETWORK_IFACE; + conf3.direct_iface = BSC_NETWORK_IFACE; + conf3.direct_connect_accept_enable = true; + conf3.direct_connect_initiate_enable = true; + conf3.hub_function_enabled = false; + conf3.direct_connection_accept_uris = NULL; + conf3.direct_connection_accept_uris_len = 0; + conf3.event_func = node_event3; + + init_node_ev(&node_ev); + init_node_ev(&node_ev2); + init_node_ev(&node_ev3); + + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + // wait while node3 and node2 connects to node + wait_for_connection_to_hub(&node_ev3, node3); + wait_for_connection_to_hub(&node_ev2, node2); + + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + bsc_node_disconnect_direct(node3, &node_vmac2); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3), true, + 0); + + bsc_node_disconnect_direct(node3, &node_vmac2); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3), + true, 0); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3), true, + 0); + + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 111, NULL, &node_vmac2, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 5555, NULL, &broadcast, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + bsc_node_start(node2); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + wait_for_connection_to_hub(&node_ev2, node2); + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 112, NULL, &node_vmac2, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + bsc_node_stop(node); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3); + bsc_node_stop(node3); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + // wait while node3 and node2 connects to node + wait_for_connection_to_hub(&node_ev3, node3); + wait_for_connection_to_hub(&node_ev2, node2); + + sprintf(url1, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + url2, "wss://%s:%d", BACNET_LOCALHOST, BACNET_NODE_LOCAL_DIRECT_PORT2); + ret = bsc_node_connect_direct(node3, NULL, urls, 2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3), true, + 0); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 111, NULL, &node_vmac2, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + bsc_node_stop(node); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node), true, 0); + bsc_node_stop(node2); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2), true, 0); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + // direct disconnnect test + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + // wait while node3 and node2 connects to node + wait_for_connection_to_hub(&node_ev3, node3); + wait_for_connection_to_hub(&node_ev2, node2); + + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3), true, + 0); + + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + bsc_node_disconnect_direct(node3, &node_vmac2); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3), + true, 0); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3); + + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 111, NULL, &node_vmac2, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node3, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_RECEIVED_NPDU, node2), true, 0); + + ret = bvlc_sc_decode_message( + node_ev2.pdu, node_ev2.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 333, NULL, &node_vmac3, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node2, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_RECEIVED_NPDU, node3), true, 0); + ret = bvlc_sc_decode_message( + node_ev3.pdu, node_ev3.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, 0x222, 12, NULL, 0); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 33352, NULL, &broadcast, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node2, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_sec(1); + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), false, 0x222, 12, NULL, 0); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 3352, NULL, &broadcast, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_equal(len > 0, true, NULL); + ret = bsc_node_send(node2, buf, len); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_RECEIVED_NPDU, node3), true, 0); + ret = bvlc_sc_decode_message( + node_ev3.pdu, node_ev3.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + + bsc_node_stop(node); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node); + + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + + // bad case test (when one url in bsc_node_connect_direct() is incorrect) + sprintf( + url1, "w3sdfsdfsdfss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_DIRECT_PORT2); + sprintf( + url2, "wss://%s:%d", BACNET_LOCALHOST, BACNET_NODE_LOCAL_DIRECT_PORT2); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + // wait while node3 and node2 connects to node + wait_for_connection_to_hub(&node_ev3, node3); + wait_for_connection_to_hub(&node_ev2, node2); + + ret = bsc_node_connect_direct(node3, NULL, urls, 2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3), true, + 0); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + bsc_node_disconnect_direct(node3, &node_vmac2); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3), + true, 0); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + bsc_node_stop(node); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + // bad URLs test + + sprintf( + url1, "w3sdfsdfsdfss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_DIRECT_PORT2); + sprintf( + url2, "wssdfsdfsdfs://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_DIRECT_PORT2); + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + ret = bsc_node_connect_direct(node3, NULL, urls, 2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_sec(3 * BACNET_TIMEOUT); + bsc_node_stop(node); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + sprintf( + node_primary_url, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + node_secondary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + sprintf( + node_primary_url2, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + node_secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + sprintf( + node_primary_url3, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + node_secondary_url3, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + wait_sec(3 * BACNET_TIMEOUT); + bsc_node_stop(node); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + // freshness timeout test + conf.address_resolution_freshness_timeout_s = 1; + conf2.address_resolution_freshness_timeout_s = 1; + conf3.address_resolution_freshness_timeout_s = 1; + sprintf( + node_primary_url, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + node_secondary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + sprintf( + node_primary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + sprintf( + node_primary_url3, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url3, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + // wait while node3 and node2 connects to node + wait_for_connection_to_hub(&node_ev3, node3); + wait_for_connection_to_hub(&node_ev2, node2); + + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3), true, + 0); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + bsc_node_disconnect_direct(node3, &node_vmac2); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3), + true, 0); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + wait_sec(2); + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_CONNECTED, node3), true, + 0); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + bsc_node_disconnect_direct(node3, &node_vmac2); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_DIRECT_DISCONNECTED, node3); + ret = memcmp( + &node_vmac2.address[0], &node_ev3.dest.address[0], + sizeof(node_ev3.dest.address)); + zassert_equal(ret, 0, NULL); + bsc_node_stop(node); + wait_specific_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node); + bsc_node_stop(node2); + wait_specific_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2); + bsc_node_stop(node3); + wait_specific_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev); + deinit_node_ev(&node_ev2); + deinit_node_ev(&node_ev3); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(node_test_6, test_node_direct_connection_unsupported) +#else +static void test_node_direct_connection_unsupported(void) +#endif +{ + BACNET_SC_UUID node_uuid; + BACNET_SC_VMAC_ADDRESS node_vmac; + BACNET_SC_UUID node_uuid2; + BACNET_SC_VMAC_ADDRESS node_vmac2; + BACNET_SC_UUID node_uuid3; + BACNET_SC_VMAC_ADDRESS node_vmac3; + BSC_NODE_CONF conf; + BSC_NODE_CONF conf2; + BSC_NODE_CONF conf3; + BSC_SC_RET ret; + BSC_NODE *node; + BSC_NODE *node2; + BSC_NODE *node3; + char node_primary_url[128]; + char node_secondary_url[128]; + char node_primary_url2[128]; + char node_secondary_url2[128]; + char node_primary_url3[128]; + char node_secondary_url3[128]; + uint8_t npdu[128]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc; + char uris[256]; + // test configuration + // node is a just a hub + // node2 has node switch disabed and connected to node using hub connector + // node3 has node switch disabed connected to node using hub connector and + // initiate + // direct connection to node 2 + + memset(npdu, 0x33, sizeof(npdu)); + memset(&node_uuid, 0x1, sizeof(node_uuid)); + memset(&node_vmac, 0x2, sizeof(node_vmac)); + memset(&node_uuid2, 0x3, sizeof(node_uuid2)); + memset(&node_vmac2, 0x4, sizeof(node_vmac2)); + memset(&node_uuid3, 0x5, sizeof(node_uuid2)); + memset(&node_vmac3, 0x6, sizeof(node_vmac2)); + + sprintf( + node_primary_url, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + node_secondary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + sprintf( + node_primary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url2, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + sprintf( + node_primary_url3, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_HUB_PORT); + sprintf( + node_secondary_url3, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + + conf.ca_cert_chain = ca_cert; + conf.ca_cert_chain_size = sizeof(ca_cert); + conf.cert_chain = node_cert; + conf.cert_chain_size = sizeof(node_cert); + conf.key = node_key; + conf.key_size = sizeof(node_key); + conf.local_uuid = &node_uuid; + conf.local_vmac = node_vmac; + conf.max_local_bvlc_len = MAX_BVLC_LEN; + conf.max_local_npdu_len = MAX_NDPU_LEN; + conf.connect_timeout_s = BACNET_TIMEOUT; + conf.heartbeat_timeout_s = BACNET_INFINITE_TIMEOUT; + conf.disconnect_timeout_s = BACNET_TIMEOUT; + conf.reconnnect_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf.primaryURL = node_primary_url; + conf.failoverURL = node_secondary_url; + conf.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf.hub_iface = BSC_NETWORK_IFACE; + conf.direct_iface = BSC_NETWORK_IFACE; + conf.direct_connect_accept_enable = false; + conf.direct_connect_initiate_enable = false; + conf.hub_function_enabled = true; + conf.direct_connection_accept_uris = NULL; + conf.direct_connection_accept_uris_len = 0; + conf.event_func = node_event; + + sprintf( + uris, "wss://%s:%d wss://%s:%d", BACNET_LOCALHOST, + BACNET_NODE_LOCAL_DIRECT_PORT2, BACNET_LOCALHOST, + BACNET_NODE_LOCAL_DIRECT_PORT2); + conf2.ca_cert_chain = ca_cert; + conf2.ca_cert_chain_size = sizeof(ca_cert); + conf2.cert_chain = node_cert; + conf2.cert_chain_size = sizeof(node_cert); + conf2.key = NODE_KEY; + conf2.key_size = sizeof(NODE_KEY); + conf2.local_uuid = &node_uuid2; + conf2.local_vmac = node_vmac2; + conf2.max_local_bvlc_len = MAX_BVLC_LEN; + conf2.max_local_npdu_len = MAX_NDPU_LEN; + conf2.connect_timeout_s = BACNET_TIMEOUT; + conf2.heartbeat_timeout_s = BACNET_INFINITE_TIMEOUT; + conf2.disconnect_timeout_s = BACNET_TIMEOUT; + conf2.reconnnect_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_timeout_s = BACNET_TIMEOUT; + conf2.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf2.primaryURL = node_primary_url2; + conf2.failoverURL = node_secondary_url2; + conf2.hub_server_port = 0; + conf2.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT2; + conf2.hub_iface = BSC_NETWORK_IFACE; + conf2.direct_iface = BSC_NETWORK_IFACE; + conf2.direct_connect_accept_enable = false; + conf2.direct_connect_initiate_enable = false; + conf2.hub_function_enabled = false; + conf2.direct_connection_accept_uris = uris; + conf2.direct_connection_accept_uris_len = strlen(uris); + conf2.event_func = node_event2; + + conf3.ca_cert_chain = ca_cert; + conf3.ca_cert_chain_size = sizeof(ca_cert); + conf3.cert_chain = node_cert; + conf3.cert_chain_size = sizeof(node_cert); + conf3.key = NODE_KEY; + conf3.key_size = sizeof(NODE_KEY); + conf3.local_uuid = &node_uuid3; + conf3.local_vmac = node_vmac3; + conf3.max_local_bvlc_len = MAX_BVLC_LEN; + conf3.max_local_npdu_len = MAX_NDPU_LEN; + conf3.connect_timeout_s = BACNET_TIMEOUT; + conf3.heartbeat_timeout_s = BACNET_INFINITE_TIMEOUT; + conf3.disconnect_timeout_s = BACNET_TIMEOUT; + conf3.reconnnect_timeout_s = BACNET_TIMEOUT; + conf3.address_resolution_timeout_s = BACNET_TIMEOUT; + conf3.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf3.primaryURL = node_primary_url3; + conf3.failoverURL = node_secondary_url3; + conf3.hub_server_port = 0; + conf3.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT3; + conf3.hub_iface = BSC_NETWORK_IFACE; + conf3.direct_iface = BSC_NETWORK_IFACE; + conf3.direct_connect_accept_enable = true; + conf3.direct_connect_initiate_enable = true; + conf3.hub_function_enabled = false; + conf3.direct_connection_accept_uris = NULL; + conf3.direct_connection_accept_uris_len = 0; + conf3.event_func = node_event3; + + init_node_ev(&node_ev); + init_node_ev(&node_ev2); + init_node_ev(&node_ev3); + + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf2, &node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_init(&conf3, &node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_start(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STARTED, node2), true, 0); + ret = bsc_node_start(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STARTED, node3), true, 0); + // wait while node3 and node2 connects to node + wait_for_connection_to_hub(&node_ev3, node3); + wait_for_connection_to_hub(&node_ev2, node2); + + ret = bsc_node_connect_direct(node3, &node_vmac2, NULL, 0); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_RECEIVED_RESULT, node3), true, + 0); + ret = bvlc_sc_decode_message( + node_ev3.pdu, node_ev3.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.bvlc_function, BVLC_SC_RESULT, NULL); + zassert_equal( + message.payload.result.error_class, ERROR_CLASS_COMMUNICATION, NULL); + zassert_equal( + message.payload.result.error_code, + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED, NULL); + bsc_node_stop(node); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node), true, 0); + bsc_node_stop(node2); + zassert_equal( + wait_node_ev(&node_ev2, BSC_NODE_EVENT_STOPPED, node2), true, 0); + bsc_node_stop(node3); + zassert_equal( + wait_node_ev(&node_ev3, BSC_NODE_EVENT_STOPPED, node3), true, 0); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node2); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_deinit(node3); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev); + deinit_node_ev(&node_ev2); + deinit_node_ev(&node_ev3); +} + +static void node_switch_event( + BSC_NODE_SWITCH_EVENT ev, + BSC_NODE_SWITCH_HANDLE h, + void *user_arg, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + (void)ev; + (void)h; + (void)user_arg; + (void)dest; + (void)pdu; + (void)pdu_len; + (void)decoded_pdu; +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(node_test_7, test_node_bad_cases) +#else +static void test_node_bad_cases(void) +#endif +{ + BSC_SC_RET ret; + BSC_NODE *node = NULL; + BSC_NODE_CONF conf = { 0 }; + BACNET_SC_UUID node_uuid = { 0 }; + BACNET_SC_VMAC_ADDRESS node_vmac = { 0 }; + char node_primary_url[128] = { 0 }; + char node_secondary_url[128] = { 0 }; + char big_url[2 * BSC_WSURL_MAX_LEN] = { 0 }; + BSC_ADDRESS_RESOLUTION *r = NULL; + char *url[1] = { node_primary_url }; + char t[2 * BSC_CONF_NODE_MAX_URI_SIZE_IN_ADDRESS_RESOLUTION_ACK] = { 0 }; + char *long_url[1] = { t }; + BSC_NODE_SWITCH_HANDLE sh[4] = { 0 }; + BSC_SC_RET res[4] = { 0 }; + int i = 0; + + memset(&node_uuid, 0x1, sizeof(node_uuid)); + memset(&node_vmac, 0x2, sizeof(node_vmac)); + memset(big_url, 0x22, sizeof(big_url) - 1); + memset(t, 0x22, sizeof(t) - 1); + + sprintf( + node_primary_url, "wss://%s:%d", BACNET_LOCALHOST, BACNET_CLOSED_PORT); + sprintf( + node_secondary_url, "wss://%s:%d", BACNET_LOCALHOST, + BACNET_CLOSED_PORT); + + conf.ca_cert_chain = ca_cert; + conf.ca_cert_chain_size = sizeof(ca_cert); + conf.cert_chain = node_cert; + conf.cert_chain_size = sizeof(node_cert); + conf.key = node_key; + conf.key_size = sizeof(node_key); + conf.local_uuid = &node_uuid; + conf.local_vmac = node_vmac; + conf.max_local_bvlc_len = MAX_BVLC_LEN; + conf.max_local_npdu_len = MAX_NDPU_LEN; + conf.connect_timeout_s = BACNET_TIMEOUT; + conf.heartbeat_timeout_s = BACNET_INFINITE_TIMEOUT; + conf.disconnect_timeout_s = BACNET_TIMEOUT; + conf.reconnnect_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf.primaryURL = big_url; + conf.failoverURL = node_secondary_url; + conf.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf.hub_iface = BSC_NETWORK_IFACE; + conf.direct_iface = BSC_NETWORK_IFACE; + conf.direct_connect_accept_enable = true; + conf.direct_connect_initiate_enable = true; + conf.hub_function_enabled = true; + conf.direct_connection_accept_uris = NULL; + conf.direct_connection_accept_uris_len = 0; + conf.event_func = node_event; + + init_node_ev(&node_ev); + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + r = bsc_node_get_address_resolution(NULL, &node_vmac); + zassert_equal(r == NULL, true, 0); + r = bsc_node_get_address_resolution(NULL, NULL); + zassert_equal(r == NULL, true, 0); + r = bsc_node_get_address_resolution(node, NULL); + zassert_equal(r == NULL, true, 0); + conf.cert_chain_size = 20; + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + conf.hub_function_enabled = false; + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + conf.hub_function_enabled = false; + ret = bsc_node_switch_start( + NULL, 0, NULL, 0, NULL, 0, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, + false, false, NULL, NULL); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + bsc_node_deinit(node); + + ret = bsc_node_switch_start( + NULL, 0, NULL, 0, NULL, 0, 0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, + false, false, NULL, NULL); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + ret = bsc_node_switch_start( + ca_cert, sizeof(ca_cert), node_cert, sizeof(node_cert), node_key, + sizeof(node_key), BACNET_NODE_LOCAL_DIRECT_PORT, NULL, &node_uuid, + &node_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, BACNET_TIMEOUT, BACNET_TIMEOUT, + BACNET_TIMEOUT, BACNET_TIMEOUT, BACNET_TIMEOUT, false, false, + node_switch_event, NULL, &sh[0]); + + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + for (i = 0; i < sizeof(sh) / sizeof(sh[0]); i++) { + res[i] = bsc_node_switch_start( + ca_cert, sizeof(ca_cert), node_cert, sizeof(node_cert), node_key, + sizeof(node_key), BACNET_NODE_LOCAL_DIRECT_PORT + i, NULL, + &node_uuid, &node_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, BACNET_TIMEOUT, + BACNET_TIMEOUT, BACNET_TIMEOUT, BACNET_TIMEOUT, BACNET_TIMEOUT, + false, true, node_switch_event, NULL, &sh[i]); + } + zassert_equal(res[0] == BSC_SC_SUCCESS, true, 0); + zassert_equal(res[1] == BSC_SC_SUCCESS, true, 0); + zassert_equal(res[2] == BSC_SC_SUCCESS, true, 0); + zassert_equal(res[3] == BSC_SC_NO_RESOURCES, true, 0); + ret = bsc_node_switch_started(sh[0]); + zassert_equal(ret == true, true, 0); + bsc_node_switch_stop(sh[0]); + bsc_node_switch_stop(sh[1]); + bsc_node_switch_stop(sh[2]); + ret = bsc_node_switch_started(sh[0]); + zassert_equal(ret == false, true, 0); + ret = bsc_node_switch_stopped(sh[0]); + zassert_equal(ret == true, true, 0); + ret = bsc_node_switch_start( + NULL, sizeof(ca_cert), node_cert, sizeof(node_cert), node_key, + sizeof(node_key), BACNET_NODE_LOCAL_DIRECT_PORT, NULL, &node_uuid, + &node_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, BACNET_TIMEOUT, BACNET_TIMEOUT, + BACNET_TIMEOUT, BACNET_TIMEOUT, BACNET_TIMEOUT, true, false, + node_switch_event, NULL, &sh[0]); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + ret = bsc_node_switch_start( + ca_cert, sizeof(ca_cert), node_cert, sizeof(node_cert), node_key, + sizeof(node_key), BACNET_NODE_LOCAL_DIRECT_PORT + 1, NULL, &node_uuid, + &node_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, BACNET_TIMEOUT, BACNET_TIMEOUT, + BACNET_TIMEOUT, BACNET_TIMEOUT, BACNET_TIMEOUT, true, false, + node_switch_event, NULL, &sh[0]); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + bsc_node_switch_stop(sh[0]); + ret = bsc_node_switch_started(sh[0]); + zassert_equal(ret == false, true, 0); + conf.ca_cert_chain = ca_cert; + conf.ca_cert_chain_size = sizeof(ca_cert); + conf.cert_chain = node_cert; + conf.cert_chain_size = sizeof(node_cert); + conf.key = node_key; + conf.key_size = sizeof(node_key); + conf.local_uuid = &node_uuid; + conf.local_vmac = node_vmac; + conf.max_local_bvlc_len = MAX_BVLC_LEN; + conf.max_local_npdu_len = MAX_NDPU_LEN; + conf.connect_timeout_s = BACNET_TIMEOUT; + conf.heartbeat_timeout_s = BACNET_INFINITE_TIMEOUT; + conf.disconnect_timeout_s = BACNET_TIMEOUT; + conf.reconnnect_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_timeout_s = BACNET_TIMEOUT; + conf.address_resolution_freshness_timeout_s = BACNET_TIMEOUT; + conf.primaryURL = node_primary_url; + conf.failoverURL = node_secondary_url; + conf.hub_server_port = BACNET_NODE_LOCAL_HUB_PORT; + conf.direct_server_port = BACNET_NODE_LOCAL_DIRECT_PORT; + conf.hub_iface = BSC_NETWORK_IFACE; + conf.direct_iface = BSC_NETWORK_IFACE; + conf.direct_connect_accept_enable = true; + conf.direct_connect_initiate_enable = true; + conf.hub_function_enabled = true; + conf.direct_connection_accept_uris = NULL; + conf.direct_connection_accept_uris_len = 0; + conf.event_func = node_event; + ret = bsc_node_init(&conf, &node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + ret = bsc_node_start(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STARTED, node), true, 0); + ret = bsc_node_connect_direct(node, NULL, NULL, 0); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + ret = bsc_node_connect_direct(node, &node_vmac, url, 1); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + ret = bsc_node_connect_direct(node, &node_vmac, long_url, 1); + zassert_equal(ret == BSC_SC_BAD_PARAM, true, 0); + bsc_node_stop(node); + zassert_equal( + wait_node_ev(&node_ev, BSC_NODE_EVENT_STOPPED, node), true, 0); + ret = bsc_node_deinit(node); + zassert_equal(ret == BSC_SC_SUCCESS, true, 0); + deinit_node_ev(&node_ev); +} + +#if defined(CONFIG_ZTEST_NEW_API) +static void *suite_setup(void) +{ + setbuf(stdout, NULL); + return NULL; +} + +ZTEST_SUITE(node_test_1, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(node_test_2, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(node_test_3, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(node_test_4, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(node_test_5, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(node_test_6, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(node_test_7, NULL, suite_setup, NULL, NULL, NULL); +#else +void test_main(void) +{ + // setbuf(stdout, NULL); + // Tests must not be run in parallel threads! + // Thats why tests functions are in different suites. + ztest_test_suite(node_test_1, ztest_unit_test(test_node_start_stop)); + ztest_test_suite(node_test_2, ztest_unit_test(test_node_duplicated_vmac)); + ztest_test_suite(node_test_3, ztest_unit_test(test_node_send)); + ztest_test_suite( + node_test_4, ztest_unit_test(test_node_local_hub_function)); + ztest_test_suite(node_test_5, ztest_unit_test(test_node_direct_connection)); + ztest_test_suite( + node_test_6, ztest_unit_test(test_node_direct_connection_unsupported)); + ztest_test_suite(node_test_7, ztest_unit_test(test_node_bad_cases)); + ztest_run_test_suite(node_test_1); + ztest_run_test_suite(node_test_2); + ztest_run_test_suite(node_test_3); + ztest_run_test_suite(node_test_4); + ztest_run_test_suite(node_test_5); + ztest_run_test_suite(node_test_6); + ztest_run_test_suite(node_test_7); +} +#endif diff --git a/test/bacnet/datalink/bsc-socket/CMakeLists.txt b/test/bacnet/datalink/bsc-socket/CMakeLists.txt new file mode 100644 index 00000000..c4d38eb5 --- /dev/null +++ b/test/bacnet/datalink/bsc-socket/CMakeLists.txt @@ -0,0 +1,274 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BACDL_BSC + BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM=4 + BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM=4 + BSC_CONF_WSURL_MAX_LEN=128 + BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN=128 + BSC_CONF_WEBSOCKET_SERVERS_NUM=4 + MAX_TSM_TRANSACTIONS=0 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +if(ZEPHYR_BASE) + message(FATAL_ERROR "ZEPHYR_BASE env variable defined. Use zephyr/CMakeLists.txt for Zephyr build") +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(STATUS "BACNet/SC socket test: building for linux") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + add_compile_definitions(BACNET_PORT=linux) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/linux/websocket-cli.c + ${PORTS_DIR}/linux/websocket-srv.c + ${PORTS_DIR}/linux/websocket-global.c + ${PORTS_DIR}/linux/bsc-event.c + ${PORTS_DIR}/linux/mstimer-init.c + ${PORTS_DIR}/linux/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) +elseif(WIN32) + message(STATUS "BACNet/SC socket test: building for win32") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/win32) + add_compile_definitions(BACNET_PORT=win32) + add_compile_definitions(BACNET_STACK_STATIC_DEFINE) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + find_package(OpenSSL) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/win32/websocket-cli.c + ${PORTS_DIR}/win32/websocket-srv.c + ${PORTS_DIR}/win32/websocket-global.c + ${PORTS_DIR}/win32/bsc-event.c + ${PORTS_DIR}/win32/mstimer-init.c + ${PORTS_DIR}/win32/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + + # basically if you use vcpkg you should just add ${LIBWEBSOCKETS_LIBRARIES} + # into target_link_libraries() but for some reason it does not work as expected + # so that's why libs have to be hardcoded as workaround + target_link_libraries(${PROJECT_NAME} + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\websockets.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libssl.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libcrypto.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\pthreadVC3.lib + ws2_32.lib + userenv.lib + psapi.lib + iphlpapi.lib + crypt32.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\zlib.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\uv.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + comdlg32.lib + advapi32.lib + ) +elseif(APPLE) + message(STATUS "BACNet/SC socket test: building for APPLE") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/bsd) + execute_process ( + COMMAND bash -c "brew --prefix openssl" + OUTPUT_VARIABLE OPEN_SSL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE + ) + include_directories(${OPEN_SSL_DIR}/include) + add_compile_definitions(BACNET_PORT=bsd) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/bsd/websocket-cli.c + ${PORTS_DIR}/bsd/websocket-srv.c + ${PORTS_DIR}/bsd/websocket-global.c + ${PORTS_DIR}/bsd/bsc-event.c + ${PORTS_DIR}/bsd/mstimer-init.c + ${PORTS_DIR}/bsd/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/wp.c + # Test and test library files + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) +endif() diff --git a/test/bacnet/datalink/bsc-socket/src/main.c b/test/bacnet/datalink/bsc-socket/src/main.c new file mode 100644 index 00000000..ccd28978 --- /dev/null +++ b/test/bacnet/datalink/bsc-socket/src/main.c @@ -0,0 +1,10668 @@ +/* + * Copyright (c) 2020 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +/* @file + * @brief test of bsc-socket interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned char ca_key[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x6f, + 0x67, + 0x49, + 0x42, + 0x41, + 0x41, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x45, + 0x41, + 0x31, + 0x66, + 0x7a, + 0x73, + 0x2b, + 0x56, + 0x62, + 0x46, + 0x39, + 0x31, + 0x65, + 0x45, + 0x66, + 0x4c, + 0x32, + 0x67, + 0x44, + 0x63, + 0x6a, + 0x79, + 0x44, + 0x47, + 0x68, + 0x62, + 0x4d, + 0x6f, + 0x44, + 0x48, + 0x6c, + 0x71, + 0x53, + 0x6d, + 0x70, + 0x78, + 0x7a, + 0x45, + 0x2b, + 0x4a, + 0x48, + 0x34, + 0x6f, + 0x46, + 0x47, + 0x46, + 0x42, + 0x31, + 0x47, + 0x48, + 0x0a, + 0x69, + 0x35, + 0x47, + 0x7a, + 0x33, + 0x73, + 0x47, + 0x51, + 0x4d, + 0x6e, + 0x41, + 0x6e, + 0x32, + 0x4c, + 0x41, + 0x6e, + 0x51, + 0x53, + 0x5a, + 0x72, + 0x46, + 0x77, + 0x6c, + 0x77, + 0x72, + 0x48, + 0x34, + 0x68, + 0x43, + 0x36, + 0x62, + 0x6d, + 0x4a, + 0x33, + 0x71, + 0x55, + 0x58, + 0x72, + 0x61, + 0x32, + 0x64, + 0x76, + 0x63, + 0x45, + 0x34, + 0x2f, + 0x32, + 0x2f, + 0x70, + 0x74, + 0x6b, + 0x34, + 0x31, + 0x47, + 0x39, + 0x68, + 0x75, + 0x45, + 0x4f, + 0x75, + 0x77, + 0x69, + 0x6b, + 0x68, + 0x0a, + 0x7a, + 0x5a, + 0x59, + 0x65, + 0x67, + 0x6b, + 0x4b, + 0x68, + 0x62, + 0x78, + 0x46, + 0x70, + 0x77, + 0x56, + 0x2f, + 0x76, + 0x48, + 0x32, + 0x48, + 0x6f, + 0x55, + 0x45, + 0x39, + 0x30, + 0x4d, + 0x43, + 0x56, + 0x7a, + 0x66, + 0x59, + 0x56, + 0x51, + 0x62, + 0x6a, + 0x38, + 0x51, + 0x2f, + 0x73, + 0x51, + 0x50, + 0x6d, + 0x79, + 0x4f, + 0x6d, + 0x64, + 0x76, + 0x46, + 0x74, + 0x4a, + 0x41, + 0x4e, + 0x47, + 0x53, + 0x2f, + 0x52, + 0x58, + 0x31, + 0x67, + 0x6b, + 0x2b, + 0x6e, + 0x7a, + 0x67, + 0x38, + 0x0a, + 0x54, + 0x4f, + 0x33, + 0x6f, + 0x4a, + 0x45, + 0x6b, + 0x70, + 0x69, + 0x6e, + 0x67, + 0x34, + 0x45, + 0x31, + 0x4c, + 0x4a, + 0x2b, + 0x63, + 0x48, + 0x66, + 0x45, + 0x66, + 0x4a, + 0x45, + 0x49, + 0x5a, + 0x4f, + 0x4d, + 0x69, + 0x4e, + 0x69, + 0x6a, + 0x31, + 0x4d, + 0x72, + 0x50, + 0x70, + 0x32, + 0x4d, + 0x63, + 0x36, + 0x4d, + 0x49, + 0x7a, + 0x54, + 0x6f, + 0x76, + 0x4c, + 0x6f, + 0x51, + 0x31, + 0x38, + 0x36, + 0x2f, + 0x68, + 0x63, + 0x59, + 0x50, + 0x36, + 0x62, + 0x4f, + 0x76, + 0x46, + 0x59, + 0x0a, + 0x6d, + 0x59, + 0x78, + 0x68, + 0x59, + 0x46, + 0x30, + 0x68, + 0x52, + 0x38, + 0x4f, + 0x57, + 0x53, + 0x4c, + 0x30, + 0x55, + 0x45, + 0x78, + 0x69, + 0x4e, + 0x6f, + 0x64, + 0x45, + 0x2f, + 0x61, + 0x70, + 0x68, + 0x7a, + 0x39, + 0x41, + 0x56, + 0x52, + 0x63, + 0x6d, + 0x79, + 0x62, + 0x76, + 0x62, + 0x4f, + 0x6c, + 0x77, + 0x32, + 0x4c, + 0x71, + 0x44, + 0x50, + 0x42, + 0x43, + 0x62, + 0x46, + 0x6b, + 0x72, + 0x50, + 0x75, + 0x4c, + 0x7a, + 0x6d, + 0x6e, + 0x47, + 0x6b, + 0x78, + 0x46, + 0x57, + 0x41, + 0x0a, + 0x45, + 0x42, + 0x65, + 0x52, + 0x38, + 0x51, + 0x43, + 0x33, + 0x77, + 0x72, + 0x57, + 0x6a, + 0x6e, + 0x6a, + 0x34, + 0x30, + 0x71, + 0x66, + 0x6e, + 0x33, + 0x78, + 0x50, + 0x6c, + 0x4e, + 0x41, + 0x73, + 0x57, + 0x42, + 0x64, + 0x7a, + 0x52, + 0x2f, + 0x64, + 0x32, + 0x43, + 0x6d, + 0x63, + 0x51, + 0x49, + 0x44, + 0x41, + 0x51, + 0x41, + 0x42, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x43, + 0x58, + 0x49, + 0x78, + 0x7a, + 0x38, + 0x76, + 0x70, + 0x7a, + 0x30, + 0x4a, + 0x59, + 0x77, + 0x64, + 0x7a, + 0x0a, + 0x70, + 0x44, + 0x4e, + 0x4b, + 0x37, + 0x42, + 0x4a, + 0x73, + 0x79, + 0x73, + 0x32, + 0x63, + 0x46, + 0x36, + 0x48, + 0x74, + 0x36, + 0x4d, + 0x39, + 0x52, + 0x4d, + 0x53, + 0x61, + 0x43, + 0x2f, + 0x39, + 0x65, + 0x76, + 0x44, + 0x55, + 0x4a, + 0x42, + 0x6a, + 0x79, + 0x47, + 0x42, + 0x31, + 0x4c, + 0x54, + 0x63, + 0x6b, + 0x4d, + 0x32, + 0x58, + 0x4b, + 0x44, + 0x49, + 0x47, + 0x79, + 0x4b, + 0x65, + 0x6b, + 0x56, + 0x50, + 0x78, + 0x34, + 0x57, + 0x6b, + 0x44, + 0x61, + 0x39, + 0x4a, + 0x4c, + 0x4f, + 0x0a, + 0x49, + 0x59, + 0x32, + 0x50, + 0x41, + 0x32, + 0x76, + 0x46, + 0x37, + 0x32, + 0x6f, + 0x4b, + 0x4b, + 0x2f, + 0x37, + 0x6c, + 0x36, + 0x31, + 0x56, + 0x57, + 0x76, + 0x63, + 0x59, + 0x6b, + 0x39, + 0x4b, + 0x68, + 0x49, + 0x71, + 0x79, + 0x37, + 0x31, + 0x66, + 0x46, + 0x61, + 0x45, + 0x7a, + 0x31, + 0x5a, + 0x49, + 0x31, + 0x61, + 0x42, + 0x36, + 0x2f, + 0x71, + 0x56, + 0x36, + 0x66, + 0x77, + 0x71, + 0x58, + 0x69, + 0x79, + 0x48, + 0x44, + 0x4a, + 0x63, + 0x7a, + 0x71, + 0x6a, + 0x2f, + 0x33, + 0x31, + 0x0a, + 0x38, + 0x45, + 0x48, + 0x48, + 0x4f, + 0x51, + 0x55, + 0x44, + 0x4d, + 0x59, + 0x34, + 0x2f, + 0x4f, + 0x55, + 0x46, + 0x2f, + 0x56, + 0x37, + 0x6f, + 0x6f, + 0x4b, + 0x64, + 0x31, + 0x33, + 0x67, + 0x35, + 0x72, + 0x7a, + 0x4c, + 0x41, + 0x50, + 0x47, + 0x4e, + 0x74, + 0x43, + 0x37, + 0x6e, + 0x2b, + 0x56, + 0x6b, + 0x6a, + 0x41, + 0x31, + 0x31, + 0x78, + 0x38, + 0x4f, + 0x6c, + 0x32, + 0x57, + 0x62, + 0x48, + 0x33, + 0x64, + 0x6a, + 0x31, + 0x78, + 0x4f, + 0x47, + 0x2f, + 0x66, + 0x50, + 0x52, + 0x57, + 0x0a, + 0x72, + 0x63, + 0x69, + 0x57, + 0x46, + 0x61, + 0x79, + 0x4a, + 0x72, + 0x33, + 0x71, + 0x56, + 0x6c, + 0x5a, + 0x2b, + 0x42, + 0x79, + 0x31, + 0x66, + 0x44, + 0x43, + 0x59, + 0x38, + 0x31, + 0x4d, + 0x73, + 0x55, + 0x56, + 0x41, + 0x65, + 0x55, + 0x57, + 0x2f, + 0x54, + 0x33, + 0x6b, + 0x39, + 0x42, + 0x6e, + 0x4b, + 0x67, + 0x36, + 0x68, + 0x71, + 0x61, + 0x33, + 0x4a, + 0x47, + 0x36, + 0x37, + 0x4e, + 0x41, + 0x36, + 0x42, + 0x72, + 0x63, + 0x7a, + 0x4e, + 0x4b, + 0x53, + 0x4b, + 0x47, + 0x79, + 0x37, + 0x0a, + 0x70, + 0x70, + 0x56, + 0x56, + 0x41, + 0x65, + 0x61, + 0x47, + 0x44, + 0x49, + 0x73, + 0x37, + 0x62, + 0x73, + 0x43, + 0x7a, + 0x52, + 0x70, + 0x45, + 0x42, + 0x43, + 0x67, + 0x4e, + 0x68, + 0x52, + 0x42, + 0x5a, + 0x45, + 0x2f, + 0x41, + 0x79, + 0x65, + 0x77, + 0x4d, + 0x56, + 0x6e, + 0x42, + 0x4a, + 0x62, + 0x4b, + 0x41, + 0x72, + 0x6e, + 0x72, + 0x79, + 0x69, + 0x69, + 0x38, + 0x2f, + 0x5a, + 0x62, + 0x56, + 0x51, + 0x55, + 0x4d, + 0x4c, + 0x56, + 0x48, + 0x32, + 0x70, + 0x68, + 0x34, + 0x46, + 0x69, + 0x0a, + 0x6b, + 0x2b, + 0x30, + 0x56, + 0x66, + 0x56, + 0x45, + 0x43, + 0x67, + 0x59, + 0x45, + 0x41, + 0x2f, + 0x5a, + 0x45, + 0x39, + 0x35, + 0x71, + 0x4b, + 0x46, + 0x6d, + 0x71, + 0x51, + 0x65, + 0x4f, + 0x36, + 0x6d, + 0x76, + 0x42, + 0x37, + 0x6b, + 0x56, + 0x6b, + 0x70, + 0x56, + 0x4b, + 0x53, + 0x58, + 0x47, + 0x75, + 0x2f, + 0x59, + 0x35, + 0x6b, + 0x46, + 0x63, + 0x70, + 0x4c, + 0x57, + 0x4f, + 0x63, + 0x35, + 0x45, + 0x56, + 0x48, + 0x4c, + 0x36, + 0x46, + 0x6d, + 0x66, + 0x7a, + 0x62, + 0x71, + 0x42, + 0x0a, + 0x41, + 0x78, + 0x6f, + 0x71, + 0x39, + 0x48, + 0x68, + 0x66, + 0x4c, + 0x79, + 0x39, + 0x61, + 0x37, + 0x4e, + 0x7a, + 0x73, + 0x61, + 0x43, + 0x77, + 0x30, + 0x66, + 0x34, + 0x56, + 0x79, + 0x5a, + 0x72, + 0x73, + 0x4f, + 0x61, + 0x73, + 0x67, + 0x42, + 0x31, + 0x43, + 0x42, + 0x5a, + 0x50, + 0x36, + 0x30, + 0x32, + 0x43, + 0x47, + 0x69, + 0x38, + 0x68, + 0x62, + 0x48, + 0x61, + 0x6a, + 0x4a, + 0x4d, + 0x77, + 0x4a, + 0x67, + 0x37, + 0x42, + 0x39, + 0x64, + 0x38, + 0x52, + 0x71, + 0x62, + 0x67, + 0x35, + 0x0a, + 0x76, + 0x61, + 0x6b, + 0x45, + 0x75, + 0x64, + 0x70, + 0x42, + 0x31, + 0x68, + 0x39, + 0x6a, + 0x6d, + 0x39, + 0x4e, + 0x4a, + 0x35, + 0x61, + 0x63, + 0x52, + 0x37, + 0x2f, + 0x76, + 0x63, + 0x64, + 0x56, + 0x70, + 0x45, + 0x67, + 0x70, + 0x6e, + 0x32, + 0x6e, + 0x50, + 0x38, + 0x34, + 0x52, + 0x6e, + 0x2f, + 0x4e, + 0x36, + 0x72, + 0x65, + 0x75, + 0x77, + 0x52, + 0x56, + 0x61, + 0x38, + 0x2f, + 0x75, + 0x2b, + 0x59, + 0x65, + 0x63, + 0x43, + 0x67, + 0x59, + 0x45, + 0x41, + 0x32, + 0x41, + 0x70, + 0x36, + 0x0a, + 0x4f, + 0x36, + 0x78, + 0x4d, + 0x32, + 0x32, + 0x70, + 0x4e, + 0x44, + 0x64, + 0x33, + 0x70, + 0x35, + 0x79, + 0x50, + 0x65, + 0x38, + 0x36, + 0x38, + 0x66, + 0x79, + 0x66, + 0x35, + 0x56, + 0x35, + 0x2f, + 0x32, + 0x4b, + 0x6f, + 0x76, + 0x34, + 0x76, + 0x46, + 0x38, + 0x77, + 0x66, + 0x69, + 0x73, + 0x61, + 0x45, + 0x63, + 0x66, + 0x62, + 0x2f, + 0x57, + 0x62, + 0x4a, + 0x52, + 0x67, + 0x77, + 0x63, + 0x36, + 0x2f, + 0x6b, + 0x55, + 0x55, + 0x6b, + 0x31, + 0x6c, + 0x4a, + 0x6e, + 0x78, + 0x6e, + 0x47, + 0x0a, + 0x50, + 0x4b, + 0x39, + 0x38, + 0x48, + 0x6d, + 0x6a, + 0x69, + 0x64, + 0x35, + 0x59, + 0x33, + 0x39, + 0x64, + 0x7a, + 0x69, + 0x6c, + 0x58, + 0x67, + 0x74, + 0x43, + 0x36, + 0x36, + 0x58, + 0x56, + 0x41, + 0x75, + 0x30, + 0x4f, + 0x4a, + 0x30, + 0x34, + 0x6c, + 0x71, + 0x69, + 0x6c, + 0x73, + 0x4d, + 0x34, + 0x42, + 0x53, + 0x43, + 0x64, + 0x6b, + 0x6c, + 0x69, + 0x6d, + 0x70, + 0x68, + 0x75, + 0x46, + 0x73, + 0x7a, + 0x4a, + 0x33, + 0x50, + 0x4f, + 0x74, + 0x36, + 0x55, + 0x37, + 0x68, + 0x67, + 0x42, + 0x0a, + 0x31, + 0x71, + 0x73, + 0x4b, + 0x4d, + 0x4b, + 0x61, + 0x75, + 0x74, + 0x6a, + 0x33, + 0x2f, + 0x38, + 0x76, + 0x32, + 0x53, + 0x77, + 0x63, + 0x71, + 0x36, + 0x46, + 0x49, + 0x78, + 0x36, + 0x6c, + 0x36, + 0x46, + 0x6f, + 0x37, + 0x71, + 0x2b, + 0x73, + 0x70, + 0x74, + 0x56, + 0x72, + 0x57, + 0x65, + 0x63, + 0x43, + 0x67, + 0x59, + 0x42, + 0x2b, + 0x36, + 0x63, + 0x31, + 0x54, + 0x76, + 0x4a, + 0x43, + 0x6d, + 0x66, + 0x2f, + 0x4a, + 0x70, + 0x35, + 0x6c, + 0x6f, + 0x6d, + 0x77, + 0x57, + 0x71, + 0x63, + 0x0a, + 0x76, + 0x59, + 0x41, + 0x37, + 0x46, + 0x6c, + 0x32, + 0x42, + 0x70, + 0x31, + 0x31, + 0x4d, + 0x30, + 0x72, + 0x32, + 0x33, + 0x74, + 0x37, + 0x4f, + 0x47, + 0x69, + 0x61, + 0x78, + 0x48, + 0x6c, + 0x57, + 0x51, + 0x34, + 0x73, + 0x6c, + 0x71, + 0x55, + 0x56, + 0x4f, + 0x71, + 0x66, + 0x42, + 0x67, + 0x69, + 0x4f, + 0x4d, + 0x32, + 0x4f, + 0x4e, + 0x48, + 0x6c, + 0x35, + 0x74, + 0x48, + 0x59, + 0x4d, + 0x42, + 0x4f, + 0x4b, + 0x65, + 0x7a, + 0x35, + 0x33, + 0x67, + 0x6c, + 0x31, + 0x67, + 0x6d, + 0x6b, + 0x0a, + 0x52, + 0x4c, + 0x53, + 0x6d, + 0x2f, + 0x47, + 0x6b, + 0x49, + 0x2b, + 0x48, + 0x4d, + 0x7a, + 0x62, + 0x33, + 0x74, + 0x31, + 0x31, + 0x4d, + 0x33, + 0x4b, + 0x6e, + 0x71, + 0x52, + 0x53, + 0x44, + 0x64, + 0x35, + 0x6e, + 0x56, + 0x6b, + 0x41, + 0x41, + 0x50, + 0x37, + 0x4b, + 0x50, + 0x32, + 0x30, + 0x41, + 0x4d, + 0x6a, + 0x68, + 0x56, + 0x72, + 0x44, + 0x75, + 0x76, + 0x7a, + 0x75, + 0x42, + 0x56, + 0x77, + 0x53, + 0x6c, + 0x31, + 0x6a, + 0x6c, + 0x31, + 0x53, + 0x6e, + 0x45, + 0x61, + 0x79, + 0x76, + 0x0a, + 0x6b, + 0x38, + 0x5a, + 0x30, + 0x38, + 0x73, + 0x37, + 0x37, + 0x35, + 0x67, + 0x66, + 0x66, + 0x75, + 0x48, + 0x4b, + 0x58, + 0x6e, + 0x36, + 0x38, + 0x41, + 0x6a, + 0x51, + 0x4b, + 0x42, + 0x67, + 0x46, + 0x58, + 0x73, + 0x4c, + 0x4e, + 0x73, + 0x6f, + 0x31, + 0x74, + 0x52, + 0x35, + 0x50, + 0x62, + 0x59, + 0x6a, + 0x4b, + 0x56, + 0x44, + 0x39, + 0x69, + 0x6b, + 0x47, + 0x65, + 0x78, + 0x2b, + 0x54, + 0x64, + 0x57, + 0x36, + 0x74, + 0x4e, + 0x77, + 0x6d, + 0x4b, + 0x36, + 0x39, + 0x31, + 0x33, + 0x65, + 0x0a, + 0x6d, + 0x44, + 0x6a, + 0x6f, + 0x5a, + 0x57, + 0x71, + 0x79, + 0x45, + 0x72, + 0x4c, + 0x49, + 0x34, + 0x66, + 0x52, + 0x62, + 0x33, + 0x74, + 0x47, + 0x63, + 0x42, + 0x65, + 0x66, + 0x6f, + 0x6e, + 0x67, + 0x68, + 0x43, + 0x42, + 0x76, + 0x37, + 0x42, + 0x79, + 0x48, + 0x71, + 0x4c, + 0x75, + 0x6d, + 0x35, + 0x58, + 0x64, + 0x32, + 0x41, + 0x34, + 0x66, + 0x6f, + 0x46, + 0x31, + 0x37, + 0x32, + 0x78, + 0x79, + 0x2f, + 0x73, + 0x71, + 0x31, + 0x63, + 0x50, + 0x4d, + 0x48, + 0x54, + 0x4b, + 0x64, + 0x57, + 0x0a, + 0x34, + 0x62, + 0x63, + 0x6b, + 0x35, + 0x34, + 0x75, + 0x62, + 0x35, + 0x7a, + 0x78, + 0x31, + 0x79, + 0x32, + 0x2f, + 0x53, + 0x6e, + 0x69, + 0x50, + 0x76, + 0x4b, + 0x36, + 0x6b, + 0x39, + 0x4e, + 0x7a, + 0x78, + 0x4f, + 0x6e, + 0x67, + 0x53, + 0x54, + 0x75, + 0x42, + 0x54, + 0x4c, + 0x5a, + 0x6a, + 0x63, + 0x6a, + 0x42, + 0x33, + 0x58, + 0x4c, + 0x39, + 0x68, + 0x39, + 0x50, + 0x45, + 0x70, + 0x7a, + 0x7a, + 0x6c, + 0x68, + 0x70, + 0x53, + 0x58, + 0x74, + 0x70, + 0x33, + 0x55, + 0x68, + 0x4a, + 0x30, + 0x0a, + 0x56, + 0x53, + 0x4c, + 0x48, + 0x41, + 0x6f, + 0x47, + 0x41, + 0x44, + 0x4e, + 0x79, + 0x6b, + 0x52, + 0x50, + 0x32, + 0x55, + 0x38, + 0x34, + 0x78, + 0x70, + 0x50, + 0x6d, + 0x36, + 0x77, + 0x31, + 0x69, + 0x36, + 0x72, + 0x47, + 0x48, + 0x71, + 0x50, + 0x38, + 0x67, + 0x65, + 0x2b, + 0x4f, + 0x6f, + 0x6f, + 0x77, + 0x69, + 0x47, + 0x47, + 0x4b, + 0x48, + 0x54, + 0x45, + 0x48, + 0x72, + 0x56, + 0x39, + 0x53, + 0x79, + 0x66, + 0x6c, + 0x2f, + 0x63, + 0x42, + 0x39, + 0x55, + 0x4d, + 0x65, + 0x79, + 0x43, + 0x0a, + 0x45, + 0x49, + 0x68, + 0x46, + 0x37, + 0x4e, + 0x71, + 0x6b, + 0x35, + 0x76, + 0x2b, + 0x4d, + 0x68, + 0x2b, + 0x50, + 0x31, + 0x52, + 0x49, + 0x52, + 0x71, + 0x35, + 0x39, + 0x66, + 0x4d, + 0x41, + 0x34, + 0x72, + 0x4e, + 0x7a, + 0x6d, + 0x61, + 0x78, + 0x65, + 0x6b, + 0x35, + 0x72, + 0x4b, + 0x36, + 0x45, + 0x50, + 0x67, + 0x31, + 0x62, + 0x2f, + 0x67, + 0x78, + 0x7a, + 0x45, + 0x73, + 0x4c, + 0x56, + 0x45, + 0x45, + 0x42, + 0x58, + 0x49, + 0x51, + 0x35, + 0x57, + 0x57, + 0x6e, + 0x58, + 0x6a, + 0x52, + 0x0a, + 0x75, + 0x2b, + 0x5a, + 0x56, + 0x49, + 0x37, + 0x33, + 0x68, + 0x62, + 0x36, + 0x79, + 0x56, + 0x56, + 0x70, + 0x75, + 0x6f, + 0x4e, + 0x68, + 0x52, + 0x43, + 0x67, + 0x59, + 0x67, + 0x45, + 0x70, + 0x48, + 0x6b, + 0x6e, + 0x47, + 0x43, + 0x45, + 0x76, + 0x62, + 0x55, + 0x52, + 0x4b, + 0x4f, + 0x68, + 0x79, + 0x44, + 0x4d, + 0x4e, + 0x69, + 0x6c, + 0x49, + 0x6a, + 0x31, + 0x6d, + 0x71, + 0x44, + 0x4d, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char ca_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x44, + 0x48, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x51, + 0x43, + 0x43, + 0x51, + 0x44, + 0x43, + 0x44, + 0x35, + 0x33, + 0x59, + 0x5a, + 0x4a, + 0x4a, + 0x37, + 0x6c, + 0x6a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x6b, + 0x71, + 0x68, + 0x6b, + 0x69, + 0x47, + 0x39, + 0x77, + 0x30, + 0x42, + 0x41, + 0x51, + 0x73, + 0x46, + 0x41, + 0x44, + 0x42, + 0x50, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x63, + 0x54, + 0x43, + 0x56, + 0x68, + 0x70, + 0x59, + 0x57, + 0x39, + 0x69, + 0x61, + 0x58, + 0x52, + 0x68, + 0x62, + 0x6a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x0a, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x41, + 0x67, + 0x46, + 0x77, + 0x30, + 0x79, + 0x4d, + 0x6a, + 0x41, + 0x33, + 0x4d, + 0x44, + 0x59, + 0x78, + 0x4d, + 0x54, + 0x49, + 0x30, + 0x4d, + 0x6a, + 0x42, + 0x61, + 0x47, + 0x41, + 0x38, + 0x79, + 0x4d, + 0x44, + 0x55, + 0x77, + 0x4d, + 0x44, + 0x63, + 0x78, + 0x4f, + 0x54, + 0x45, + 0x78, + 0x0a, + 0x4d, + 0x6a, + 0x51, + 0x79, + 0x4d, + 0x46, + 0x6f, + 0x77, + 0x54, + 0x7a, + 0x45, + 0x62, + 0x4d, + 0x42, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x68, + 0x4d, + 0x53, + 0x62, + 0x47, + 0x6c, + 0x69, + 0x64, + 0x32, + 0x56, + 0x69, + 0x63, + 0x32, + 0x39, + 0x6a, + 0x61, + 0x32, + 0x56, + 0x30, + 0x63, + 0x79, + 0x31, + 0x30, + 0x5a, + 0x58, + 0x4e, + 0x30, + 0x4d, + 0x52, + 0x49, + 0x77, + 0x45, + 0x41, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x48, + 0x45, + 0x77, + 0x6c, + 0x59, + 0x0a, + 0x61, + 0x57, + 0x46, + 0x76, + 0x59, + 0x6d, + 0x6c, + 0x30, + 0x59, + 0x57, + 0x34, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x67, + 0x54, + 0x42, + 0x6c, + 0x52, + 0x68, + 0x61, + 0x58, + 0x42, + 0x6c, + 0x61, + 0x54, + 0x45, + 0x4c, + 0x4d, + 0x41, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x68, + 0x4d, + 0x43, + 0x56, + 0x46, + 0x63, + 0x77, + 0x67, + 0x67, + 0x45, + 0x69, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x0a, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x41, + 0x51, + 0x55, + 0x41, + 0x41, + 0x34, + 0x49, + 0x42, + 0x44, + 0x77, + 0x41, + 0x77, + 0x67, + 0x67, + 0x45, + 0x4b, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x51, + 0x44, + 0x56, + 0x2f, + 0x4f, + 0x7a, + 0x35, + 0x56, + 0x73, + 0x58, + 0x33, + 0x56, + 0x34, + 0x52, + 0x38, + 0x76, + 0x61, + 0x41, + 0x4e, + 0x79, + 0x50, + 0x49, + 0x4d, + 0x61, + 0x46, + 0x73, + 0x79, + 0x67, + 0x4d, + 0x65, + 0x57, + 0x70, + 0x4b, + 0x61, + 0x6e, + 0x0a, + 0x48, + 0x4d, + 0x54, + 0x34, + 0x6b, + 0x66, + 0x69, + 0x67, + 0x55, + 0x59, + 0x55, + 0x48, + 0x55, + 0x59, + 0x65, + 0x4c, + 0x6b, + 0x62, + 0x50, + 0x65, + 0x77, + 0x5a, + 0x41, + 0x79, + 0x63, + 0x43, + 0x66, + 0x59, + 0x73, + 0x43, + 0x64, + 0x42, + 0x4a, + 0x6d, + 0x73, + 0x58, + 0x43, + 0x58, + 0x43, + 0x73, + 0x66, + 0x69, + 0x45, + 0x4c, + 0x70, + 0x75, + 0x59, + 0x6e, + 0x65, + 0x70, + 0x52, + 0x65, + 0x74, + 0x72, + 0x5a, + 0x32, + 0x39, + 0x77, + 0x54, + 0x6a, + 0x2f, + 0x62, + 0x2b, + 0x6d, + 0x0a, + 0x32, + 0x54, + 0x6a, + 0x55, + 0x62, + 0x32, + 0x47, + 0x34, + 0x51, + 0x36, + 0x37, + 0x43, + 0x4b, + 0x53, + 0x48, + 0x4e, + 0x6c, + 0x68, + 0x36, + 0x43, + 0x51, + 0x71, + 0x46, + 0x76, + 0x45, + 0x57, + 0x6e, + 0x42, + 0x58, + 0x2b, + 0x38, + 0x66, + 0x59, + 0x65, + 0x68, + 0x51, + 0x54, + 0x33, + 0x51, + 0x77, + 0x4a, + 0x58, + 0x4e, + 0x39, + 0x68, + 0x56, + 0x42, + 0x75, + 0x50, + 0x78, + 0x44, + 0x2b, + 0x78, + 0x41, + 0x2b, + 0x62, + 0x49, + 0x36, + 0x5a, + 0x32, + 0x38, + 0x57, + 0x30, + 0x6b, + 0x0a, + 0x41, + 0x30, + 0x5a, + 0x4c, + 0x39, + 0x46, + 0x66, + 0x57, + 0x43, + 0x54, + 0x36, + 0x66, + 0x4f, + 0x44, + 0x78, + 0x4d, + 0x37, + 0x65, + 0x67, + 0x6b, + 0x53, + 0x53, + 0x6d, + 0x4b, + 0x65, + 0x44, + 0x67, + 0x54, + 0x55, + 0x73, + 0x6e, + 0x35, + 0x77, + 0x64, + 0x38, + 0x52, + 0x38, + 0x6b, + 0x51, + 0x68, + 0x6b, + 0x34, + 0x79, + 0x49, + 0x32, + 0x4b, + 0x50, + 0x55, + 0x79, + 0x73, + 0x2b, + 0x6e, + 0x59, + 0x78, + 0x7a, + 0x6f, + 0x77, + 0x6a, + 0x4e, + 0x4f, + 0x69, + 0x38, + 0x75, + 0x68, + 0x0a, + 0x44, + 0x58, + 0x7a, + 0x72, + 0x2b, + 0x46, + 0x78, + 0x67, + 0x2f, + 0x70, + 0x73, + 0x36, + 0x38, + 0x56, + 0x69, + 0x5a, + 0x6a, + 0x47, + 0x46, + 0x67, + 0x58, + 0x53, + 0x46, + 0x48, + 0x77, + 0x35, + 0x5a, + 0x49, + 0x76, + 0x52, + 0x51, + 0x54, + 0x47, + 0x49, + 0x32, + 0x68, + 0x30, + 0x54, + 0x39, + 0x71, + 0x6d, + 0x48, + 0x50, + 0x30, + 0x42, + 0x56, + 0x46, + 0x79, + 0x62, + 0x4a, + 0x75, + 0x39, + 0x73, + 0x36, + 0x58, + 0x44, + 0x59, + 0x75, + 0x6f, + 0x4d, + 0x38, + 0x45, + 0x4a, + 0x73, + 0x0a, + 0x57, + 0x53, + 0x73, + 0x2b, + 0x34, + 0x76, + 0x4f, + 0x61, + 0x63, + 0x61, + 0x54, + 0x45, + 0x56, + 0x59, + 0x41, + 0x51, + 0x46, + 0x35, + 0x48, + 0x78, + 0x41, + 0x4c, + 0x66, + 0x43, + 0x74, + 0x61, + 0x4f, + 0x65, + 0x50, + 0x6a, + 0x53, + 0x70, + 0x2b, + 0x66, + 0x66, + 0x45, + 0x2b, + 0x55, + 0x30, + 0x43, + 0x78, + 0x59, + 0x46, + 0x33, + 0x4e, + 0x48, + 0x39, + 0x33, + 0x59, + 0x4b, + 0x5a, + 0x78, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x0a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x49, + 0x6d, + 0x4a, + 0x76, + 0x71, + 0x6e, + 0x70, + 0x50, + 0x64, + 0x53, + 0x75, + 0x46, + 0x4f, + 0x5a, + 0x54, + 0x36, + 0x76, + 0x74, + 0x48, + 0x31, + 0x70, + 0x4a, + 0x43, + 0x45, + 0x76, + 0x4a, + 0x39, + 0x62, + 0x53, + 0x78, + 0x31, + 0x43, + 0x41, + 0x76, + 0x36, + 0x46, + 0x34, + 0x46, + 0x44, + 0x6f, + 0x77, + 0x4b, + 0x77, + 0x0a, + 0x43, + 0x71, + 0x4b, + 0x53, + 0x6c, + 0x59, + 0x45, + 0x6a, + 0x72, + 0x49, + 0x4c, + 0x64, + 0x6c, + 0x42, + 0x30, + 0x39, + 0x32, + 0x31, + 0x4f, + 0x54, + 0x30, + 0x76, + 0x61, + 0x68, + 0x33, + 0x6c, + 0x55, + 0x76, + 0x2f, + 0x6b, + 0x47, + 0x4e, + 0x4c, + 0x76, + 0x58, + 0x55, + 0x71, + 0x54, + 0x69, + 0x42, + 0x61, + 0x6b, + 0x77, + 0x66, + 0x52, + 0x47, + 0x30, + 0x39, + 0x61, + 0x49, + 0x45, + 0x6e, + 0x53, + 0x68, + 0x6d, + 0x79, + 0x6f, + 0x30, + 0x68, + 0x63, + 0x65, + 0x4f, + 0x68, + 0x33, + 0x0a, + 0x4f, + 0x31, + 0x4b, + 0x4b, + 0x59, + 0x76, + 0x4a, + 0x32, + 0x6a, + 0x4a, + 0x47, + 0x6b, + 0x36, + 0x50, + 0x6c, + 0x52, + 0x78, + 0x65, + 0x53, + 0x67, + 0x37, + 0x64, + 0x35, + 0x4d, + 0x69, + 0x37, + 0x58, + 0x67, + 0x6e, + 0x41, + 0x64, + 0x65, + 0x61, + 0x78, + 0x77, + 0x68, + 0x75, + 0x76, + 0x5a, + 0x5a, + 0x6d, + 0x61, + 0x49, + 0x7a, + 0x62, + 0x68, + 0x41, + 0x57, + 0x50, + 0x38, + 0x71, + 0x67, + 0x49, + 0x30, + 0x36, + 0x50, + 0x32, + 0x52, + 0x42, + 0x53, + 0x35, + 0x42, + 0x4a, + 0x76, + 0x0a, + 0x72, + 0x44, + 0x44, + 0x33, + 0x44, + 0x68, + 0x77, + 0x38, + 0x4e, + 0x38, + 0x47, + 0x77, + 0x42, + 0x44, + 0x31, + 0x52, + 0x59, + 0x32, + 0x79, + 0x4b, + 0x72, + 0x79, + 0x46, + 0x51, + 0x2b, + 0x34, + 0x55, + 0x32, + 0x31, + 0x45, + 0x72, + 0x73, + 0x77, + 0x2f, + 0x33, + 0x38, + 0x63, + 0x59, + 0x38, + 0x55, + 0x41, + 0x46, + 0x54, + 0x6b, + 0x67, + 0x33, + 0x72, + 0x57, + 0x72, + 0x34, + 0x44, + 0x57, + 0x78, + 0x36, + 0x74, + 0x6e, + 0x49, + 0x66, + 0x64, + 0x72, + 0x4e, + 0x31, + 0x49, + 0x49, + 0x0a, + 0x70, + 0x52, + 0x71, + 0x53, + 0x78, + 0x48, + 0x51, + 0x57, + 0x34, + 0x6b, + 0x5a, + 0x61, + 0x67, + 0x31, + 0x75, + 0x5a, + 0x64, + 0x46, + 0x63, + 0x53, + 0x69, + 0x61, + 0x59, + 0x54, + 0x2b, + 0x65, + 0x62, + 0x4c, + 0x57, + 0x56, + 0x58, + 0x41, + 0x7a, + 0x62, + 0x66, + 0x4a, + 0x4c, + 0x4f, + 0x56, + 0x38, + 0x45, + 0x46, + 0x46, + 0x33, + 0x52, + 0x44, + 0x43, + 0x2f, + 0x41, + 0x33, + 0x55, + 0x33, + 0x56, + 0x63, + 0x4f, + 0x52, + 0x44, + 0x42, + 0x44, + 0x57, + 0x44, + 0x68, + 0x6d, + 0x68, + 0x0a, + 0x38, + 0x72, + 0x34, + 0x54, + 0x2b, + 0x36, + 0x46, + 0x67, + 0x67, + 0x59, + 0x4c, + 0x32, + 0x65, + 0x6a, + 0x4d, + 0x6d, + 0x66, + 0x77, + 0x62, + 0x65, + 0x77, + 0x7a, + 0x49, + 0x2b, + 0x48, + 0x6d, + 0x34, + 0x53, + 0x69, + 0x64, + 0x67, + 0x4f, + 0x78, + 0x4c, + 0x66, + 0x31, + 0x46, + 0x78, + 0x31, + 0x64, + 0x32, + 0x76, + 0x34, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char client_key[] = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x52, + 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, + 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x4a, + 0x4b, 0x51, 0x49, 0x42, 0x41, 0x41, 0x4b, 0x43, 0x41, 0x67, 0x45, 0x41, + 0x72, 0x77, 0x53, 0x79, 0x4e, 0x6e, 0x68, 0x66, 0x76, 0x44, 0x69, 0x63, + 0x47, 0x4a, 0x4f, 0x42, 0x66, 0x55, 0x51, 0x4c, 0x47, 0x45, 0x70, 0x4b, + 0x43, 0x32, 0x2f, 0x4e, 0x32, 0x35, 0x54, 0x6f, 0x32, 0x4b, 0x66, 0x36, + 0x36, 0x7a, 0x6b, 0x44, 0x62, 0x33, 0x6d, 0x34, 0x49, 0x50, 0x77, 0x4c, + 0x0a, 0x63, 0x63, 0x78, 0x47, 0x57, 0x48, 0x57, 0x75, 0x65, 0x78, 0x69, + 0x70, 0x6d, 0x76, 0x41, 0x77, 0x46, 0x2b, 0x58, 0x32, 0x36, 0x46, 0x47, + 0x7a, 0x35, 0x66, 0x4a, 0x64, 0x71, 0x38, 0x46, 0x68, 0x77, 0x6c, 0x71, + 0x2b, 0x6e, 0x71, 0x6a, 0x34, 0x50, 0x68, 0x6c, 0x2f, 0x51, 0x2b, 0x77, + 0x55, 0x42, 0x6c, 0x51, 0x2b, 0x58, 0x4d, 0x6a, 0x71, 0x49, 0x70, 0x77, + 0x79, 0x7a, 0x70, 0x5a, 0x6d, 0x0a, 0x34, 0x48, 0x6d, 0x2b, 0x4f, 0x4f, + 0x43, 0x4b, 0x66, 0x73, 0x79, 0x58, 0x64, 0x6d, 0x4c, 0x4c, 0x70, 0x46, + 0x79, 0x56, 0x6b, 0x34, 0x72, 0x72, 0x32, 0x47, 0x51, 0x34, 0x4b, 0x46, + 0x53, 0x51, 0x58, 0x51, 0x58, 0x79, 0x5a, 0x78, 0x78, 0x62, 0x4e, 0x46, + 0x64, 0x67, 0x6e, 0x43, 0x72, 0x76, 0x6d, 0x74, 0x6b, 0x49, 0x42, 0x51, + 0x62, 0x76, 0x6d, 0x6e, 0x42, 0x79, 0x50, 0x2b, 0x77, 0x6e, 0x0a, 0x65, + 0x7a, 0x68, 0x55, 0x51, 0x4b, 0x4c, 0x6b, 0x6d, 0x51, 0x50, 0x32, 0x53, + 0x6a, 0x37, 0x65, 0x52, 0x7a, 0x4c, 0x6f, 0x79, 0x58, 0x32, 0x71, 0x41, + 0x49, 0x75, 0x43, 0x46, 0x59, 0x62, 0x57, 0x41, 0x36, 0x44, 0x57, 0x53, + 0x39, 0x50, 0x4c, 0x56, 0x75, 0x67, 0x71, 0x53, 0x76, 0x4c, 0x65, 0x51, + 0x71, 0x7a, 0x31, 0x74, 0x70, 0x32, 0x66, 0x5a, 0x70, 0x68, 0x42, 0x64, + 0x6a, 0x66, 0x49, 0x0a, 0x6c, 0x65, 0x44, 0x42, 0x75, 0x7a, 0x59, 0x67, + 0x42, 0x30, 0x61, 0x63, 0x39, 0x79, 0x44, 0x48, 0x31, 0x6d, 0x65, 0x65, + 0x51, 0x4f, 0x43, 0x4e, 0x50, 0x61, 0x7a, 0x52, 0x41, 0x63, 0x57, 0x57, + 0x77, 0x75, 0x2b, 0x6f, 0x56, 0x57, 0x6d, 0x45, 0x42, 0x45, 0x6b, 0x50, + 0x44, 0x49, 0x31, 0x70, 0x68, 0x75, 0x6e, 0x44, 0x39, 0x33, 0x33, 0x47, + 0x31, 0x52, 0x63, 0x57, 0x64, 0x6d, 0x51, 0x55, 0x0a, 0x72, 0x37, 0x6b, + 0x48, 0x65, 0x50, 0x4d, 0x50, 0x77, 0x35, 0x70, 0x33, 0x42, 0x7a, 0x2b, + 0x71, 0x6a, 0x71, 0x61, 0x48, 0x2b, 0x36, 0x6f, 0x43, 0x54, 0x68, 0x5a, + 0x43, 0x6f, 0x2b, 0x59, 0x55, 0x39, 0x68, 0x51, 0x2f, 0x34, 0x2b, 0x48, + 0x77, 0x4d, 0x4e, 0x79, 0x38, 0x51, 0x73, 0x79, 0x6a, 0x61, 0x6d, 0x66, + 0x37, 0x31, 0x7a, 0x35, 0x67, 0x34, 0x43, 0x68, 0x65, 0x32, 0x34, 0x51, + 0x50, 0x0a, 0x66, 0x63, 0x6e, 0x30, 0x33, 0x57, 0x79, 0x2f, 0x5a, 0x52, + 0x61, 0x75, 0x53, 0x67, 0x73, 0x51, 0x6e, 0x79, 0x7a, 0x68, 0x72, 0x5a, + 0x7a, 0x39, 0x57, 0x4a, 0x35, 0x2b, 0x38, 0x77, 0x55, 0x71, 0x6a, 0x34, + 0x34, 0x51, 0x38, 0x45, 0x36, 0x70, 0x59, 0x52, 0x4c, 0x30, 0x68, 0x34, + 0x78, 0x65, 0x49, 0x73, 0x45, 0x4f, 0x76, 0x2b, 0x49, 0x77, 0x4e, 0x62, + 0x56, 0x59, 0x6d, 0x38, 0x57, 0x31, 0x0a, 0x62, 0x48, 0x5a, 0x62, 0x64, + 0x57, 0x31, 0x53, 0x65, 0x4d, 0x77, 0x70, 0x74, 0x45, 0x62, 0x37, 0x47, + 0x50, 0x67, 0x55, 0x61, 0x4f, 0x2f, 0x79, 0x4a, 0x7a, 0x2f, 0x68, 0x4b, + 0x71, 0x63, 0x72, 0x70, 0x49, 0x4a, 0x37, 0x2f, 0x49, 0x6f, 0x61, 0x41, + 0x54, 0x4b, 0x2b, 0x6a, 0x39, 0x70, 0x38, 0x32, 0x62, 0x33, 0x51, 0x69, + 0x32, 0x2f, 0x55, 0x54, 0x70, 0x71, 0x49, 0x44, 0x49, 0x67, 0x79, 0x0a, + 0x79, 0x5a, 0x41, 0x4c, 0x55, 0x6b, 0x36, 0x68, 0x57, 0x48, 0x39, 0x37, + 0x53, 0x6f, 0x4d, 0x61, 0x71, 0x6b, 0x68, 0x55, 0x6a, 0x73, 0x44, 0x38, + 0x36, 0x7a, 0x50, 0x32, 0x31, 0x4b, 0x48, 0x45, 0x4e, 0x35, 0x47, 0x35, + 0x6b, 0x58, 0x33, 0x36, 0x41, 0x77, 0x79, 0x66, 0x53, 0x45, 0x41, 0x6d, + 0x52, 0x76, 0x6c, 0x48, 0x63, 0x7a, 0x47, 0x6a, 0x78, 0x66, 0x71, 0x51, + 0x50, 0x42, 0x71, 0x7a, 0x0a, 0x4d, 0x63, 0x65, 0x39, 0x46, 0x53, 0x78, + 0x39, 0x4e, 0x2b, 0x52, 0x39, 0x62, 0x77, 0x2f, 0x56, 0x42, 0x73, 0x66, + 0x43, 0x63, 0x7a, 0x46, 0x35, 0x4c, 0x4b, 0x6d, 0x37, 0x52, 0x6f, 0x6a, + 0x48, 0x44, 0x35, 0x7a, 0x77, 0x4f, 0x62, 0x4f, 0x33, 0x36, 0x57, 0x7a, + 0x74, 0x75, 0x50, 0x61, 0x62, 0x62, 0x6f, 0x66, 0x7a, 0x30, 0x6d, 0x46, + 0x69, 0x56, 0x79, 0x75, 0x46, 0x33, 0x63, 0x4f, 0x62, 0x0a, 0x2f, 0x74, + 0x71, 0x47, 0x56, 0x56, 0x39, 0x33, 0x6e, 0x49, 0x72, 0x41, 0x42, 0x62, + 0x79, 0x2f, 0x44, 0x62, 0x42, 0x62, 0x49, 0x51, 0x4a, 0x58, 0x4c, 0x73, + 0x73, 0x74, 0x6e, 0x70, 0x77, 0x70, 0x6c, 0x5a, 0x62, 0x61, 0x5a, 0x6b, + 0x59, 0x71, 0x50, 0x76, 0x4f, 0x37, 0x47, 0x52, 0x56, 0x37, 0x30, 0x48, + 0x38, 0x77, 0x4e, 0x4f, 0x35, 0x4f, 0x66, 0x33, 0x4d, 0x43, 0x41, 0x77, + 0x45, 0x41, 0x0a, 0x41, 0x51, 0x4b, 0x43, 0x41, 0x67, 0x42, 0x6c, 0x78, + 0x33, 0x56, 0x62, 0x39, 0x2b, 0x53, 0x30, 0x73, 0x4c, 0x63, 0x57, 0x45, + 0x37, 0x48, 0x61, 0x42, 0x78, 0x66, 0x73, 0x71, 0x45, 0x63, 0x6e, 0x48, + 0x33, 0x32, 0x33, 0x6c, 0x49, 0x46, 0x55, 0x66, 0x56, 0x75, 0x4f, 0x4c, + 0x7a, 0x6d, 0x77, 0x4f, 0x6a, 0x69, 0x35, 0x39, 0x64, 0x6b, 0x78, 0x39, + 0x48, 0x6c, 0x30, 0x4e, 0x2f, 0x75, 0x66, 0x0a, 0x32, 0x6c, 0x66, 0x48, + 0x6f, 0x71, 0x5a, 0x56, 0x50, 0x34, 0x61, 0x32, 0x30, 0x38, 0x79, 0x71, + 0x6a, 0x4b, 0x65, 0x73, 0x6d, 0x6d, 0x6b, 0x66, 0x66, 0x57, 0x59, 0x64, + 0x48, 0x6d, 0x59, 0x2b, 0x74, 0x74, 0x55, 0x72, 0x79, 0x72, 0x35, 0x61, + 0x62, 0x2b, 0x4e, 0x55, 0x55, 0x67, 0x4c, 0x57, 0x33, 0x62, 0x38, 0x75, + 0x4a, 0x49, 0x6a, 0x76, 0x51, 0x64, 0x30, 0x39, 0x64, 0x6c, 0x63, 0x55, + 0x0a, 0x4a, 0x69, 0x5a, 0x75, 0x30, 0x6b, 0x56, 0x61, 0x37, 0x2f, 0x79, + 0x4d, 0x4d, 0x4e, 0x32, 0x32, 0x6d, 0x5a, 0x47, 0x4f, 0x34, 0x70, 0x36, + 0x52, 0x65, 0x6b, 0x50, 0x64, 0x63, 0x73, 0x41, 0x58, 0x55, 0x44, 0x6a, + 0x2b, 0x6d, 0x48, 0x6c, 0x2b, 0x73, 0x33, 0x66, 0x57, 0x64, 0x4a, 0x49, + 0x69, 0x58, 0x67, 0x49, 0x53, 0x36, 0x6d, 0x4b, 0x4c, 0x5a, 0x64, 0x61, + 0x5a, 0x51, 0x43, 0x46, 0x77, 0x0a, 0x57, 0x72, 0x31, 0x2f, 0x72, 0x38, + 0x2f, 0x54, 0x31, 0x2b, 0x64, 0x49, 0x52, 0x61, 0x76, 0x33, 0x5a, 0x53, + 0x6e, 0x68, 0x47, 0x75, 0x69, 0x61, 0x63, 0x34, 0x34, 0x72, 0x79, 0x70, + 0x38, 0x56, 0x69, 0x79, 0x34, 0x4e, 0x4a, 0x2b, 0x2f, 0x5a, 0x46, 0x6e, + 0x78, 0x4f, 0x46, 0x70, 0x76, 0x38, 0x4c, 0x63, 0x37, 0x6a, 0x30, 0x4d, + 0x7a, 0x31, 0x58, 0x42, 0x39, 0x4e, 0x6e, 0x38, 0x78, 0x41, 0x0a, 0x62, + 0x76, 0x41, 0x5a, 0x52, 0x78, 0x62, 0x76, 0x75, 0x4a, 0x4a, 0x76, 0x61, + 0x43, 0x61, 0x37, 0x46, 0x79, 0x54, 0x30, 0x77, 0x74, 0x4e, 0x4a, 0x79, + 0x64, 0x55, 0x36, 0x31, 0x6f, 0x48, 0x50, 0x66, 0x43, 0x30, 0x6b, 0x50, + 0x35, 0x68, 0x2b, 0x76, 0x4c, 0x4d, 0x5a, 0x32, 0x69, 0x73, 0x6e, 0x41, + 0x4b, 0x59, 0x76, 0x63, 0x30, 0x51, 0x55, 0x62, 0x4b, 0x58, 0x4c, 0x30, + 0x49, 0x33, 0x36, 0x0a, 0x55, 0x6e, 0x6d, 0x6c, 0x33, 0x59, 0x42, 0x79, + 0x4e, 0x4b, 0x59, 0x66, 0x31, 0x46, 0x35, 0x43, 0x79, 0x75, 0x38, 0x32, + 0x49, 0x54, 0x64, 0x50, 0x59, 0x37, 0x64, 0x43, 0x39, 0x39, 0x55, 0x6f, + 0x6e, 0x2f, 0x47, 0x35, 0x66, 0x52, 0x55, 0x56, 0x6e, 0x61, 0x6d, 0x58, + 0x34, 0x41, 0x2b, 0x4e, 0x62, 0x66, 0x47, 0x45, 0x68, 0x6c, 0x4a, 0x32, + 0x58, 0x58, 0x55, 0x51, 0x58, 0x54, 0x69, 0x68, 0x0a, 0x41, 0x77, 0x46, + 0x75, 0x5a, 0x48, 0x72, 0x54, 0x48, 0x41, 0x4a, 0x58, 0x39, 0x64, 0x6f, + 0x77, 0x34, 0x4c, 0x51, 0x51, 0x75, 0x49, 0x68, 0x44, 0x50, 0x4f, 0x59, + 0x55, 0x4e, 0x7a, 0x5a, 0x6f, 0x34, 0x57, 0x30, 0x34, 0x44, 0x44, 0x4a, + 0x2b, 0x62, 0x75, 0x66, 0x34, 0x67, 0x7a, 0x70, 0x4e, 0x68, 0x31, 0x39, + 0x59, 0x52, 0x65, 0x42, 0x44, 0x75, 0x7a, 0x67, 0x33, 0x43, 0x54, 0x62, + 0x59, 0x0a, 0x4b, 0x38, 0x52, 0x45, 0x6e, 0x58, 0x76, 0x4d, 0x59, 0x34, + 0x4b, 0x44, 0x36, 0x63, 0x69, 0x4d, 0x4f, 0x72, 0x61, 0x78, 0x4a, 0x53, + 0x31, 0x43, 0x49, 0x6d, 0x67, 0x53, 0x70, 0x68, 0x56, 0x70, 0x50, 0x37, + 0x54, 0x77, 0x4f, 0x4f, 0x6c, 0x62, 0x75, 0x78, 0x6a, 0x39, 0x76, 0x31, + 0x45, 0x63, 0x54, 0x41, 0x78, 0x34, 0x49, 0x68, 0x31, 0x38, 0x71, 0x72, + 0x71, 0x6c, 0x36, 0x32, 0x32, 0x56, 0x0a, 0x34, 0x45, 0x70, 0x34, 0x6c, + 0x62, 0x2f, 0x4a, 0x78, 0x57, 0x6e, 0x58, 0x4e, 0x48, 0x77, 0x2f, 0x7a, + 0x32, 0x64, 0x70, 0x64, 0x4d, 0x55, 0x6b, 0x4a, 0x4a, 0x66, 0x33, 0x6c, + 0x6d, 0x6e, 0x2b, 0x2f, 0x34, 0x5a, 0x56, 0x2b, 0x67, 0x4e, 0x42, 0x74, + 0x42, 0x79, 0x71, 0x4c, 0x52, 0x49, 0x50, 0x54, 0x47, 0x32, 0x6c, 0x37, + 0x4f, 0x34, 0x55, 0x6e, 0x52, 0x2f, 0x32, 0x69, 0x75, 0x71, 0x78, 0x0a, + 0x30, 0x77, 0x4b, 0x70, 0x59, 0x37, 0x54, 0x68, 0x34, 0x49, 0x66, 0x58, + 0x6a, 0x53, 0x56, 0x57, 0x36, 0x73, 0x4f, 0x63, 0x2b, 0x4a, 0x77, 0x37, + 0x64, 0x35, 0x68, 0x73, 0x56, 0x2f, 0x7a, 0x37, 0x41, 0x55, 0x52, 0x45, + 0x45, 0x4a, 0x34, 0x57, 0x58, 0x36, 0x4e, 0x4e, 0x70, 0x42, 0x77, 0x51, + 0x5a, 0x4e, 0x32, 0x56, 0x78, 0x48, 0x30, 0x37, 0x49, 0x78, 0x79, 0x76, + 0x6b, 0x33, 0x5a, 0x51, 0x0a, 0x58, 0x67, 0x56, 0x51, 0x58, 0x4e, 0x68, + 0x72, 0x79, 0x4e, 0x43, 0x49, 0x57, 0x70, 0x74, 0x2f, 0x66, 0x57, 0x69, + 0x76, 0x75, 0x50, 0x43, 0x7a, 0x77, 0x74, 0x32, 0x55, 0x74, 0x4f, 0x72, + 0x70, 0x2f, 0x35, 0x57, 0x43, 0x35, 0x37, 0x77, 0x31, 0x65, 0x65, 0x4d, + 0x73, 0x58, 0x63, 0x68, 0x72, 0x41, 0x51, 0x4b, 0x43, 0x41, 0x51, 0x45, + 0x41, 0x35, 0x4b, 0x48, 0x7a, 0x4c, 0x34, 0x76, 0x57, 0x0a, 0x42, 0x58, + 0x65, 0x66, 0x6f, 0x36, 0x2f, 0x4b, 0x68, 0x77, 0x49, 0x4a, 0x41, 0x37, + 0x79, 0x35, 0x54, 0x53, 0x67, 0x41, 0x62, 0x44, 0x68, 0x59, 0x70, 0x51, + 0x69, 0x34, 0x32, 0x4c, 0x6c, 0x74, 0x33, 0x37, 0x4e, 0x4d, 0x6e, 0x4a, + 0x63, 0x79, 0x4b, 0x54, 0x56, 0x6d, 0x79, 0x78, 0x70, 0x66, 0x59, 0x47, + 0x38, 0x65, 0x6e, 0x70, 0x31, 0x46, 0x71, 0x7a, 0x54, 0x56, 0x59, 0x74, + 0x63, 0x65, 0x0a, 0x5a, 0x49, 0x77, 0x33, 0x65, 0x39, 0x54, 0x35, 0x48, + 0x47, 0x4e, 0x71, 0x55, 0x6a, 0x70, 0x6f, 0x78, 0x43, 0x70, 0x35, 0x33, + 0x4b, 0x63, 0x44, 0x75, 0x38, 0x69, 0x47, 0x46, 0x36, 0x43, 0x51, 0x71, + 0x4a, 0x4d, 0x46, 0x72, 0x6b, 0x38, 0x47, 0x6b, 0x56, 0x51, 0x55, 0x38, + 0x31, 0x71, 0x58, 0x75, 0x57, 0x6f, 0x41, 0x50, 0x32, 0x79, 0x4a, 0x66, + 0x51, 0x53, 0x6d, 0x42, 0x75, 0x68, 0x67, 0x0a, 0x50, 0x64, 0x48, 0x4b, + 0x55, 0x4b, 0x31, 0x68, 0x47, 0x31, 0x6f, 0x36, 0x32, 0x44, 0x48, 0x32, + 0x67, 0x57, 0x33, 0x65, 0x6e, 0x37, 0x6a, 0x33, 0x70, 0x78, 0x43, 0x70, + 0x34, 0x4e, 0x68, 0x70, 0x39, 0x49, 0x70, 0x2f, 0x35, 0x39, 0x67, 0x30, + 0x4c, 0x73, 0x50, 0x44, 0x42, 0x67, 0x36, 0x6e, 0x59, 0x69, 0x38, 0x78, + 0x35, 0x61, 0x44, 0x4e, 0x6f, 0x59, 0x6d, 0x76, 0x45, 0x56, 0x45, 0x41, + 0x0a, 0x35, 0x46, 0x52, 0x7a, 0x37, 0x75, 0x2f, 0x77, 0x77, 0x67, 0x79, + 0x6d, 0x47, 0x49, 0x50, 0x67, 0x57, 0x70, 0x59, 0x47, 0x33, 0x63, 0x35, + 0x6b, 0x73, 0x31, 0x6e, 0x51, 0x41, 0x5a, 0x47, 0x53, 0x61, 0x37, 0x64, + 0x4c, 0x46, 0x74, 0x44, 0x4d, 0x45, 0x59, 0x4f, 0x78, 0x34, 0x37, 0x36, + 0x77, 0x42, 0x75, 0x48, 0x41, 0x56, 0x53, 0x57, 0x53, 0x41, 0x70, 0x64, + 0x55, 0x4d, 0x38, 0x56, 0x77, 0x0a, 0x6b, 0x2f, 0x6b, 0x63, 0x66, 0x2f, + 0x76, 0x61, 0x6f, 0x33, 0x4f, 0x7a, 0x71, 0x65, 0x33, 0x6f, 0x73, 0x37, + 0x37, 0x43, 0x6f, 0x46, 0x52, 0x77, 0x5a, 0x4d, 0x4e, 0x61, 0x7a, 0x2f, + 0x46, 0x2f, 0x7a, 0x35, 0x53, 0x77, 0x63, 0x47, 0x77, 0x42, 0x6f, 0x65, + 0x34, 0x51, 0x67, 0x69, 0x42, 0x6f, 0x33, 0x39, 0x4b, 0x58, 0x52, 0x33, + 0x5a, 0x63, 0x57, 0x33, 0x48, 0x63, 0x68, 0x76, 0x7a, 0x7a, 0x0a, 0x46, + 0x38, 0x54, 0x36, 0x6d, 0x38, 0x30, 0x78, 0x61, 0x7a, 0x67, 0x54, 0x6a, + 0x51, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, 0x77, 0x2f, 0x66, 0x55, 0x62, + 0x53, 0x78, 0x69, 0x6b, 0x33, 0x78, 0x38, 0x5a, 0x70, 0x59, 0x50, 0x71, + 0x53, 0x4a, 0x5a, 0x6f, 0x62, 0x30, 0x58, 0x76, 0x4b, 0x6b, 0x74, 0x67, + 0x37, 0x52, 0x35, 0x44, 0x4e, 0x32, 0x57, 0x2f, 0x65, 0x4c, 0x6e, 0x46, + 0x34, 0x63, 0x55, 0x0a, 0x45, 0x33, 0x70, 0x2b, 0x38, 0x5a, 0x33, 0x38, + 0x4c, 0x57, 0x76, 0x37, 0x6c, 0x36, 0x72, 0x72, 0x4e, 0x47, 0x4d, 0x41, + 0x4e, 0x48, 0x6a, 0x59, 0x44, 0x47, 0x53, 0x5a, 0x2f, 0x67, 0x79, 0x2b, + 0x48, 0x31, 0x6f, 0x75, 0x38, 0x69, 0x72, 0x54, 0x2b, 0x55, 0x64, 0x42, + 0x6c, 0x6c, 0x58, 0x4b, 0x70, 0x71, 0x44, 0x35, 0x39, 0x5a, 0x56, 0x55, + 0x73, 0x2b, 0x6f, 0x76, 0x4e, 0x73, 0x7a, 0x52, 0x0a, 0x68, 0x50, 0x37, + 0x77, 0x6c, 0x4c, 0x4d, 0x30, 0x42, 0x67, 0x45, 0x44, 0x58, 0x33, 0x6b, + 0x6e, 0x36, 0x78, 0x52, 0x41, 0x63, 0x5a, 0x2b, 0x74, 0x6b, 0x59, 0x53, + 0x64, 0x57, 0x79, 0x63, 0x6f, 0x2b, 0x5a, 0x4b, 0x4f, 0x48, 0x63, 0x66, + 0x71, 0x75, 0x54, 0x2f, 0x72, 0x4a, 0x34, 0x4b, 0x35, 0x42, 0x43, 0x65, + 0x75, 0x33, 0x31, 0x48, 0x6f, 0x78, 0x74, 0x51, 0x6c, 0x78, 0x4a, 0x44, + 0x51, 0x0a, 0x52, 0x36, 0x4b, 0x6a, 0x42, 0x4e, 0x2b, 0x61, 0x63, 0x37, + 0x63, 0x33, 0x6c, 0x32, 0x57, 0x52, 0x47, 0x7a, 0x2f, 0x70, 0x4b, 0x6a, + 0x30, 0x47, 0x39, 0x35, 0x32, 0x71, 0x42, 0x43, 0x72, 0x58, 0x4c, 0x71, + 0x32, 0x32, 0x30, 0x54, 0x4e, 0x71, 0x44, 0x4b, 0x44, 0x44, 0x4c, 0x50, + 0x54, 0x32, 0x5a, 0x46, 0x41, 0x6b, 0x65, 0x48, 0x77, 0x7a, 0x50, 0x58, + 0x32, 0x65, 0x65, 0x71, 0x53, 0x75, 0x0a, 0x62, 0x34, 0x6b, 0x48, 0x45, + 0x49, 0x44, 0x7a, 0x72, 0x66, 0x4d, 0x55, 0x44, 0x35, 0x63, 0x6d, 0x54, + 0x73, 0x42, 0x50, 0x71, 0x30, 0x4c, 0x59, 0x75, 0x6c, 0x67, 0x75, 0x42, + 0x59, 0x78, 0x5a, 0x57, 0x73, 0x50, 0x63, 0x6d, 0x2b, 0x66, 0x57, 0x63, + 0x53, 0x6e, 0x77, 0x39, 0x73, 0x32, 0x4c, 0x2b, 0x45, 0x4c, 0x32, 0x37, + 0x52, 0x39, 0x39, 0x49, 0x67, 0x7a, 0x43, 0x76, 0x64, 0x47, 0x49, 0x0a, + 0x71, 0x52, 0x45, 0x75, 0x2f, 0x69, 0x65, 0x5a, 0x66, 0x79, 0x6d, 0x31, + 0x6e, 0x67, 0x65, 0x58, 0x33, 0x56, 0x70, 0x73, 0x50, 0x4e, 0x54, 0x36, + 0x77, 0x43, 0x55, 0x58, 0x4c, 0x61, 0x63, 0x70, 0x79, 0x4a, 0x46, 0x6d, + 0x59, 0x57, 0x71, 0x65, 0x2f, 0x77, 0x4b, 0x43, 0x41, 0x51, 0x42, 0x44, + 0x75, 0x49, 0x53, 0x6a, 0x7a, 0x4c, 0x4f, 0x30, 0x49, 0x74, 0x36, 0x79, + 0x53, 0x56, 0x75, 0x66, 0x0a, 0x36, 0x63, 0x5a, 0x70, 0x79, 0x50, 0x6a, + 0x4b, 0x46, 0x64, 0x4d, 0x71, 0x4f, 0x76, 0x5a, 0x6d, 0x79, 0x39, 0x4b, + 0x55, 0x76, 0x7a, 0x67, 0x41, 0x54, 0x73, 0x65, 0x65, 0x69, 0x6c, 0x70, + 0x64, 0x51, 0x6d, 0x67, 0x55, 0x4f, 0x4e, 0x65, 0x50, 0x5a, 0x4e, 0x71, + 0x59, 0x2b, 0x4e, 0x53, 0x75, 0x42, 0x5a, 0x51, 0x2f, 0x46, 0x71, 0x44, + 0x31, 0x2f, 0x32, 0x4a, 0x66, 0x31, 0x35, 0x47, 0x43, 0x0a, 0x43, 0x79, + 0x42, 0x76, 0x41, 0x73, 0x59, 0x4e, 0x64, 0x4e, 0x64, 0x72, 0x75, 0x44, + 0x79, 0x75, 0x33, 0x70, 0x4a, 0x35, 0x5a, 0x53, 0x48, 0x30, 0x44, 0x4c, + 0x68, 0x65, 0x44, 0x53, 0x4a, 0x51, 0x34, 0x61, 0x72, 0x69, 0x2b, 0x69, + 0x35, 0x2b, 0x79, 0x52, 0x73, 0x52, 0x72, 0x6b, 0x42, 0x37, 0x4f, 0x32, + 0x6c, 0x43, 0x47, 0x6f, 0x71, 0x48, 0x52, 0x53, 0x43, 0x38, 0x44, 0x4e, + 0x37, 0x36, 0x0a, 0x6a, 0x78, 0x74, 0x6d, 0x39, 0x6b, 0x57, 0x68, 0x79, + 0x4c, 0x31, 0x73, 0x61, 0x67, 0x6c, 0x51, 0x2f, 0x75, 0x71, 0x53, 0x6f, + 0x77, 0x65, 0x66, 0x57, 0x33, 0x62, 0x50, 0x59, 0x59, 0x62, 0x7a, 0x59, + 0x79, 0x44, 0x64, 0x4a, 0x6e, 0x49, 0x46, 0x37, 0x78, 0x7a, 0x45, 0x65, + 0x34, 0x44, 0x74, 0x48, 0x43, 0x38, 0x79, 0x33, 0x64, 0x39, 0x35, 0x77, + 0x6c, 0x6f, 0x6b, 0x71, 0x41, 0x57, 0x55, 0x0a, 0x4e, 0x4e, 0x4c, 0x74, + 0x36, 0x41, 0x49, 0x49, 0x55, 0x75, 0x2f, 0x74, 0x75, 0x7a, 0x69, 0x77, + 0x74, 0x79, 0x57, 0x5a, 0x6b, 0x56, 0x6a, 0x68, 0x64, 0x77, 0x56, 0x53, + 0x32, 0x4c, 0x33, 0x5a, 0x59, 0x6a, 0x7a, 0x4b, 0x7a, 0x4b, 0x79, 0x76, + 0x48, 0x53, 0x63, 0x76, 0x45, 0x56, 0x58, 0x53, 0x56, 0x71, 0x69, 0x6d, + 0x50, 0x52, 0x45, 0x2f, 0x67, 0x30, 0x59, 0x68, 0x30, 0x71, 0x50, 0x4d, + 0x0a, 0x54, 0x6e, 0x55, 0x6c, 0x48, 0x45, 0x63, 0x56, 0x46, 0x71, 0x66, + 0x6e, 0x66, 0x5a, 0x74, 0x63, 0x2f, 0x56, 0x42, 0x6b, 0x7a, 0x34, 0x2b, + 0x67, 0x47, 0x4e, 0x61, 0x36, 0x4e, 0x67, 0x4f, 0x31, 0x78, 0x79, 0x49, + 0x30, 0x71, 0x51, 0x7a, 0x6e, 0x58, 0x79, 0x56, 0x59, 0x45, 0x59, 0x4d, + 0x42, 0x77, 0x78, 0x51, 0x6a, 0x46, 0x38, 0x47, 0x5a, 0x68, 0x4d, 0x73, + 0x47, 0x59, 0x78, 0x30, 0x45, 0x0a, 0x4f, 0x54, 0x50, 0x78, 0x41, 0x6f, + 0x49, 0x42, 0x41, 0x51, 0x43, 0x36, 0x67, 0x31, 0x72, 0x4c, 0x32, 0x59, + 0x32, 0x73, 0x76, 0x37, 0x4f, 0x4b, 0x30, 0x39, 0x48, 0x74, 0x38, 0x51, + 0x4b, 0x4c, 0x2f, 0x48, 0x50, 0x6d, 0x48, 0x4d, 0x4a, 0x7a, 0x38, 0x73, + 0x57, 0x76, 0x61, 0x41, 0x74, 0x34, 0x63, 0x6f, 0x66, 0x74, 0x6a, 0x61, + 0x4f, 0x65, 0x38, 0x6c, 0x78, 0x31, 0x7a, 0x42, 0x36, 0x69, 0x0a, 0x67, + 0x48, 0x37, 0x42, 0x6d, 0x47, 0x77, 0x70, 0x76, 0x50, 0x77, 0x4a, 0x39, + 0x4f, 0x58, 0x7a, 0x7a, 0x5a, 0x2f, 0x2b, 0x2b, 0x74, 0x50, 0x4d, 0x39, + 0x55, 0x46, 0x76, 0x50, 0x54, 0x48, 0x6f, 0x74, 0x46, 0x67, 0x4a, 0x4a, + 0x48, 0x67, 0x75, 0x35, 0x56, 0x46, 0x68, 0x32, 0x70, 0x48, 0x32, 0x64, + 0x72, 0x49, 0x66, 0x48, 0x77, 0x74, 0x65, 0x34, 0x47, 0x48, 0x4c, 0x74, + 0x55, 0x2b, 0x54, 0x0a, 0x55, 0x30, 0x32, 0x4a, 0x68, 0x62, 0x39, 0x6e, + 0x62, 0x79, 0x76, 0x79, 0x75, 0x4c, 0x34, 0x79, 0x6e, 0x4a, 0x78, 0x44, + 0x6b, 0x46, 0x37, 0x67, 0x51, 0x67, 0x73, 0x2b, 0x37, 0x76, 0x4a, 0x4a, + 0x42, 0x44, 0x76, 0x6b, 0x71, 0x2f, 0x63, 0x35, 0x72, 0x43, 0x63, 0x30, + 0x35, 0x7a, 0x6c, 0x38, 0x57, 0x35, 0x66, 0x69, 0x6f, 0x4d, 0x32, 0x4c, + 0x45, 0x4a, 0x44, 0x46, 0x5a, 0x36, 0x67, 0x52, 0x0a, 0x38, 0x46, 0x52, + 0x5a, 0x70, 0x4e, 0x4a, 0x5a, 0x74, 0x42, 0x64, 0x51, 0x47, 0x79, 0x74, + 0x52, 0x61, 0x74, 0x37, 0x51, 0x2f, 0x70, 0x45, 0x31, 0x48, 0x53, 0x4b, + 0x39, 0x73, 0x34, 0x69, 0x41, 0x6f, 0x6c, 0x57, 0x41, 0x71, 0x56, 0x6b, + 0x45, 0x6d, 0x6e, 0x35, 0x4c, 0x71, 0x6a, 0x48, 0x2b, 0x6d, 0x56, 0x76, + 0x48, 0x63, 0x49, 0x75, 0x52, 0x48, 0x6b, 0x4c, 0x36, 0x41, 0x7a, 0x46, + 0x70, 0x0a, 0x34, 0x73, 0x75, 0x36, 0x46, 0x75, 0x72, 0x66, 0x30, 0x77, + 0x74, 0x41, 0x66, 0x65, 0x76, 0x57, 0x56, 0x32, 0x7a, 0x64, 0x33, 0x50, + 0x6d, 0x43, 0x65, 0x4a, 0x32, 0x30, 0x53, 0x52, 0x43, 0x42, 0x50, 0x69, + 0x44, 0x59, 0x56, 0x55, 0x4f, 0x64, 0x73, 0x75, 0x5a, 0x66, 0x51, 0x7a, + 0x51, 0x4f, 0x76, 0x30, 0x52, 0x74, 0x38, 0x30, 0x70, 0x31, 0x56, 0x79, + 0x52, 0x35, 0x77, 0x75, 0x6a, 0x46, 0x0a, 0x37, 0x63, 0x57, 0x73, 0x74, + 0x5a, 0x39, 0x6c, 0x6a, 0x74, 0x55, 0x72, 0x68, 0x4b, 0x31, 0x76, 0x53, + 0x52, 0x33, 0x70, 0x56, 0x6f, 0x74, 0x66, 0x47, 0x48, 0x76, 0x76, 0x78, + 0x64, 0x47, 0x7a, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x51, 0x43, 0x36, 0x59, + 0x70, 0x5a, 0x64, 0x71, 0x4d, 0x38, 0x7a, 0x68, 0x32, 0x45, 0x41, 0x30, + 0x75, 0x69, 0x4e, 0x55, 0x4f, 0x55, 0x2f, 0x5a, 0x63, 0x6d, 0x54, 0x0a, + 0x31, 0x51, 0x6c, 0x4b, 0x67, 0x2b, 0x69, 0x58, 0x4b, 0x66, 0x57, 0x5a, + 0x72, 0x2b, 0x61, 0x64, 0x50, 0x56, 0x50, 0x6b, 0x31, 0x64, 0x54, 0x39, + 0x79, 0x55, 0x78, 0x2f, 0x6e, 0x4e, 0x77, 0x6e, 0x66, 0x43, 0x62, 0x4b, + 0x63, 0x54, 0x45, 0x79, 0x56, 0x74, 0x37, 0x65, 0x41, 0x7a, 0x69, 0x69, + 0x4c, 0x73, 0x5a, 0x77, 0x36, 0x65, 0x58, 0x4f, 0x50, 0x4b, 0x39, 0x55, + 0x38, 0x7a, 0x76, 0x43, 0x0a, 0x4a, 0x50, 0x55, 0x71, 0x67, 0x67, 0x44, + 0x5a, 0x33, 0x2b, 0x6b, 0x62, 0x6b, 0x34, 0x6a, 0x50, 0x44, 0x52, 0x41, + 0x73, 0x30, 0x4a, 0x74, 0x49, 0x6b, 0x43, 0x30, 0x62, 0x75, 0x48, 0x7a, + 0x6b, 0x58, 0x46, 0x30, 0x72, 0x67, 0x41, 0x34, 0x62, 0x4a, 0x7a, 0x75, + 0x55, 0x47, 0x30, 0x44, 0x45, 0x73, 0x58, 0x4a, 0x58, 0x59, 0x71, 0x48, + 0x6f, 0x4f, 0x6d, 0x31, 0x4f, 0x56, 0x4a, 0x66, 0x73, 0x0a, 0x78, 0x6f, + 0x4c, 0x50, 0x74, 0x68, 0x71, 0x55, 0x44, 0x30, 0x44, 0x32, 0x73, 0x43, + 0x59, 0x37, 0x46, 0x62, 0x4a, 0x58, 0x67, 0x4e, 0x4d, 0x61, 0x33, 0x76, + 0x59, 0x62, 0x31, 0x39, 0x6b, 0x78, 0x65, 0x4a, 0x37, 0x76, 0x37, 0x70, + 0x69, 0x42, 0x72, 0x69, 0x68, 0x4c, 0x6b, 0x35, 0x4c, 0x57, 0x64, 0x41, + 0x78, 0x32, 0x6c, 0x53, 0x56, 0x30, 0x6c, 0x61, 0x46, 0x45, 0x39, 0x4f, + 0x38, 0x43, 0x0a, 0x68, 0x36, 0x48, 0x61, 0x5a, 0x65, 0x52, 0x39, 0x4a, + 0x49, 0x69, 0x42, 0x79, 0x63, 0x5a, 0x39, 0x71, 0x48, 0x49, 0x58, 0x50, + 0x50, 0x43, 0x79, 0x5a, 0x57, 0x51, 0x4f, 0x6c, 0x6c, 0x43, 0x49, 0x56, + 0x69, 0x31, 0x71, 0x53, 0x51, 0x6b, 0x70, 0x51, 0x4e, 0x34, 0x76, 0x59, + 0x4e, 0x68, 0x63, 0x2f, 0x49, 0x45, 0x37, 0x4d, 0x76, 0x51, 0x31, 0x4a, + 0x42, 0x61, 0x4f, 0x62, 0x6c, 0x4a, 0x48, 0x0a, 0x63, 0x73, 0x63, 0x49, + 0x4c, 0x43, 0x38, 0x61, 0x69, 0x4a, 0x56, 0x46, 0x4f, 0x53, 0x5a, 0x6e, + 0x4e, 0x6a, 0x4b, 0x2f, 0x66, 0x6c, 0x6f, 0x72, 0x78, 0x74, 0x54, 0x4f, + 0x44, 0x44, 0x39, 0x49, 0x47, 0x33, 0x67, 0x48, 0x66, 0x78, 0x54, 0x52, + 0x31, 0x73, 0x66, 0x61, 0x4c, 0x76, 0x35, 0x7a, 0x43, 0x51, 0x66, 0x39, + 0x77, 0x4f, 0x49, 0x35, 0x52, 0x49, 0x2f, 0x78, 0x0a, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x52, 0x53, 0x41, 0x20, 0x50, 0x52, + 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a +}; + +unsigned char client_key_der[] = { + 0x30, 0x82, 0x09, 0x29, 0x02, 0x01, 0x00, 0x02, 0x82, 0x02, 0x01, 0x00, + 0xAF, 0x04, 0xB2, 0x36, 0x78, 0x5F, 0xBC, 0x38, 0x9C, 0x18, 0x93, 0x81, + 0x7D, 0x44, 0x0B, 0x18, 0x4A, 0x4A, 0x0B, 0x6F, 0xCD, 0xDB, 0x94, 0xE8, + 0xD8, 0xA7, 0xFA, 0xEB, 0x39, 0x03, 0x6F, 0x79, 0xB8, 0x20, 0xFC, 0x0B, + 0x71, 0xCC, 0x46, 0x58, 0x75, 0xAE, 0x7B, 0x18, 0xA9, 0x9A, 0xF0, 0x30, + 0x17, 0xE5, 0xF6, 0xE8, 0x51, 0xB3, 0xE5, 0xF2, 0x5D, 0xAB, 0xC1, 0x61, + 0xC2, 0x5A, 0xBE, 0x9E, 0xA8, 0xF8, 0x3E, 0x19, 0x7F, 0x43, 0xEC, 0x14, + 0x06, 0x54, 0x3E, 0x5C, 0xC8, 0xEA, 0x22, 0x9C, 0x32, 0xCE, 0x96, 0x66, + 0xE0, 0x79, 0xBE, 0x38, 0xE0, 0x8A, 0x7E, 0xCC, 0x97, 0x76, 0x62, 0xCB, + 0xA4, 0x5C, 0x95, 0x93, 0x8A, 0xEB, 0xD8, 0x64, 0x38, 0x28, 0x54, 0x90, + 0x5D, 0x05, 0xF2, 0x67, 0x1C, 0x5B, 0x34, 0x57, 0x60, 0x9C, 0x2A, 0xEF, + 0x9A, 0xD9, 0x08, 0x05, 0x06, 0xEF, 0x9A, 0x70, 0x72, 0x3F, 0xEC, 0x27, + 0x7B, 0x38, 0x54, 0x40, 0xA2, 0xE4, 0x99, 0x03, 0xF6, 0x4A, 0x3E, 0xDE, + 0x47, 0x32, 0xE8, 0xC9, 0x7D, 0xAA, 0x00, 0x8B, 0x82, 0x15, 0x86, 0xD6, + 0x03, 0xA0, 0xD6, 0x4B, 0xD3, 0xCB, 0x56, 0xE8, 0x2A, 0x4A, 0xF2, 0xDE, + 0x42, 0xAC, 0xF5, 0xB6, 0x9D, 0x9F, 0x66, 0x98, 0x41, 0x76, 0x37, 0xC8, + 0x95, 0xE0, 0xC1, 0xBB, 0x36, 0x20, 0x07, 0x46, 0x9C, 0xF7, 0x20, 0xC7, + 0xD6, 0x67, 0x9E, 0x40, 0xE0, 0x8D, 0x3D, 0xAC, 0xD1, 0x01, 0xC5, 0x96, + 0xC2, 0xEF, 0xA8, 0x55, 0x69, 0x84, 0x04, 0x49, 0x0F, 0x0C, 0x8D, 0x69, + 0x86, 0xE9, 0xC3, 0xF7, 0x7D, 0xC6, 0xD5, 0x17, 0x16, 0x76, 0x64, 0x14, + 0xAF, 0xB9, 0x07, 0x78, 0xF3, 0x0F, 0xC3, 0x9A, 0x77, 0x07, 0x3F, 0xAA, + 0x8E, 0xA6, 0x87, 0xFB, 0xAA, 0x02, 0x4E, 0x16, 0x42, 0xA3, 0xE6, 0x14, + 0xF6, 0x14, 0x3F, 0xE3, 0xE1, 0xF0, 0x30, 0xDC, 0xBC, 0x42, 0xCC, 0xA3, + 0x6A, 0x67, 0xFB, 0xD7, 0x3E, 0x60, 0xE0, 0x28, 0x5E, 0xDB, 0x84, 0x0F, + 0x7D, 0xC9, 0xF4, 0xDD, 0x6C, 0xBF, 0x65, 0x16, 0xAE, 0x4A, 0x0B, 0x10, + 0x9F, 0x2C, 0xE1, 0xAD, 0x9C, 0xFD, 0x58, 0x9E, 0x7E, 0xF3, 0x05, 0x2A, + 0x8F, 0x8E, 0x10, 0xF0, 0x4E, 0xA9, 0x61, 0x12, 0xF4, 0x87, 0x8C, 0x5E, + 0x22, 0xC1, 0x0E, 0xBF, 0xE2, 0x30, 0x35, 0xB5, 0x58, 0x9B, 0xC5, 0xB5, + 0x6C, 0x76, 0x5B, 0x75, 0x6D, 0x52, 0x78, 0xCC, 0x29, 0xB4, 0x46, 0xFB, + 0x18, 0xF8, 0x14, 0x68, 0xEF, 0xF2, 0x27, 0x3F, 0xE1, 0x2A, 0xA7, 0x2B, + 0xA4, 0x82, 0x7B, 0xFC, 0x8A, 0x1A, 0x01, 0x32, 0xBE, 0x8F, 0xDA, 0x7C, + 0xD9, 0xBD, 0xD0, 0x8B, 0x6F, 0xD4, 0x4E, 0x9A, 0x88, 0x0C, 0x88, 0x32, + 0xC9, 0x90, 0x0B, 0x52, 0x4E, 0xA1, 0x58, 0x7F, 0x7B, 0x4A, 0x83, 0x1A, + 0xAA, 0x48, 0x54, 0x8E, 0xC0, 0xFC, 0xEB, 0x33, 0xF6, 0xD4, 0xA1, 0xC4, + 0x37, 0x91, 0xB9, 0x91, 0x7D, 0xFA, 0x03, 0x0C, 0x9F, 0x48, 0x40, 0x26, + 0x46, 0xF9, 0x47, 0x73, 0x31, 0xA3, 0xC5, 0xFA, 0x90, 0x3C, 0x1A, 0xB3, + 0x31, 0xC7, 0xBD, 0x15, 0x2C, 0x7D, 0x37, 0xE4, 0x7D, 0x6F, 0x0F, 0xD5, + 0x06, 0xC7, 0xC2, 0x73, 0x31, 0x79, 0x2C, 0xA9, 0xBB, 0x46, 0x88, 0xC7, + 0x0F, 0x9C, 0xF0, 0x39, 0xB3, 0xB7, 0xE9, 0x6C, 0xED, 0xB8, 0xF6, 0x9B, + 0x6E, 0x87, 0xF3, 0xD2, 0x61, 0x62, 0x57, 0x2B, 0x85, 0xDD, 0xC3, 0x9B, + 0xFE, 0xDA, 0x86, 0x55, 0x5F, 0x77, 0x9C, 0x8A, 0xC0, 0x05, 0xBC, 0xBF, + 0x0D, 0xB0, 0x5B, 0x21, 0x02, 0x57, 0x2E, 0xCB, 0x2D, 0x9E, 0x9C, 0x29, + 0x95, 0x96, 0xDA, 0x66, 0x46, 0x2A, 0x3E, 0xF3, 0xBB, 0x19, 0x15, 0x7B, + 0xD0, 0x7F, 0x30, 0x34, 0xEE, 0x4E, 0x7F, 0x73, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x02, 0x00, 0x65, 0xC7, 0x75, 0x5B, 0xF7, 0xE4, 0xB4, + 0xB0, 0xB7, 0x16, 0x13, 0xB1, 0xDA, 0x07, 0x17, 0xEC, 0xA8, 0x47, 0x27, + 0x1F, 0x7D, 0xB7, 0x94, 0x81, 0x54, 0x7D, 0x5B, 0x8E, 0x2F, 0x39, 0xB0, + 0x3A, 0x38, 0xB9, 0xF5, 0xD9, 0x31, 0xF4, 0x79, 0x74, 0x37, 0xFB, 0x9F, + 0xDA, 0x57, 0xC7, 0xA2, 0xA6, 0x55, 0x3F, 0x86, 0xB6, 0xD3, 0xCC, 0xAA, + 0x8C, 0xA7, 0xAC, 0x9A, 0x69, 0x1F, 0x7D, 0x66, 0x1D, 0x1E, 0x66, 0x3E, + 0xB6, 0xD5, 0x2B, 0xCA, 0xBE, 0x5A, 0x6F, 0xE3, 0x54, 0x52, 0x02, 0xD6, + 0xDD, 0xBF, 0x2E, 0x24, 0x88, 0xEF, 0x41, 0xDD, 0x3D, 0x76, 0x57, 0x14, + 0x26, 0x26, 0x6E, 0xD2, 0x45, 0x5A, 0xEF, 0xFC, 0x8C, 0x30, 0xDD, 0xB6, + 0x99, 0x91, 0x8E, 0xE2, 0x9E, 0x91, 0x7A, 0x43, 0xDD, 0x72, 0xC0, 0x17, + 0x50, 0x38, 0xFE, 0x98, 0x79, 0x7E, 0xB3, 0x77, 0xD6, 0x74, 0x92, 0x22, + 0x5E, 0x02, 0x12, 0xEA, 0x62, 0x8B, 0x65, 0xD6, 0x99, 0x40, 0x21, 0x70, + 0x5A, 0xBD, 0x7F, 0xAF, 0xCF, 0xD3, 0xD7, 0xE7, 0x48, 0x45, 0xAB, 0xF7, + 0x65, 0x29, 0xE1, 0x1A, 0xE8, 0x9A, 0x73, 0x8E, 0x2B, 0xCA, 0x9F, 0x15, + 0x8B, 0x2E, 0x0D, 0x27, 0xEF, 0xD9, 0x16, 0x7C, 0x4E, 0x16, 0x9B, 0xFC, + 0x2D, 0xCE, 0xE3, 0xD0, 0xCC, 0xF5, 0x5C, 0x1F, 0x4D, 0x9F, 0xCC, 0x40, + 0x6E, 0xF0, 0x19, 0x47, 0x16, 0xEF, 0xB8, 0x92, 0x6F, 0x68, 0x26, 0xBB, + 0x17, 0x24, 0xF4, 0xC2, 0xD3, 0x49, 0xC9, 0xD5, 0x3A, 0xD6, 0x81, 0xCF, + 0x7C, 0x2D, 0x24, 0x3F, 0x98, 0x7E, 0xBC, 0xB3, 0x19, 0xDA, 0x2B, 0x27, + 0x00, 0xA6, 0x2F, 0x73, 0x44, 0x14, 0x6C, 0xA5, 0xCB, 0xD0, 0x8D, 0xFA, + 0x52, 0x79, 0xA5, 0xDD, 0x80, 0x72, 0x34, 0xA6, 0x1F, 0xD4, 0x5E, 0x42, + 0xCA, 0xEF, 0x36, 0x21, 0x37, 0x4F, 0x63, 0xB7, 0x42, 0xF7, 0xD5, 0x28, + 0x9F, 0xF1, 0xB9, 0x7D, 0x15, 0x15, 0x9D, 0xA9, 0x97, 0xE0, 0x0F, 0x8D, + 0x6D, 0xF1, 0x84, 0x86, 0x52, 0x76, 0x5D, 0x75, 0x10, 0x5D, 0x38, 0xA1, + 0x03, 0x01, 0x6E, 0x64, 0x7A, 0xD3, 0x1C, 0x02, 0x57, 0xF5, 0xDA, 0x30, + 0xE0, 0xB4, 0x10, 0xB8, 0x88, 0x43, 0x3C, 0xE6, 0x14, 0x37, 0x36, 0x68, + 0xE1, 0x6D, 0x38, 0x0C, 0x32, 0x7E, 0x6E, 0xE7, 0xF8, 0x83, 0x3A, 0x4D, + 0x87, 0x5F, 0x58, 0x45, 0xE0, 0x43, 0xBB, 0x38, 0x37, 0x09, 0x36, 0xD8, + 0x2B, 0xC4, 0x44, 0x9D, 0x7B, 0xCC, 0x63, 0x82, 0x83, 0xE9, 0xC8, 0x8C, + 0x3A, 0xB6, 0xB1, 0x25, 0x2D, 0x42, 0x22, 0x68, 0x12, 0xA6, 0x15, 0x69, + 0x3F, 0xB4, 0xF0, 0x38, 0xE9, 0x5B, 0xBB, 0x18, 0xFD, 0xBF, 0x51, 0x1C, + 0x4C, 0x0C, 0x78, 0x22, 0x1D, 0x7C, 0xAA, 0xBA, 0xA5, 0xEB, 0x6D, 0x95, + 0xE0, 0x4A, 0x78, 0x95, 0xBF, 0xC9, 0xC5, 0x69, 0xD7, 0x34, 0x7C, 0x3F, + 0xCF, 0x67, 0x69, 0x74, 0xC5, 0x24, 0x24, 0x97, 0xF7, 0x96, 0x69, 0xFE, + 0xFF, 0x86, 0x55, 0xFA, 0x03, 0x41, 0xB4, 0x1C, 0xAA, 0x2D, 0x12, 0x0F, + 0x4C, 0x6D, 0xA5, 0xEC, 0xEE, 0x14, 0x9D, 0x1F, 0xF6, 0x8A, 0xEA, 0xB1, + 0xD3, 0x02, 0xA9, 0x63, 0xB4, 0xE1, 0xE0, 0x87, 0xD7, 0x8D, 0x25, 0x56, + 0xEA, 0xC3, 0x9C, 0xF8, 0x9C, 0x3B, 0x77, 0x98, 0x6C, 0x57, 0xFC, 0xFB, + 0x01, 0x44, 0x44, 0x10, 0x9E, 0x16, 0x5F, 0xA3, 0x4D, 0xA4, 0x1C, 0x10, + 0x64, 0xDD, 0x95, 0xC4, 0x7D, 0x3B, 0x23, 0x1C, 0xAF, 0x93, 0x76, 0x50, + 0x5E, 0x05, 0x50, 0x5C, 0xD8, 0x6B, 0xC8, 0xD0, 0x88, 0x5A, 0x9B, 0x7F, + 0x7D, 0x68, 0xAF, 0xB8, 0xF0, 0xB3, 0xC2, 0xDD, 0x94, 0xB4, 0xEA, 0xE9, + 0xFF, 0x95, 0x82, 0xE7, 0xBC, 0x35, 0x79, 0xE3, 0x2C, 0x5D, 0xC8, 0x6B, + 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE4, 0xA1, 0xF3, 0x2F, 0x8B, 0xD6, + 0x05, 0x77, 0x9F, 0xA3, 0xAF, 0xCA, 0x87, 0x02, 0x09, 0x03, 0xBC, 0xB9, + 0x4D, 0x28, 0x00, 0x6C, 0x38, 0x58, 0xA5, 0x08, 0xB8, 0xD8, 0xB9, 0x6D, + 0xDF, 0xB3, 0x4C, 0x9C, 0x97, 0x32, 0x29, 0x35, 0x66, 0xCB, 0x1A, 0x5F, + 0x60, 0x6F, 0x1E, 0x9E, 0x9D, 0x45, 0xAB, 0x34, 0xD5, 0x62, 0xD7, 0x1E, + 0x64, 0x8C, 0x37, 0x7B, 0xD4, 0xF9, 0x1C, 0x63, 0x6A, 0x52, 0x3A, 0x68, + 0xC4, 0x2A, 0x79, 0xDC, 0xA7, 0x03, 0xBB, 0xC8, 0x86, 0x17, 0xA0, 0x90, + 0xA8, 0x93, 0x05, 0xAE, 0x4F, 0x06, 0x91, 0x54, 0x14, 0xF3, 0x5A, 0x97, + 0xB9, 0x6A, 0x00, 0x3F, 0x6C, 0x89, 0x7D, 0x04, 0xA6, 0x06, 0xE8, 0x60, + 0x3D, 0xD1, 0xCA, 0x50, 0xAD, 0x61, 0x1B, 0x5A, 0x3A, 0xD8, 0x31, 0xF6, + 0x81, 0x6D, 0xDE, 0x9F, 0xB8, 0xF7, 0xA7, 0x10, 0xA9, 0xE0, 0xD8, 0x69, + 0xF4, 0x8A, 0x7F, 0xE7, 0xD8, 0x34, 0x2E, 0xC3, 0xC3, 0x06, 0x0E, 0xA7, + 0x62, 0x2F, 0x31, 0xE5, 0xA0, 0xCD, 0xA1, 0x89, 0xAF, 0x11, 0x51, 0x00, + 0xE4, 0x54, 0x73, 0xEE, 0xEF, 0xF0, 0xC2, 0x0C, 0xA6, 0x18, 0x83, 0xE0, + 0x5A, 0x96, 0x06, 0xDD, 0xCE, 0x64, 0xB3, 0x59, 0xD0, 0x01, 0x91, 0x92, + 0x6B, 0xB7, 0x4B, 0x16, 0xD0, 0xCC, 0x11, 0x83, 0xB1, 0xE3, 0xBE, 0xB0, + 0x06, 0xE1, 0xC0, 0x55, 0x25, 0x92, 0x02, 0x97, 0x54, 0x33, 0xC5, 0x70, + 0x93, 0xF9, 0x1C, 0x7F, 0xFB, 0xDA, 0xA3, 0x73, 0xB3, 0xA9, 0xED, 0xE8, + 0xB3, 0xBE, 0xC2, 0xA0, 0x54, 0x70, 0x64, 0xC3, 0x5A, 0xCF, 0xF1, 0x7F, + 0xCF, 0x94, 0xB0, 0x70, 0x6C, 0x01, 0xA1, 0xEE, 0x10, 0x82, 0x20, 0x68, + 0xDF, 0xD2, 0x97, 0x47, 0x76, 0x5C, 0x5B, 0x71, 0xDC, 0x86, 0xFC, 0xF3, + 0x17, 0xC4, 0xFA, 0x9B, 0xCD, 0x31, 0x6B, 0x38, 0x13, 0x8D, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xC3, 0xF7, 0xD4, 0x6D, 0x2C, 0x62, 0x93, 0x7C, 0x7C, + 0x66, 0x96, 0x0F, 0xA9, 0x22, 0x59, 0xA1, 0xBD, 0x17, 0xBC, 0xA9, 0x2D, + 0x83, 0xB4, 0x79, 0x0C, 0xDD, 0x96, 0xFD, 0xE2, 0xE7, 0x17, 0x87, 0x14, + 0x13, 0x7A, 0x7E, 0xF1, 0x9D, 0xFC, 0x2D, 0x6B, 0xFB, 0x97, 0xAA, 0xEB, + 0x34, 0x63, 0x00, 0x34, 0x78, 0xD8, 0x0C, 0x64, 0x99, 0xFE, 0x0C, 0xBE, + 0x1F, 0x5A, 0x2E, 0xF2, 0x2A, 0xD3, 0xF9, 0x47, 0x41, 0x96, 0x55, 0xCA, + 0xA6, 0xA0, 0xF9, 0xF5, 0x95, 0x54, 0xB3, 0xEA, 0x2F, 0x36, 0xCC, 0xD1, + 0x84, 0xFE, 0xF0, 0x94, 0xB3, 0x34, 0x06, 0x01, 0x03, 0x5F, 0x79, 0x27, + 0xEB, 0x14, 0x40, 0x71, 0x9F, 0xAD, 0x91, 0x84, 0x9D, 0x5B, 0x27, 0x28, + 0xF9, 0x92, 0x8E, 0x1D, 0xC7, 0xEA, 0xB9, 0x3F, 0xEB, 0x27, 0x82, 0xB9, + 0x04, 0x27, 0xAE, 0xDF, 0x51, 0xE8, 0xC6, 0xD4, 0x25, 0xC4, 0x90, 0xD0, + 0x47, 0xA2, 0xA3, 0x04, 0xDF, 0x9A, 0x73, 0xB7, 0x37, 0x97, 0x65, 0x91, + 0x1B, 0x3F, 0xE9, 0x2A, 0x3D, 0x06, 0xF7, 0x9D, 0xAA, 0x04, 0x2A, 0xD7, + 0x2E, 0xAD, 0xB6, 0xD1, 0x33, 0x6A, 0x0C, 0xA0, 0xC3, 0x2C, 0xF4, 0xF6, + 0x64, 0x50, 0x24, 0x78, 0x7C, 0x33, 0x3D, 0x7D, 0x9E, 0x7A, 0xA4, 0xAE, + 0x6F, 0x89, 0x07, 0x10, 0x80, 0xF3, 0xAD, 0xF3, 0x14, 0x0F, 0x97, 0x26, + 0x4E, 0xC0, 0x4F, 0xAB, 0x42, 0xD8, 0xBA, 0x58, 0x2E, 0x05, 0x8C, 0x59, + 0x5A, 0xC3, 0xDC, 0x9B, 0xE7, 0xD6, 0x71, 0x29, 0xF0, 0xF6, 0xCD, 0x8B, + 0xF8, 0x42, 0xF6, 0xED, 0x1F, 0x7D, 0x22, 0x0C, 0xC2, 0xBD, 0xD1, 0x88, + 0xA9, 0x11, 0x2E, 0xFE, 0x27, 0x99, 0x7F, 0x29, 0xB5, 0x9E, 0x07, 0x97, + 0xDD, 0x5A, 0x6C, 0x3C, 0xD4, 0xFA, 0xC0, 0x25, 0x17, 0x2D, 0xA7, 0x29, + 0xC8, 0x91, 0x66, 0x61, 0x6A, 0x9E, 0xFF, 0x02, 0x82, 0x01, 0x00, 0x43, + 0xB8, 0x84, 0xA3, 0xCC, 0xB3, 0xB4, 0x22, 0xDE, 0xB2, 0x49, 0x5B, 0x9F, + 0xE9, 0xC6, 0x69, 0xC8, 0xF8, 0xCA, 0x15, 0xD3, 0x2A, 0x3A, 0xF6, 0x66, + 0xCB, 0xD2, 0x94, 0xBF, 0x38, 0x00, 0x4E, 0xC7, 0x9E, 0x8A, 0x5A, 0x5D, + 0x42, 0x68, 0x14, 0x38, 0xD7, 0x8F, 0x64, 0xDA, 0x98, 0xF8, 0xD4, 0xAE, + 0x05, 0x94, 0x3F, 0x16, 0xA0, 0xF5, 0xFF, 0x62, 0x5F, 0xD7, 0x91, 0x82, + 0x0B, 0x20, 0x6F, 0x02, 0xC6, 0x0D, 0x74, 0xD7, 0x6B, 0xB8, 0x3C, 0xAE, + 0xDE, 0x92, 0x79, 0x65, 0x21, 0xF4, 0x0C, 0xB8, 0x5E, 0x0D, 0x22, 0x50, + 0xE1, 0xAA, 0xE2, 0xFA, 0x2E, 0x7E, 0xC9, 0x1B, 0x11, 0xAE, 0x40, 0x7B, + 0x3B, 0x69, 0x42, 0x1A, 0x8A, 0x87, 0x45, 0x20, 0xBC, 0x0C, 0xDE, 0xFA, + 0x8F, 0x1B, 0x66, 0xF6, 0x45, 0xA1, 0xC8, 0xBD, 0x6C, 0x6A, 0x09, 0x50, + 0xFE, 0xEA, 0x92, 0xA3, 0x07, 0x9F, 0x5B, 0x76, 0xCF, 0x61, 0x86, 0xF3, + 0x63, 0x20, 0xDD, 0x26, 0x72, 0x05, 0xEF, 0x1C, 0xC4, 0x7B, 0x80, 0xED, + 0x1C, 0x2F, 0x32, 0xDD, 0xDF, 0x79, 0xC2, 0x5A, 0x24, 0xA8, 0x05, 0x94, + 0x34, 0xD2, 0xED, 0xE8, 0x02, 0x08, 0x52, 0xEF, 0xED, 0xBB, 0x38, 0xB0, + 0xB7, 0x25, 0x99, 0x91, 0x58, 0xE1, 0x77, 0x05, 0x52, 0xD8, 0xBD, 0xD9, + 0x62, 0x3C, 0xCA, 0xCC, 0xAC, 0xAF, 0x1D, 0x27, 0x2F, 0x11, 0x55, 0xD2, + 0x56, 0xA8, 0xA6, 0x3D, 0x11, 0x3F, 0x83, 0x46, 0x21, 0xD2, 0xA3, 0xCC, + 0x4E, 0x75, 0x25, 0x1C, 0x47, 0x15, 0x16, 0xA7, 0xE7, 0x7D, 0x9B, 0x5C, + 0xFD, 0x50, 0x64, 0xCF, 0x8F, 0xA0, 0x18, 0xD6, 0xBA, 0x36, 0x03, 0xB5, + 0xC7, 0x22, 0x34, 0xA9, 0x0C, 0xE7, 0x5F, 0x25, 0x58, 0x11, 0x83, 0x01, + 0xC3, 0x14, 0x23, 0x17, 0xC1, 0x99, 0x84, 0xCB, 0x06, 0x63, 0x1D, 0x04, + 0x39, 0x33, 0xF1, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x83, 0x5A, 0xCB, + 0xD9, 0x8D, 0xAC, 0xBF, 0xB3, 0x8A, 0xD3, 0xD1, 0xED, 0xF1, 0x02, 0x8B, + 0xFC, 0x73, 0xE6, 0x1C, 0xC2, 0x73, 0xF2, 0xC5, 0xAF, 0x68, 0x0B, 0x78, + 0x72, 0x87, 0xED, 0x8D, 0xA3, 0x9E, 0xF2, 0x5C, 0x75, 0xCC, 0x1E, 0xA2, + 0x80, 0x7E, 0xC1, 0x98, 0x6C, 0x29, 0xBC, 0xFC, 0x09, 0xF4, 0xE5, 0xF3, + 0xCD, 0x9F, 0xFE, 0xFA, 0xD3, 0xCC, 0xF5, 0x41, 0x6F, 0x3D, 0x31, 0xE8, + 0xB4, 0x58, 0x09, 0x24, 0x78, 0x2E, 0xE5, 0x51, 0x61, 0xDA, 0x91, 0xF6, + 0x76, 0xB2, 0x1F, 0x1F, 0x0B, 0x5E, 0xE0, 0x61, 0xCB, 0xB5, 0x4F, 0x93, + 0x53, 0x4D, 0x89, 0x85, 0xBF, 0x67, 0x6F, 0x2B, 0xF2, 0xB8, 0xBE, 0x32, + 0x9C, 0x9C, 0x43, 0x90, 0x5E, 0xE0, 0x42, 0x0B, 0x3E, 0xEE, 0xF2, 0x49, + 0x04, 0x3B, 0xE4, 0xAB, 0xF7, 0x39, 0xAC, 0x27, 0x34, 0xE7, 0x39, 0x7C, + 0x5B, 0x97, 0xE2, 0xA0, 0xCD, 0x8B, 0x10, 0x90, 0xC5, 0x67, 0xA8, 0x11, + 0xF0, 0x54, 0x59, 0xA4, 0xD2, 0x59, 0xB4, 0x17, 0x50, 0x1B, 0x2B, 0x51, + 0x6A, 0xDE, 0xD0, 0xFE, 0x91, 0x35, 0x1D, 0x22, 0xBD, 0xB3, 0x88, 0x80, + 0xA2, 0x55, 0x80, 0xA9, 0x59, 0x04, 0x9A, 0x7E, 0x4B, 0xAA, 0x31, 0xFE, + 0x99, 0x5B, 0xC7, 0x70, 0x8B, 0x91, 0x1E, 0x42, 0xFA, 0x03, 0x31, 0x69, + 0xE2, 0xCB, 0xBA, 0x16, 0xEA, 0xDF, 0xD3, 0x0B, 0x40, 0x7D, 0xEB, 0xD6, + 0x57, 0x6C, 0xDD, 0xDC, 0xF9, 0x82, 0x78, 0x9D, 0xB4, 0x49, 0x10, 0x81, + 0x3E, 0x20, 0xD8, 0x55, 0x43, 0x9D, 0xB2, 0xE6, 0x5F, 0x43, 0x34, 0x0E, + 0xBF, 0x44, 0x6D, 0xF3, 0x4A, 0x75, 0x57, 0x24, 0x79, 0xC2, 0xE8, 0xC5, + 0xED, 0xC5, 0xAC, 0xB5, 0x9F, 0x65, 0x8E, 0xD5, 0x2B, 0x84, 0xAD, 0x6F, + 0x49, 0x1D, 0xE9, 0x56, 0x8B, 0x5F, 0x18, 0x7B, 0xEF, 0xC5, 0xD1, 0xB3, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x62, 0x96, 0x5D, 0xA8, 0xCF, 0x33, + 0x87, 0x61, 0x00, 0xD2, 0xE8, 0x8D, 0x50, 0xE5, 0x3F, 0x65, 0xC9, 0x93, + 0xD5, 0x09, 0x4A, 0x83, 0xE8, 0x97, 0x29, 0xF5, 0x99, 0xAF, 0xE6, 0x9D, + 0x3D, 0x53, 0xE4, 0xD5, 0xD4, 0xFD, 0xC9, 0x4C, 0x7F, 0x9C, 0xDC, 0x27, + 0x7C, 0x26, 0xCA, 0x71, 0x31, 0x32, 0x56, 0xDE, 0xDE, 0x03, 0x38, 0xA2, + 0x2E, 0xC6, 0x70, 0xE9, 0xE5, 0xCE, 0x3C, 0xAF, 0x54, 0xF3, 0x3B, 0xC2, + 0x24, 0xF5, 0x2A, 0x82, 0x00, 0xD9, 0xDF, 0xE9, 0x1B, 0x93, 0x88, 0xCF, + 0x0D, 0x10, 0x2C, 0xD0, 0x9B, 0x48, 0x90, 0x2D, 0x1B, 0xB8, 0x7C, 0xE4, + 0x5C, 0x5D, 0x2B, 0x80, 0x0E, 0x1B, 0x27, 0x3B, 0x94, 0x1B, 0x40, 0xC4, + 0xB1, 0x72, 0x57, 0x62, 0xA1, 0xE8, 0x3A, 0x6D, 0x4E, 0x54, 0x97, 0xEC, + 0xC6, 0x82, 0xCF, 0xB6, 0x1A, 0x94, 0x0F, 0x40, 0xF6, 0xB0, 0x26, 0x3B, + 0x15, 0xB2, 0x57, 0x80, 0xD3, 0x1A, 0xDE, 0xF6, 0x1B, 0xD7, 0xD9, 0x31, + 0x78, 0x9E, 0xEF, 0xEE, 0x98, 0x81, 0xAE, 0x28, 0x4B, 0x93, 0x92, 0xD6, + 0x74, 0x0C, 0x76, 0x95, 0x25, 0x74, 0x95, 0xA1, 0x44, 0xF4, 0xEF, 0x02, + 0x87, 0xA1, 0xDA, 0x65, 0xE4, 0x7D, 0x24, 0x88, 0x81, 0xC9, 0xC6, 0x7D, + 0xA8, 0x72, 0x17, 0x3C, 0xF0, 0xB2, 0x65, 0x64, 0x0E, 0x96, 0x50, 0x88, + 0x56, 0x2D, 0x6A, 0x49, 0x09, 0x29, 0x40, 0xDE, 0x2F, 0x60, 0xD8, 0x5C, + 0xFC, 0x81, 0x3B, 0x32, 0xF4, 0x35, 0x24, 0x16, 0x8E, 0x6E, 0x52, 0x47, + 0x72, 0xC7, 0x08, 0x2C, 0x2F, 0x1A, 0x88, 0x95, 0x45, 0x39, 0x26, 0x67, + 0x36, 0x32, 0xBF, 0x7E, 0x5A, 0x2B, 0xC6, 0xD4, 0xCE, 0x0C, 0x3F, 0x48, + 0x1B, 0x78, 0x07, 0x7F, 0x14, 0xD1, 0xD6, 0xC7, 0xDA, 0x2E, 0xFE, 0x73, + 0x09, 0x07, 0xFD, 0xC0, 0xE2, 0x39, 0x44, 0x8F, 0xF1 +}; + +unsigned char client_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x44, + 0x43, + 0x43, + 0x41, + 0x77, + 0x43, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x45, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x78, + 0x4f, + 0x46, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x45, + 0x34, + 0x57, + 0x6a, + 0x42, + 0x4d, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x42, + 0x6d, + 0x4e, + 0x73, + 0x61, + 0x57, + 0x56, + 0x75, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x0a, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4b, + 0x38, + 0x45, + 0x73, + 0x6a, + 0x5a, + 0x34, + 0x58, + 0x37, + 0x77, + 0x34, + 0x6e, + 0x42, + 0x69, + 0x54, + 0x67, + 0x58, + 0x31, + 0x45, + 0x43, + 0x78, + 0x68, + 0x4b, + 0x53, + 0x67, + 0x74, + 0x76, + 0x7a, + 0x64, + 0x75, + 0x55, + 0x36, + 0x4e, + 0x69, + 0x6e, + 0x2b, + 0x75, + 0x73, + 0x35, + 0x0a, + 0x41, + 0x32, + 0x39, + 0x35, + 0x75, + 0x43, + 0x44, + 0x38, + 0x43, + 0x33, + 0x48, + 0x4d, + 0x52, + 0x6c, + 0x68, + 0x31, + 0x72, + 0x6e, + 0x73, + 0x59, + 0x71, + 0x5a, + 0x72, + 0x77, + 0x4d, + 0x42, + 0x66, + 0x6c, + 0x39, + 0x75, + 0x68, + 0x52, + 0x73, + 0x2b, + 0x58, + 0x79, + 0x58, + 0x61, + 0x76, + 0x42, + 0x59, + 0x63, + 0x4a, + 0x61, + 0x76, + 0x70, + 0x36, + 0x6f, + 0x2b, + 0x44, + 0x34, + 0x5a, + 0x66, + 0x30, + 0x50, + 0x73, + 0x46, + 0x41, + 0x5a, + 0x55, + 0x50, + 0x6c, + 0x7a, + 0x49, + 0x0a, + 0x36, + 0x69, + 0x4b, + 0x63, + 0x4d, + 0x73, + 0x36, + 0x57, + 0x5a, + 0x75, + 0x42, + 0x35, + 0x76, + 0x6a, + 0x6a, + 0x67, + 0x69, + 0x6e, + 0x37, + 0x4d, + 0x6c, + 0x33, + 0x5a, + 0x69, + 0x79, + 0x36, + 0x52, + 0x63, + 0x6c, + 0x5a, + 0x4f, + 0x4b, + 0x36, + 0x39, + 0x68, + 0x6b, + 0x4f, + 0x43, + 0x68, + 0x55, + 0x6b, + 0x46, + 0x30, + 0x46, + 0x38, + 0x6d, + 0x63, + 0x63, + 0x57, + 0x7a, + 0x52, + 0x58, + 0x59, + 0x4a, + 0x77, + 0x71, + 0x37, + 0x35, + 0x72, + 0x5a, + 0x43, + 0x41, + 0x55, + 0x47, + 0x0a, + 0x37, + 0x35, + 0x70, + 0x77, + 0x63, + 0x6a, + 0x2f, + 0x73, + 0x4a, + 0x33, + 0x73, + 0x34, + 0x56, + 0x45, + 0x43, + 0x69, + 0x35, + 0x4a, + 0x6b, + 0x44, + 0x39, + 0x6b, + 0x6f, + 0x2b, + 0x33, + 0x6b, + 0x63, + 0x79, + 0x36, + 0x4d, + 0x6c, + 0x39, + 0x71, + 0x67, + 0x43, + 0x4c, + 0x67, + 0x68, + 0x57, + 0x47, + 0x31, + 0x67, + 0x4f, + 0x67, + 0x31, + 0x6b, + 0x76, + 0x54, + 0x79, + 0x31, + 0x62, + 0x6f, + 0x4b, + 0x6b, + 0x72, + 0x79, + 0x33, + 0x6b, + 0x4b, + 0x73, + 0x39, + 0x62, + 0x61, + 0x64, + 0x0a, + 0x6e, + 0x32, + 0x61, + 0x59, + 0x51, + 0x58, + 0x59, + 0x33, + 0x79, + 0x4a, + 0x58, + 0x67, + 0x77, + 0x62, + 0x73, + 0x32, + 0x49, + 0x41, + 0x64, + 0x47, + 0x6e, + 0x50, + 0x63, + 0x67, + 0x78, + 0x39, + 0x5a, + 0x6e, + 0x6e, + 0x6b, + 0x44, + 0x67, + 0x6a, + 0x54, + 0x32, + 0x73, + 0x30, + 0x51, + 0x48, + 0x46, + 0x6c, + 0x73, + 0x4c, + 0x76, + 0x71, + 0x46, + 0x56, + 0x70, + 0x68, + 0x41, + 0x52, + 0x4a, + 0x44, + 0x77, + 0x79, + 0x4e, + 0x61, + 0x59, + 0x62, + 0x70, + 0x77, + 0x2f, + 0x64, + 0x39, + 0x0a, + 0x78, + 0x74, + 0x55, + 0x58, + 0x46, + 0x6e, + 0x5a, + 0x6b, + 0x46, + 0x4b, + 0x2b, + 0x35, + 0x42, + 0x33, + 0x6a, + 0x7a, + 0x44, + 0x38, + 0x4f, + 0x61, + 0x64, + 0x77, + 0x63, + 0x2f, + 0x71, + 0x6f, + 0x36, + 0x6d, + 0x68, + 0x2f, + 0x75, + 0x71, + 0x41, + 0x6b, + 0x34, + 0x57, + 0x51, + 0x71, + 0x50, + 0x6d, + 0x46, + 0x50, + 0x59, + 0x55, + 0x50, + 0x2b, + 0x50, + 0x68, + 0x38, + 0x44, + 0x44, + 0x63, + 0x76, + 0x45, + 0x4c, + 0x4d, + 0x6f, + 0x32, + 0x70, + 0x6e, + 0x2b, + 0x39, + 0x63, + 0x2b, + 0x0a, + 0x59, + 0x4f, + 0x41, + 0x6f, + 0x58, + 0x74, + 0x75, + 0x45, + 0x44, + 0x33, + 0x33, + 0x4a, + 0x39, + 0x4e, + 0x31, + 0x73, + 0x76, + 0x32, + 0x55, + 0x57, + 0x72, + 0x6b, + 0x6f, + 0x4c, + 0x45, + 0x4a, + 0x38, + 0x73, + 0x34, + 0x61, + 0x32, + 0x63, + 0x2f, + 0x56, + 0x69, + 0x65, + 0x66, + 0x76, + 0x4d, + 0x46, + 0x4b, + 0x6f, + 0x2b, + 0x4f, + 0x45, + 0x50, + 0x42, + 0x4f, + 0x71, + 0x57, + 0x45, + 0x53, + 0x39, + 0x49, + 0x65, + 0x4d, + 0x58, + 0x69, + 0x4c, + 0x42, + 0x44, + 0x72, + 0x2f, + 0x69, + 0x0a, + 0x4d, + 0x44, + 0x57, + 0x31, + 0x57, + 0x4a, + 0x76, + 0x46, + 0x74, + 0x57, + 0x78, + 0x32, + 0x57, + 0x33, + 0x56, + 0x74, + 0x55, + 0x6e, + 0x6a, + 0x4d, + 0x4b, + 0x62, + 0x52, + 0x47, + 0x2b, + 0x78, + 0x6a, + 0x34, + 0x46, + 0x47, + 0x6a, + 0x76, + 0x38, + 0x69, + 0x63, + 0x2f, + 0x34, + 0x53, + 0x71, + 0x6e, + 0x4b, + 0x36, + 0x53, + 0x43, + 0x65, + 0x2f, + 0x79, + 0x4b, + 0x47, + 0x67, + 0x45, + 0x79, + 0x76, + 0x6f, + 0x2f, + 0x61, + 0x66, + 0x4e, + 0x6d, + 0x39, + 0x30, + 0x49, + 0x74, + 0x76, + 0x0a, + 0x31, + 0x45, + 0x36, + 0x61, + 0x69, + 0x41, + 0x79, + 0x49, + 0x4d, + 0x73, + 0x6d, + 0x51, + 0x43, + 0x31, + 0x4a, + 0x4f, + 0x6f, + 0x56, + 0x68, + 0x2f, + 0x65, + 0x30, + 0x71, + 0x44, + 0x47, + 0x71, + 0x70, + 0x49, + 0x56, + 0x49, + 0x37, + 0x41, + 0x2f, + 0x4f, + 0x73, + 0x7a, + 0x39, + 0x74, + 0x53, + 0x68, + 0x78, + 0x44, + 0x65, + 0x52, + 0x75, + 0x5a, + 0x46, + 0x39, + 0x2b, + 0x67, + 0x4d, + 0x4d, + 0x6e, + 0x30, + 0x68, + 0x41, + 0x4a, + 0x6b, + 0x62, + 0x35, + 0x52, + 0x33, + 0x4d, + 0x78, + 0x0a, + 0x6f, + 0x38, + 0x58, + 0x36, + 0x6b, + 0x44, + 0x77, + 0x61, + 0x73, + 0x7a, + 0x48, + 0x48, + 0x76, + 0x52, + 0x55, + 0x73, + 0x66, + 0x54, + 0x66, + 0x6b, + 0x66, + 0x57, + 0x38, + 0x50, + 0x31, + 0x51, + 0x62, + 0x48, + 0x77, + 0x6e, + 0x4d, + 0x78, + 0x65, + 0x53, + 0x79, + 0x70, + 0x75, + 0x30, + 0x61, + 0x49, + 0x78, + 0x77, + 0x2b, + 0x63, + 0x38, + 0x44, + 0x6d, + 0x7a, + 0x74, + 0x2b, + 0x6c, + 0x73, + 0x37, + 0x62, + 0x6a, + 0x32, + 0x6d, + 0x32, + 0x36, + 0x48, + 0x38, + 0x39, + 0x4a, + 0x68, + 0x0a, + 0x59, + 0x6c, + 0x63, + 0x72, + 0x68, + 0x64, + 0x33, + 0x44, + 0x6d, + 0x2f, + 0x37, + 0x61, + 0x68, + 0x6c, + 0x56, + 0x66, + 0x64, + 0x35, + 0x79, + 0x4b, + 0x77, + 0x41, + 0x57, + 0x38, + 0x76, + 0x77, + 0x32, + 0x77, + 0x57, + 0x79, + 0x45, + 0x43, + 0x56, + 0x79, + 0x37, + 0x4c, + 0x4c, + 0x5a, + 0x36, + 0x63, + 0x4b, + 0x5a, + 0x57, + 0x57, + 0x32, + 0x6d, + 0x5a, + 0x47, + 0x4b, + 0x6a, + 0x37, + 0x7a, + 0x75, + 0x78, + 0x6b, + 0x56, + 0x65, + 0x39, + 0x42, + 0x2f, + 0x4d, + 0x44, + 0x54, + 0x75, + 0x0a, + 0x54, + 0x6e, + 0x39, + 0x7a, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4d, + 0x4c, + 0x70, + 0x6f, + 0x2b, + 0x53, + 0x58, + 0x43, + 0x46, + 0x2b, + 0x35, + 0x73, + 0x4d, + 0x35, + 0x68, + 0x57, + 0x62, + 0x4a, + 0x6d, + 0x38, + 0x77, + 0x52, + 0x64, + 0x4e, + 0x36, + 0x45, + 0x63, + 0x0a, + 0x4d, + 0x48, + 0x6a, + 0x54, + 0x33, + 0x74, + 0x61, + 0x2b, + 0x7a, + 0x79, + 0x74, + 0x6d, + 0x33, + 0x6f, + 0x79, + 0x71, + 0x59, + 0x63, + 0x53, + 0x2f, + 0x62, + 0x53, + 0x43, + 0x56, + 0x74, + 0x30, + 0x6a, + 0x38, + 0x65, + 0x71, + 0x72, + 0x5a, + 0x54, + 0x33, + 0x73, + 0x63, + 0x43, + 0x77, + 0x77, + 0x69, + 0x77, + 0x53, + 0x79, + 0x32, + 0x73, + 0x37, + 0x5a, + 0x63, + 0x65, + 0x74, + 0x35, + 0x75, + 0x6d, + 0x6b, + 0x46, + 0x55, + 0x48, + 0x62, + 0x36, + 0x4a, + 0x65, + 0x6e, + 0x30, + 0x51, + 0x0a, + 0x6c, + 0x58, + 0x4b, + 0x42, + 0x6f, + 0x61, + 0x53, + 0x47, + 0x54, + 0x57, + 0x42, + 0x4f, + 0x5a, + 0x62, + 0x6b, + 0x69, + 0x55, + 0x36, + 0x4f, + 0x36, + 0x6d, + 0x36, + 0x52, + 0x4c, + 0x4d, + 0x72, + 0x4d, + 0x6d, + 0x62, + 0x70, + 0x32, + 0x6b, + 0x68, + 0x4e, + 0x61, + 0x67, + 0x36, + 0x59, + 0x71, + 0x39, + 0x70, + 0x33, + 0x73, + 0x66, + 0x73, + 0x59, + 0x7a, + 0x38, + 0x69, + 0x45, + 0x31, + 0x38, + 0x7a, + 0x36, + 0x43, + 0x31, + 0x76, + 0x70, + 0x75, + 0x4b, + 0x63, + 0x39, + 0x51, + 0x54, + 0x0a, + 0x50, + 0x51, + 0x4c, + 0x51, + 0x65, + 0x7a, + 0x6c, + 0x68, + 0x77, + 0x5a, + 0x5a, + 0x57, + 0x52, + 0x4e, + 0x77, + 0x6b, + 0x43, + 0x79, + 0x4e, + 0x52, + 0x42, + 0x6a, + 0x69, + 0x39, + 0x4e, + 0x37, + 0x55, + 0x2f, + 0x75, + 0x4f, + 0x52, + 0x4f, + 0x49, + 0x38, + 0x64, + 0x6b, + 0x36, + 0x4b, + 0x48, + 0x6e, + 0x4d, + 0x5a, + 0x52, + 0x32, + 0x69, + 0x52, + 0x34, + 0x65, + 0x43, + 0x53, + 0x42, + 0x32, + 0x67, + 0x74, + 0x6b, + 0x76, + 0x68, + 0x50, + 0x65, + 0x4c, + 0x55, + 0x58, + 0x77, + 0x58, + 0x0a, + 0x38, + 0x4a, + 0x79, + 0x6d, + 0x32, + 0x51, + 0x48, + 0x58, + 0x35, + 0x43, + 0x59, + 0x37, + 0x2f, + 0x59, + 0x4d, + 0x66, + 0x73, + 0x34, + 0x44, + 0x30, + 0x49, + 0x38, + 0x66, + 0x70, + 0x6a, + 0x53, + 0x4f, + 0x65, + 0x65, + 0x52, + 0x59, + 0x33, + 0x63, + 0x6e, + 0x4a, + 0x76, + 0x33, + 0x37, + 0x4c, + 0x75, + 0x51, + 0x4c, + 0x35, + 0x72, + 0x4a, + 0x70, + 0x75, + 0x76, + 0x49, + 0x5a, + 0x78, + 0x46, + 0x4a, + 0x33, + 0x6e, + 0x56, + 0x72, + 0x37, + 0x56, + 0x2b, + 0x44, + 0x54, + 0x4d, + 0x72, + 0x0a, + 0x4e, + 0x71, + 0x4e, + 0x45, + 0x34, + 0x69, + 0x57, + 0x48, + 0x4d, + 0x56, + 0x58, + 0x71, + 0x63, + 0x45, + 0x62, + 0x4d, + 0x53, + 0x2f, + 0x59, + 0x4a, + 0x6a, + 0x38, + 0x34, + 0x69, + 0x63, + 0x47, + 0x65, + 0x31, + 0x70, + 0x43, + 0x55, + 0x30, + 0x31, + 0x65, + 0x49, + 0x2f, + 0x61, + 0x33, + 0x34, + 0x72, + 0x79, + 0x53, + 0x67, + 0x55, + 0x75, + 0x2b, + 0x33, + 0x53, + 0x43, + 0x55, + 0x55, + 0x61, + 0x58, + 0x5a, + 0x73, + 0x2f, + 0x2b, + 0x51, + 0x41, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char server_key[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x4a, + 0x4b, + 0x41, + 0x49, + 0x42, + 0x41, + 0x41, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x79, + 0x31, + 0x65, + 0x71, + 0x47, + 0x4a, + 0x69, + 0x45, + 0x61, + 0x4e, + 0x55, + 0x76, + 0x42, + 0x57, + 0x35, + 0x72, + 0x4b, + 0x38, + 0x77, + 0x77, + 0x69, + 0x75, + 0x4d, + 0x72, + 0x74, + 0x4c, + 0x67, + 0x74, + 0x74, + 0x45, + 0x65, + 0x55, + 0x31, + 0x51, + 0x4f, + 0x30, + 0x6e, + 0x4d, + 0x57, + 0x6a, + 0x59, + 0x43, + 0x4f, + 0x34, + 0x75, + 0x36, + 0x61, + 0x31, + 0x0a, + 0x34, + 0x58, + 0x34, + 0x6f, + 0x75, + 0x7a, + 0x68, + 0x57, + 0x4e, + 0x6a, + 0x50, + 0x47, + 0x35, + 0x31, + 0x6f, + 0x4a, + 0x43, + 0x76, + 0x4b, + 0x38, + 0x78, + 0x71, + 0x58, + 0x52, + 0x33, + 0x2f, + 0x7a, + 0x62, + 0x79, + 0x36, + 0x66, + 0x66, + 0x48, + 0x69, + 0x42, + 0x61, + 0x4e, + 0x68, + 0x4d, + 0x34, + 0x4c, + 0x6e, + 0x6c, + 0x78, + 0x44, + 0x30, + 0x77, + 0x4d, + 0x31, + 0x33, + 0x36, + 0x6d, + 0x59, + 0x77, + 0x51, + 0x54, + 0x6a, + 0x35, + 0x33, + 0x57, + 0x36, + 0x64, + 0x4e, + 0x2b, + 0x0a, + 0x6a, + 0x33, + 0x7a, + 0x7a, + 0x61, + 0x4d, + 0x7a, + 0x78, + 0x36, + 0x4f, + 0x55, + 0x30, + 0x56, + 0x4e, + 0x77, + 0x4f, + 0x68, + 0x4d, + 0x2b, + 0x47, + 0x67, + 0x33, + 0x76, + 0x43, + 0x70, + 0x2f, + 0x45, + 0x37, + 0x58, + 0x6b, + 0x57, + 0x65, + 0x4a, + 0x50, + 0x42, + 0x4d, + 0x4a, + 0x62, + 0x33, + 0x52, + 0x75, + 0x62, + 0x50, + 0x76, + 0x61, + 0x33, + 0x72, + 0x4d, + 0x73, + 0x36, + 0x62, + 0x63, + 0x4b, + 0x6c, + 0x6d, + 0x36, + 0x35, + 0x30, + 0x64, + 0x4b, + 0x64, + 0x63, + 0x65, + 0x71, + 0x0a, + 0x32, + 0x41, + 0x37, + 0x50, + 0x71, + 0x64, + 0x4a, + 0x4a, + 0x74, + 0x4f, + 0x4d, + 0x37, + 0x56, + 0x42, + 0x48, + 0x63, + 0x73, + 0x33, + 0x34, + 0x4f, + 0x38, + 0x33, + 0x54, + 0x68, + 0x73, + 0x78, + 0x63, + 0x46, + 0x39, + 0x37, + 0x2f, + 0x51, + 0x31, + 0x55, + 0x54, + 0x39, + 0x43, + 0x6c, + 0x34, + 0x34, + 0x34, + 0x59, + 0x39, + 0x6c, + 0x49, + 0x59, + 0x49, + 0x36, + 0x30, + 0x71, + 0x38, + 0x4e, + 0x6e, + 0x67, + 0x36, + 0x37, + 0x49, + 0x71, + 0x47, + 0x62, + 0x5a, + 0x47, + 0x69, + 0x73, + 0x0a, + 0x59, + 0x6d, + 0x63, + 0x75, + 0x5a, + 0x66, + 0x45, + 0x4d, + 0x55, + 0x65, + 0x59, + 0x45, + 0x79, + 0x6e, + 0x54, + 0x52, + 0x30, + 0x42, + 0x78, + 0x36, + 0x73, + 0x45, + 0x52, + 0x45, + 0x73, + 0x4f, + 0x51, + 0x74, + 0x47, + 0x6c, + 0x31, + 0x62, + 0x6a, + 0x77, + 0x72, + 0x61, + 0x4c, + 0x6d, + 0x30, + 0x46, + 0x43, + 0x38, + 0x42, + 0x77, + 0x6c, + 0x6e, + 0x4f, + 0x55, + 0x76, + 0x78, + 0x44, + 0x34, + 0x66, + 0x5a, + 0x2b, + 0x43, + 0x35, + 0x4a, + 0x73, + 0x48, + 0x62, + 0x5a, + 0x35, + 0x54, + 0x0a, + 0x4b, + 0x30, + 0x46, + 0x74, + 0x73, + 0x32, + 0x65, + 0x43, + 0x2f, + 0x71, + 0x48, + 0x4a, + 0x48, + 0x4c, + 0x52, + 0x73, + 0x41, + 0x6a, + 0x4e, + 0x70, + 0x67, + 0x66, + 0x42, + 0x71, + 0x6c, + 0x45, + 0x6a, + 0x47, + 0x73, + 0x74, + 0x4e, + 0x61, + 0x64, + 0x30, + 0x79, + 0x49, + 0x48, + 0x76, + 0x49, + 0x36, + 0x39, + 0x32, + 0x4b, + 0x79, + 0x56, + 0x54, + 0x37, + 0x56, + 0x4b, + 0x67, + 0x56, + 0x59, + 0x42, + 0x74, + 0x39, + 0x58, + 0x6e, + 0x4b, + 0x36, + 0x48, + 0x41, + 0x36, + 0x66, + 0x42, + 0x0a, + 0x4d, + 0x78, + 0x4b, + 0x68, + 0x31, + 0x48, + 0x54, + 0x30, + 0x37, + 0x47, + 0x42, + 0x52, + 0x71, + 0x34, + 0x44, + 0x62, + 0x4b, + 0x73, + 0x41, + 0x76, + 0x5a, + 0x44, + 0x73, + 0x45, + 0x7a, + 0x6b, + 0x61, + 0x64, + 0x67, + 0x75, + 0x6c, + 0x53, + 0x6a, + 0x6d, + 0x72, + 0x6b, + 0x57, + 0x41, + 0x44, + 0x43, + 0x53, + 0x77, + 0x6d, + 0x58, + 0x7a, + 0x44, + 0x4a, + 0x49, + 0x4f, + 0x6c, + 0x68, + 0x78, + 0x43, + 0x76, + 0x73, + 0x57, + 0x76, + 0x4e, + 0x4b, + 0x4f, + 0x70, + 0x7a, + 0x57, + 0x6a, + 0x0a, + 0x31, + 0x53, + 0x7a, + 0x75, + 0x45, + 0x51, + 0x53, + 0x57, + 0x53, + 0x38, + 0x30, + 0x46, + 0x6d, + 0x54, + 0x58, + 0x35, + 0x55, + 0x39, + 0x4e, + 0x6f, + 0x2f, + 0x6e, + 0x45, + 0x52, + 0x6a, + 0x4b, + 0x6c, + 0x78, + 0x75, + 0x44, + 0x36, + 0x37, + 0x35, + 0x67, + 0x4c, + 0x4e, + 0x79, + 0x30, + 0x6c, + 0x61, + 0x63, + 0x6b, + 0x70, + 0x73, + 0x49, + 0x48, + 0x50, + 0x5a, + 0x36, + 0x7a, + 0x6f, + 0x71, + 0x53, + 0x6c, + 0x38, + 0x4c, + 0x68, + 0x70, + 0x5a, + 0x7a, + 0x78, + 0x51, + 0x67, + 0x75, + 0x0a, + 0x56, + 0x51, + 0x72, + 0x39, + 0x62, + 0x69, + 0x4e, + 0x45, + 0x57, + 0x77, + 0x30, + 0x62, + 0x64, + 0x53, + 0x45, + 0x44, + 0x57, + 0x2b, + 0x63, + 0x68, + 0x6e, + 0x41, + 0x72, + 0x7a, + 0x71, + 0x4b, + 0x71, + 0x62, + 0x65, + 0x54, + 0x34, + 0x38, + 0x52, + 0x74, + 0x71, + 0x46, + 0x77, + 0x54, + 0x45, + 0x44, + 0x69, + 0x6f, + 0x48, + 0x38, + 0x73, + 0x47, + 0x47, + 0x69, + 0x43, + 0x2b, + 0x7a, + 0x6a, + 0x50, + 0x31, + 0x76, + 0x59, + 0x78, + 0x44, + 0x66, + 0x6d, + 0x32, + 0x49, + 0x69, + 0x68, + 0x0a, + 0x41, + 0x4f, + 0x50, + 0x74, + 0x48, + 0x6e, + 0x45, + 0x33, + 0x78, + 0x7a, + 0x6b, + 0x44, + 0x4c, + 0x41, + 0x50, + 0x4a, + 0x76, + 0x7a, + 0x45, + 0x61, + 0x2f, + 0x5a, + 0x69, + 0x61, + 0x54, + 0x69, + 0x4d, + 0x61, + 0x30, + 0x4f, + 0x52, + 0x32, + 0x71, + 0x74, + 0x75, + 0x30, + 0x68, + 0x54, + 0x6a, + 0x62, + 0x34, + 0x63, + 0x7a, + 0x58, + 0x49, + 0x36, + 0x54, + 0x79, + 0x37, + 0x78, + 0x6b, + 0x53, + 0x4c, + 0x45, + 0x74, + 0x44, + 0x4f, + 0x6a, + 0x43, + 0x61, + 0x70, + 0x53, + 0x71, + 0x74, + 0x0a, + 0x34, + 0x45, + 0x4b, + 0x47, + 0x67, + 0x43, + 0x69, + 0x78, + 0x7a, + 0x63, + 0x55, + 0x35, + 0x45, + 0x36, + 0x5a, + 0x4d, + 0x49, + 0x4f, + 0x56, + 0x6a, + 0x72, + 0x66, + 0x6a, + 0x39, + 0x70, + 0x77, + 0x67, + 0x67, + 0x2f, + 0x66, + 0x56, + 0x31, + 0x35, + 0x30, + 0x63, + 0x31, + 0x68, + 0x6e, + 0x6e, + 0x56, + 0x47, + 0x75, + 0x36, + 0x51, + 0x42, + 0x30, + 0x44, + 0x72, + 0x59, + 0x4e, + 0x73, + 0x59, + 0x50, + 0x43, + 0x44, + 0x44, + 0x50, + 0x70, + 0x4d, + 0x43, + 0x41, + 0x77, + 0x45, + 0x41, + 0x0a, + 0x41, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x74, + 0x69, + 0x33, + 0x49, + 0x65, + 0x31, + 0x6a, + 0x4a, + 0x36, + 0x4a, + 0x4a, + 0x33, + 0x47, + 0x6b, + 0x71, + 0x66, + 0x51, + 0x68, + 0x49, + 0x69, + 0x75, + 0x34, + 0x78, + 0x6b, + 0x4c, + 0x2f, + 0x6d, + 0x44, + 0x47, + 0x53, + 0x32, + 0x34, + 0x72, + 0x6c, + 0x41, + 0x76, + 0x6c, + 0x50, + 0x57, + 0x4e, + 0x4e, + 0x69, + 0x6c, + 0x4b, + 0x37, + 0x50, + 0x53, + 0x6a, + 0x70, + 0x32, + 0x38, + 0x38, + 0x56, + 0x75, + 0x42, + 0x57, + 0x0a, + 0x66, + 0x53, + 0x46, + 0x4f, + 0x5a, + 0x79, + 0x42, + 0x48, + 0x55, + 0x63, + 0x50, + 0x41, + 0x56, + 0x63, + 0x6c, + 0x69, + 0x69, + 0x32, + 0x63, + 0x6b, + 0x46, + 0x51, + 0x5a, + 0x46, + 0x34, + 0x39, + 0x76, + 0x67, + 0x64, + 0x75, + 0x6f, + 0x70, + 0x50, + 0x35, + 0x6f, + 0x57, + 0x4c, + 0x54, + 0x70, + 0x5a, + 0x6c, + 0x77, + 0x67, + 0x6c, + 0x55, + 0x6a, + 0x6a, + 0x6f, + 0x76, + 0x5a, + 0x63, + 0x51, + 0x59, + 0x56, + 0x58, + 0x57, + 0x34, + 0x66, + 0x39, + 0x30, + 0x70, + 0x4d, + 0x52, + 0x5a, + 0x0a, + 0x52, + 0x2b, + 0x74, + 0x79, + 0x51, + 0x30, + 0x55, + 0x55, + 0x7a, + 0x50, + 0x43, + 0x51, + 0x39, + 0x32, + 0x50, + 0x2b, + 0x4e, + 0x6a, + 0x44, + 0x75, + 0x6d, + 0x37, + 0x75, + 0x4c, + 0x77, + 0x46, + 0x52, + 0x36, + 0x4a, + 0x4b, + 0x59, + 0x34, + 0x4c, + 0x45, + 0x53, + 0x46, + 0x6b, + 0x4b, + 0x6e, + 0x6f, + 0x6c, + 0x46, + 0x77, + 0x2b, + 0x6a, + 0x4d, + 0x4c, + 0x76, + 0x72, + 0x52, + 0x34, + 0x32, + 0x47, + 0x2b, + 0x66, + 0x54, + 0x72, + 0x4e, + 0x63, + 0x34, + 0x50, + 0x6c, + 0x48, + 0x6c, + 0x0a, + 0x4c, + 0x32, + 0x63, + 0x57, + 0x56, + 0x47, + 0x2f, + 0x63, + 0x61, + 0x41, + 0x50, + 0x63, + 0x71, + 0x55, + 0x70, + 0x69, + 0x6d, + 0x2b, + 0x31, + 0x59, + 0x72, + 0x32, + 0x72, + 0x76, + 0x55, + 0x75, + 0x51, + 0x49, + 0x51, + 0x53, + 0x33, + 0x42, + 0x6b, + 0x48, + 0x54, + 0x45, + 0x69, + 0x6f, + 0x7a, + 0x2b, + 0x33, + 0x70, + 0x74, + 0x76, + 0x65, + 0x39, + 0x68, + 0x2b, + 0x32, + 0x4c, + 0x77, + 0x70, + 0x57, + 0x49, + 0x42, + 0x66, + 0x55, + 0x5a, + 0x70, + 0x58, + 0x2b, + 0x34, + 0x56, + 0x48, + 0x0a, + 0x49, + 0x47, + 0x6b, + 0x51, + 0x69, + 0x63, + 0x4b, + 0x33, + 0x35, + 0x39, + 0x68, + 0x63, + 0x49, + 0x32, + 0x68, + 0x47, + 0x6b, + 0x71, + 0x55, + 0x62, + 0x73, + 0x36, + 0x39, + 0x49, + 0x78, + 0x4a, + 0x4e, + 0x59, + 0x66, + 0x70, + 0x65, + 0x32, + 0x47, + 0x5a, + 0x31, + 0x45, + 0x51, + 0x63, + 0x63, + 0x7a, + 0x30, + 0x53, + 0x73, + 0x48, + 0x4e, + 0x71, + 0x57, + 0x65, + 0x77, + 0x2b, + 0x6f, + 0x52, + 0x63, + 0x61, + 0x69, + 0x54, + 0x6f, + 0x77, + 0x46, + 0x76, + 0x6f, + 0x33, + 0x68, + 0x74, + 0x0a, + 0x36, + 0x47, + 0x78, + 0x53, + 0x6e, + 0x71, + 0x45, + 0x57, + 0x30, + 0x37, + 0x48, + 0x30, + 0x4b, + 0x54, + 0x4b, + 0x59, + 0x69, + 0x6c, + 0x6e, + 0x5a, + 0x69, + 0x34, + 0x58, + 0x49, + 0x57, + 0x33, + 0x48, + 0x35, + 0x30, + 0x51, + 0x61, + 0x75, + 0x61, + 0x68, + 0x62, + 0x77, + 0x48, + 0x30, + 0x75, + 0x56, + 0x73, + 0x64, + 0x32, + 0x55, + 0x39, + 0x6f, + 0x34, + 0x6c, + 0x37, + 0x54, + 0x38, + 0x5a, + 0x4d, + 0x65, + 0x34, + 0x52, + 0x50, + 0x6d, + 0x66, + 0x39, + 0x77, + 0x46, + 0x6e, + 0x2f, + 0x0a, + 0x45, + 0x7a, + 0x34, + 0x46, + 0x74, + 0x68, + 0x62, + 0x6e, + 0x51, + 0x4f, + 0x57, + 0x42, + 0x6f, + 0x31, + 0x44, + 0x78, + 0x61, + 0x42, + 0x6d, + 0x78, + 0x39, + 0x42, + 0x66, + 0x6e, + 0x35, + 0x6e, + 0x43, + 0x35, + 0x49, + 0x36, + 0x52, + 0x79, + 0x72, + 0x36, + 0x58, + 0x4d, + 0x6e, + 0x6e, + 0x4a, + 0x4a, + 0x2b, + 0x65, + 0x41, + 0x74, + 0x53, + 0x6a, + 0x58, + 0x53, + 0x41, + 0x56, + 0x37, + 0x2f, + 0x36, + 0x33, + 0x43, + 0x72, + 0x70, + 0x63, + 0x42, + 0x2b, + 0x73, + 0x42, + 0x74, + 0x46, + 0x0a, + 0x69, + 0x7a, + 0x46, + 0x33, + 0x79, + 0x45, + 0x51, + 0x38, + 0x72, + 0x74, + 0x57, + 0x41, + 0x43, + 0x71, + 0x78, + 0x6a, + 0x4d, + 0x55, + 0x50, + 0x43, + 0x33, + 0x4a, + 0x71, + 0x39, + 0x6c, + 0x39, + 0x33, + 0x39, + 0x38, + 0x2f, + 0x6a, + 0x7a, + 0x43, + 0x70, + 0x5a, + 0x5a, + 0x4c, + 0x76, + 0x7a, + 0x34, + 0x4e, + 0x55, + 0x6a, + 0x6a, + 0x74, + 0x66, + 0x4e, + 0x68, + 0x33, + 0x69, + 0x36, + 0x44, + 0x50, + 0x36, + 0x49, + 0x68, + 0x41, + 0x4b, + 0x31, + 0x33, + 0x54, + 0x56, + 0x37, + 0x49, + 0x0a, + 0x34, + 0x47, + 0x34, + 0x30, + 0x62, + 0x72, + 0x5a, + 0x68, + 0x6d, + 0x58, + 0x53, + 0x56, + 0x4b, + 0x42, + 0x6e, + 0x78, + 0x47, + 0x37, + 0x44, + 0x35, + 0x69, + 0x59, + 0x36, + 0x50, + 0x62, + 0x67, + 0x56, + 0x57, + 0x48, + 0x46, + 0x44, + 0x45, + 0x6a, + 0x50, + 0x44, + 0x77, + 0x6f, + 0x4a, + 0x6b, + 0x74, + 0x4b, + 0x31, + 0x42, + 0x50, + 0x61, + 0x64, + 0x33, + 0x71, + 0x57, + 0x66, + 0x39, + 0x34, + 0x66, + 0x6d, + 0x74, + 0x35, + 0x79, + 0x6b, + 0x5a, + 0x64, + 0x2f, + 0x74, + 0x65, + 0x71, + 0x0a, + 0x65, + 0x52, + 0x49, + 0x45, + 0x76, + 0x55, + 0x7a, + 0x4a, + 0x47, + 0x35, + 0x50, + 0x46, + 0x47, + 0x77, + 0x4f, + 0x7a, + 0x78, + 0x56, + 0x52, + 0x48, + 0x44, + 0x4e, + 0x38, + 0x51, + 0x51, + 0x6d, + 0x4e, + 0x4a, + 0x63, + 0x43, + 0x4a, + 0x75, + 0x79, + 0x74, + 0x77, + 0x34, + 0x44, + 0x6e, + 0x73, + 0x78, + 0x6f, + 0x43, + 0x38, + 0x33, + 0x69, + 0x7a, + 0x66, + 0x68, + 0x2b, + 0x6e, + 0x45, + 0x71, + 0x45, + 0x50, + 0x50, + 0x51, + 0x69, + 0x44, + 0x46, + 0x6f, + 0x54, + 0x4c, + 0x79, + 0x44, + 0x0a, + 0x2b, + 0x34, + 0x56, + 0x52, + 0x67, + 0x46, + 0x42, + 0x62, + 0x36, + 0x61, + 0x69, + 0x72, + 0x56, + 0x63, + 0x79, + 0x58, + 0x57, + 0x64, + 0x72, + 0x32, + 0x30, + 0x43, + 0x64, + 0x4f, + 0x47, + 0x67, + 0x58, + 0x6f, + 0x42, + 0x4d, + 0x72, + 0x4e, + 0x42, + 0x6c, + 0x2b, + 0x45, + 0x36, + 0x32, + 0x4d, + 0x59, + 0x41, + 0x65, + 0x32, + 0x71, + 0x63, + 0x36, + 0x37, + 0x63, + 0x51, + 0x59, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4f, + 0x39, + 0x32, + 0x58, + 0x4e, + 0x41, + 0x46, + 0x0a, + 0x52, + 0x41, + 0x77, + 0x5a, + 0x79, + 0x64, + 0x7a, + 0x59, + 0x63, + 0x68, + 0x35, + 0x45, + 0x35, + 0x65, + 0x6a, + 0x7a, + 0x39, + 0x54, + 0x5a, + 0x62, + 0x48, + 0x66, + 0x65, + 0x66, + 0x34, + 0x7a, + 0x4e, + 0x42, + 0x48, + 0x55, + 0x2f, + 0x6d, + 0x56, + 0x31, + 0x76, + 0x68, + 0x38, + 0x39, + 0x64, + 0x39, + 0x4b, + 0x6a, + 0x53, + 0x46, + 0x2f, + 0x4f, + 0x69, + 0x4c, + 0x33, + 0x45, + 0x6e, + 0x6f, + 0x59, + 0x78, + 0x50, + 0x6b, + 0x48, + 0x67, + 0x55, + 0x58, + 0x79, + 0x6e, + 0x39, + 0x41, + 0x0a, + 0x34, + 0x43, + 0x34, + 0x75, + 0x31, + 0x72, + 0x6c, + 0x64, + 0x5a, + 0x36, + 0x64, + 0x77, + 0x43, + 0x56, + 0x41, + 0x62, + 0x5a, + 0x4a, + 0x6e, + 0x54, + 0x38, + 0x55, + 0x46, + 0x32, + 0x6e, + 0x4c, + 0x52, + 0x46, + 0x4d, + 0x58, + 0x68, + 0x61, + 0x71, + 0x79, + 0x4a, + 0x45, + 0x6d, + 0x54, + 0x37, + 0x6a, + 0x43, + 0x59, + 0x52, + 0x79, + 0x37, + 0x65, + 0x72, + 0x64, + 0x37, + 0x56, + 0x4c, + 0x39, + 0x35, + 0x67, + 0x56, + 0x68, + 0x55, + 0x4f, + 0x73, + 0x4a, + 0x59, + 0x77, + 0x4c, + 0x6d, + 0x0a, + 0x74, + 0x59, + 0x66, + 0x63, + 0x69, + 0x6b, + 0x30, + 0x37, + 0x51, + 0x6d, + 0x44, + 0x53, + 0x50, + 0x56, + 0x75, + 0x47, + 0x68, + 0x56, + 0x32, + 0x4b, + 0x65, + 0x6b, + 0x6e, + 0x7a, + 0x54, + 0x61, + 0x31, + 0x4e, + 0x53, + 0x54, + 0x55, + 0x38, + 0x76, + 0x77, + 0x51, + 0x46, + 0x2b, + 0x77, + 0x4b, + 0x48, + 0x69, + 0x38, + 0x36, + 0x34, + 0x64, + 0x6d, + 0x7a, + 0x6b, + 0x73, + 0x6c, + 0x59, + 0x6e, + 0x51, + 0x71, + 0x62, + 0x46, + 0x6f, + 0x54, + 0x56, + 0x52, + 0x5a, + 0x74, + 0x4e, + 0x65, + 0x0a, + 0x72, + 0x4c, + 0x62, + 0x36, + 0x67, + 0x36, + 0x6e, + 0x63, + 0x77, + 0x36, + 0x4a, + 0x57, + 0x78, + 0x39, + 0x79, + 0x65, + 0x42, + 0x30, + 0x37, + 0x56, + 0x64, + 0x70, + 0x4a, + 0x70, + 0x36, + 0x78, + 0x39, + 0x54, + 0x59, + 0x54, + 0x2b, + 0x48, + 0x47, + 0x6a, + 0x33, + 0x6b, + 0x62, + 0x35, + 0x61, + 0x63, + 0x75, + 0x49, + 0x6e, + 0x50, + 0x2b, + 0x6a, + 0x67, + 0x37, + 0x47, + 0x5a, + 0x7a, + 0x6b, + 0x72, + 0x48, + 0x4a, + 0x4d, + 0x6e, + 0x48, + 0x4c, + 0x77, + 0x65, + 0x46, + 0x76, + 0x62, + 0x0a, + 0x4e, + 0x57, + 0x5a, + 0x66, + 0x6c, + 0x50, + 0x66, + 0x69, + 0x41, + 0x4e, + 0x6d, + 0x77, + 0x4d, + 0x38, + 0x77, + 0x4d, + 0x52, + 0x2b, + 0x6b, + 0x66, + 0x50, + 0x32, + 0x76, + 0x6f, + 0x67, + 0x47, + 0x61, + 0x6d, + 0x4b, + 0x43, + 0x69, + 0x65, + 0x6f, + 0x6a, + 0x56, + 0x61, + 0x54, + 0x69, + 0x38, + 0x76, + 0x67, + 0x71, + 0x71, + 0x6a, + 0x78, + 0x34, + 0x55, + 0x47, + 0x50, + 0x64, + 0x35, + 0x39, + 0x36, + 0x46, + 0x4e, + 0x46, + 0x65, + 0x69, + 0x5a, + 0x52, + 0x76, + 0x58, + 0x4e, + 0x6b, + 0x0a, + 0x30, + 0x64, + 0x42, + 0x30, + 0x41, + 0x4b, + 0x32, + 0x51, + 0x2f, + 0x31, + 0x32, + 0x36, + 0x53, + 0x6c, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4e, + 0x6c, + 0x69, + 0x74, + 0x64, + 0x78, + 0x48, + 0x74, + 0x74, + 0x78, + 0x68, + 0x2f, + 0x57, + 0x71, + 0x77, + 0x46, + 0x4b, + 0x33, + 0x35, + 0x62, + 0x63, + 0x4f, + 0x54, + 0x58, + 0x61, + 0x68, + 0x36, + 0x50, + 0x61, + 0x70, + 0x50, + 0x64, + 0x67, + 0x2f, + 0x37, + 0x32, + 0x50, + 0x75, + 0x4f, + 0x6c, + 0x33, + 0x57, + 0x74, + 0x0a, + 0x37, + 0x57, + 0x6e, + 0x38, + 0x41, + 0x74, + 0x36, + 0x46, + 0x49, + 0x47, + 0x51, + 0x33, + 0x58, + 0x5a, + 0x4f, + 0x4f, + 0x50, + 0x66, + 0x39, + 0x76, + 0x35, + 0x48, + 0x66, + 0x71, + 0x50, + 0x70, + 0x53, + 0x6c, + 0x4e, + 0x56, + 0x39, + 0x33, + 0x41, + 0x58, + 0x58, + 0x61, + 0x4f, + 0x36, + 0x49, + 0x54, + 0x7a, + 0x77, + 0x49, + 0x45, + 0x62, + 0x38, + 0x49, + 0x47, + 0x33, + 0x69, + 0x45, + 0x4f, + 0x74, + 0x54, + 0x4c, + 0x77, + 0x32, + 0x39, + 0x32, + 0x4b, + 0x55, + 0x38, + 0x6c, + 0x4a, + 0x0a, + 0x4c, + 0x6c, + 0x38, + 0x58, + 0x70, + 0x68, + 0x4f, + 0x6b, + 0x56, + 0x55, + 0x72, + 0x50, + 0x6b, + 0x7a, + 0x47, + 0x47, + 0x36, + 0x64, + 0x66, + 0x56, + 0x4e, + 0x70, + 0x36, + 0x49, + 0x32, + 0x68, + 0x54, + 0x70, + 0x59, + 0x4a, + 0x49, + 0x53, + 0x77, + 0x41, + 0x55, + 0x35, + 0x48, + 0x76, + 0x4b, + 0x70, + 0x63, + 0x39, + 0x6f, + 0x42, + 0x30, + 0x59, + 0x4b, + 0x68, + 0x56, + 0x4c, + 0x66, + 0x43, + 0x2b, + 0x69, + 0x64, + 0x61, + 0x6b, + 0x56, + 0x43, + 0x35, + 0x66, + 0x5a, + 0x7a, + 0x36, + 0x0a, + 0x7a, + 0x46, + 0x41, + 0x6f, + 0x32, + 0x37, + 0x46, + 0x65, + 0x6f, + 0x49, + 0x45, + 0x64, + 0x48, + 0x74, + 0x39, + 0x32, + 0x5a, + 0x65, + 0x36, + 0x51, + 0x33, + 0x34, + 0x4e, + 0x4a, + 0x67, + 0x70, + 0x56, + 0x62, + 0x67, + 0x6d, + 0x54, + 0x30, + 0x2b, + 0x77, + 0x74, + 0x75, + 0x75, + 0x72, + 0x71, + 0x6f, + 0x5a, + 0x2f, + 0x7a, + 0x52, + 0x74, + 0x6b, + 0x78, + 0x34, + 0x35, + 0x77, + 0x75, + 0x71, + 0x49, + 0x33, + 0x45, + 0x2b, + 0x54, + 0x44, + 0x61, + 0x31, + 0x53, + 0x75, + 0x71, + 0x33, + 0x0a, + 0x34, + 0x4f, + 0x51, + 0x73, + 0x65, + 0x32, + 0x57, + 0x63, + 0x67, + 0x6a, + 0x7a, + 0x53, + 0x66, + 0x64, + 0x4c, + 0x4a, + 0x74, + 0x54, + 0x66, + 0x79, + 0x65, + 0x4c, + 0x31, + 0x70, + 0x34, + 0x56, + 0x68, + 0x47, + 0x79, + 0x63, + 0x78, + 0x67, + 0x2b, + 0x4a, + 0x45, + 0x35, + 0x48, + 0x34, + 0x6a, + 0x69, + 0x33, + 0x7a, + 0x35, + 0x33, + 0x46, + 0x78, + 0x72, + 0x4d, + 0x6e, + 0x41, + 0x30, + 0x6d, + 0x53, + 0x45, + 0x6c, + 0x79, + 0x45, + 0x6a, + 0x69, + 0x38, + 0x38, + 0x78, + 0x54, + 0x47, + 0x0a, + 0x48, + 0x43, + 0x59, + 0x47, + 0x49, + 0x6e, + 0x4a, + 0x70, + 0x5a, + 0x72, + 0x2b, + 0x51, + 0x7a, + 0x67, + 0x50, + 0x54, + 0x7a, + 0x57, + 0x66, + 0x35, + 0x52, + 0x34, + 0x36, + 0x6b, + 0x30, + 0x38, + 0x47, + 0x4b, + 0x73, + 0x6d, + 0x56, + 0x67, + 0x6a, + 0x31, + 0x50, + 0x32, + 0x7a, + 0x39, + 0x4f, + 0x65, + 0x33, + 0x61, + 0x4d, + 0x43, + 0x67, + 0x67, + 0x45, + 0x41, + 0x48, + 0x42, + 0x6b, + 0x64, + 0x5a, + 0x75, + 0x4e, + 0x58, + 0x53, + 0x72, + 0x77, + 0x7a, + 0x37, + 0x5a, + 0x41, + 0x51, + 0x0a, + 0x51, + 0x2f, + 0x44, + 0x39, + 0x73, + 0x55, + 0x6e, + 0x2b, + 0x2b, + 0x66, + 0x50, + 0x54, + 0x48, + 0x6c, + 0x31, + 0x4b, + 0x67, + 0x5a, + 0x63, + 0x67, + 0x59, + 0x32, + 0x47, + 0x35, + 0x32, + 0x6e, + 0x51, + 0x32, + 0x38, + 0x70, + 0x41, + 0x6a, + 0x52, + 0x61, + 0x70, + 0x37, + 0x4e, + 0x4b, + 0x5a, + 0x45, + 0x6f, + 0x50, + 0x39, + 0x39, + 0x73, + 0x4c, + 0x58, + 0x52, + 0x74, + 0x2f, + 0x4e, + 0x45, + 0x74, + 0x59, + 0x33, + 0x64, + 0x51, + 0x45, + 0x34, + 0x4b, + 0x73, + 0x42, + 0x46, + 0x2f, + 0x0a, + 0x75, + 0x69, + 0x76, + 0x78, + 0x53, + 0x38, + 0x38, + 0x4c, + 0x44, + 0x4f, + 0x6e, + 0x4c, + 0x6f, + 0x30, + 0x7a, + 0x52, + 0x73, + 0x6d, + 0x31, + 0x30, + 0x45, + 0x70, + 0x56, + 0x42, + 0x41, + 0x33, + 0x4a, + 0x64, + 0x4d, + 0x50, + 0x33, + 0x65, + 0x2f, + 0x67, + 0x57, + 0x6d, + 0x57, + 0x53, + 0x72, + 0x56, + 0x55, + 0x43, + 0x6d, + 0x75, + 0x74, + 0x65, + 0x37, + 0x6e, + 0x57, + 0x63, + 0x7a, + 0x75, + 0x4b, + 0x30, + 0x62, + 0x37, + 0x41, + 0x67, + 0x67, + 0x6b, + 0x79, + 0x6b, + 0x38, + 0x72, + 0x0a, + 0x79, + 0x4d, + 0x53, + 0x69, + 0x6f, + 0x6e, + 0x79, + 0x30, + 0x5a, + 0x58, + 0x64, + 0x38, + 0x52, + 0x66, + 0x55, + 0x67, + 0x70, + 0x6a, + 0x63, + 0x74, + 0x59, + 0x65, + 0x76, + 0x51, + 0x31, + 0x68, + 0x34, + 0x46, + 0x69, + 0x42, + 0x52, + 0x7a, + 0x6d, + 0x54, + 0x77, + 0x58, + 0x32, + 0x55, + 0x73, + 0x30, + 0x69, + 0x64, + 0x74, + 0x74, + 0x66, + 0x4c, + 0x67, + 0x76, + 0x35, + 0x46, + 0x75, + 0x36, + 0x33, + 0x77, + 0x35, + 0x36, + 0x34, + 0x62, + 0x57, + 0x66, + 0x67, + 0x57, + 0x30, + 0x41, + 0x0a, + 0x48, + 0x36, + 0x70, + 0x6a, + 0x4a, + 0x55, + 0x58, + 0x39, + 0x77, + 0x59, + 0x68, + 0x57, + 0x73, + 0x33, + 0x4e, + 0x75, + 0x50, + 0x57, + 0x7a, + 0x42, + 0x4f, + 0x6e, + 0x30, + 0x56, + 0x33, + 0x54, + 0x46, + 0x53, + 0x7a, + 0x71, + 0x39, + 0x78, + 0x44, + 0x2b, + 0x71, + 0x78, + 0x68, + 0x36, + 0x75, + 0x58, + 0x6f, + 0x38, + 0x74, + 0x4a, + 0x57, + 0x4e, + 0x67, + 0x59, + 0x65, + 0x33, + 0x77, + 0x77, + 0x41, + 0x30, + 0x64, + 0x44, + 0x69, + 0x74, + 0x47, + 0x6f, + 0x58, + 0x7a, + 0x58, + 0x4c, + 0x0a, + 0x74, + 0x37, + 0x39, + 0x76, + 0x4e, + 0x49, + 0x41, + 0x30, + 0x5a, + 0x2b, + 0x78, + 0x63, + 0x75, + 0x73, + 0x4b, + 0x37, + 0x58, + 0x6c, + 0x41, + 0x63, + 0x34, + 0x6a, + 0x64, + 0x4b, + 0x2f, + 0x55, + 0x75, + 0x74, + 0x5a, + 0x4f, + 0x67, + 0x45, + 0x51, + 0x78, + 0x57, + 0x59, + 0x73, + 0x63, + 0x6b, + 0x37, + 0x74, + 0x55, + 0x47, + 0x38, + 0x34, + 0x78, + 0x69, + 0x42, + 0x37, + 0x73, + 0x45, + 0x6b, + 0x6f, + 0x38, + 0x65, + 0x75, + 0x76, + 0x76, + 0x44, + 0x39, + 0x71, + 0x32, + 0x41, + 0x61, + 0x0a, + 0x4e, + 0x74, + 0x74, + 0x48, + 0x49, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x42, + 0x44, + 0x77, + 0x30, + 0x5a, + 0x6e, + 0x56, + 0x52, + 0x2b, + 0x47, + 0x55, + 0x54, + 0x46, + 0x61, + 0x71, + 0x57, + 0x71, + 0x34, + 0x31, + 0x4a, + 0x44, + 0x48, + 0x72, + 0x30, + 0x38, + 0x35, + 0x72, + 0x6d, + 0x6b, + 0x38, + 0x68, + 0x71, + 0x71, + 0x4a, + 0x7a, + 0x76, + 0x72, + 0x54, + 0x5a, + 0x35, + 0x36, + 0x45, + 0x2b, + 0x32, + 0x4f, + 0x50, + 0x62, + 0x78, + 0x52, + 0x42, + 0x34, + 0x77, + 0x4f, + 0x32, + 0x0a, + 0x32, + 0x5a, + 0x39, + 0x63, + 0x32, + 0x4c, + 0x45, + 0x77, + 0x78, + 0x2b, + 0x4e, + 0x54, + 0x45, + 0x66, + 0x7a, + 0x52, + 0x2f, + 0x7a, + 0x48, + 0x46, + 0x41, + 0x57, + 0x61, + 0x2b, + 0x49, + 0x76, + 0x6f, + 0x46, + 0x79, + 0x75, + 0x44, + 0x7a, + 0x67, + 0x77, + 0x69, + 0x48, + 0x56, + 0x66, + 0x61, + 0x32, + 0x48, + 0x55, + 0x79, + 0x55, + 0x68, + 0x59, + 0x6f, + 0x7a, + 0x34, + 0x2b, + 0x4f, + 0x38, + 0x55, + 0x67, + 0x69, + 0x58, + 0x63, + 0x64, + 0x71, + 0x2b, + 0x4d, + 0x72, + 0x4b, + 0x33, + 0x0a, + 0x73, + 0x44, + 0x37, + 0x30, + 0x36, + 0x46, + 0x5a, + 0x4b, + 0x68, + 0x6c, + 0x63, + 0x52, + 0x55, + 0x68, + 0x32, + 0x51, + 0x2b, + 0x54, + 0x4b, + 0x32, + 0x4e, + 0x67, + 0x77, + 0x50, + 0x56, + 0x4c, + 0x2f, + 0x31, + 0x36, + 0x71, + 0x61, + 0x48, + 0x5a, + 0x5a, + 0x75, + 0x32, + 0x65, + 0x61, + 0x33, + 0x62, + 0x33, + 0x6e, + 0x5a, + 0x32, + 0x41, + 0x41, + 0x42, + 0x65, + 0x52, + 0x6f, + 0x51, + 0x47, + 0x68, + 0x42, + 0x4e, + 0x65, + 0x52, + 0x57, + 0x34, + 0x2b, + 0x4f, + 0x76, + 0x34, + 0x54, + 0x0a, + 0x59, + 0x55, + 0x31, + 0x33, + 0x59, + 0x51, + 0x50, + 0x6a, + 0x4f, + 0x74, + 0x76, + 0x42, + 0x70, + 0x41, + 0x2b, + 0x46, + 0x6d, + 0x56, + 0x48, + 0x72, + 0x51, + 0x49, + 0x5a, + 0x6c, + 0x43, + 0x4f, + 0x35, + 0x43, + 0x4d, + 0x79, + 0x36, + 0x2f, + 0x47, + 0x32, + 0x77, + 0x56, + 0x4b, + 0x48, + 0x62, + 0x31, + 0x4a, + 0x75, + 0x4d, + 0x48, + 0x63, + 0x4f, + 0x44, + 0x33, + 0x43, + 0x4d, + 0x70, + 0x43, + 0x6f, + 0x6e, + 0x45, + 0x44, + 0x44, + 0x32, + 0x36, + 0x6a, + 0x76, + 0x51, + 0x62, + 0x49, + 0x0a, + 0x43, + 0x4f, + 0x46, + 0x5a, + 0x37, + 0x4c, + 0x43, + 0x46, + 0x73, + 0x52, + 0x71, + 0x38, + 0x43, + 0x4a, + 0x35, + 0x4c, + 0x45, + 0x7a, + 0x33, + 0x69, + 0x62, + 0x4c, + 0x50, + 0x76, + 0x4b, + 0x50, + 0x78, + 0x51, + 0x67, + 0x6c, + 0x41, + 0x4f, + 0x75, + 0x37, + 0x62, + 0x44, + 0x44, + 0x74, + 0x62, + 0x79, + 0x4c, + 0x6d, + 0x58, + 0x49, + 0x4a, + 0x7a, + 0x76, + 0x51, + 0x33, + 0x65, + 0x31, + 0x30, + 0x49, + 0x31, + 0x45, + 0x77, + 0x39, + 0x53, + 0x61, + 0x56, + 0x76, + 0x59, + 0x39, + 0x65, + 0x0a, + 0x31, + 0x62, + 0x65, + 0x79, + 0x62, + 0x37, + 0x71, + 0x4f, + 0x31, + 0x36, + 0x44, + 0x62, + 0x73, + 0x54, + 0x75, + 0x6c, + 0x45, + 0x43, + 0x51, + 0x4e, + 0x2f, + 0x2b, + 0x79, + 0x4e, + 0x77, + 0x74, + 0x6b, + 0x44, + 0x34, + 0x58, + 0x69, + 0x37, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x41, + 0x63, + 0x73, + 0x6f, + 0x4f, + 0x7a, + 0x75, + 0x50, + 0x6d, + 0x37, + 0x35, + 0x4a, + 0x4f, + 0x32, + 0x42, + 0x33, + 0x76, + 0x32, + 0x45, + 0x64, + 0x2b, + 0x37, + 0x47, + 0x4b, + 0x31, + 0x53, + 0x63, + 0x0a, + 0x6c, + 0x76, + 0x56, + 0x4e, + 0x55, + 0x33, + 0x43, + 0x7a, + 0x30, + 0x4e, + 0x66, + 0x4c, + 0x50, + 0x4a, + 0x56, + 0x79, + 0x73, + 0x56, + 0x53, + 0x48, + 0x65, + 0x67, + 0x61, + 0x53, + 0x5a, + 0x38, + 0x33, + 0x5a, + 0x6f, + 0x4e, + 0x50, + 0x68, + 0x6d, + 0x54, + 0x42, + 0x47, + 0x62, + 0x72, + 0x56, + 0x45, + 0x72, + 0x31, + 0x62, + 0x33, + 0x42, + 0x55, + 0x59, + 0x30, + 0x6c, + 0x6b, + 0x46, + 0x49, + 0x4f, + 0x6f, + 0x33, + 0x6d, + 0x66, + 0x30, + 0x32, + 0x6c, + 0x37, + 0x57, + 0x48, + 0x59, + 0x0a, + 0x78, + 0x4d, + 0x51, + 0x38, + 0x48, + 0x63, + 0x32, + 0x61, + 0x6e, + 0x55, + 0x38, + 0x71, + 0x64, + 0x4f, + 0x58, + 0x76, + 0x6f, + 0x58, + 0x62, + 0x51, + 0x34, + 0x2f, + 0x59, + 0x59, + 0x4b, + 0x31, + 0x57, + 0x42, + 0x76, + 0x4b, + 0x75, + 0x2f, + 0x43, + 0x6c, + 0x42, + 0x53, + 0x45, + 0x63, + 0x61, + 0x4f, + 0x39, + 0x30, + 0x62, + 0x71, + 0x79, + 0x44, + 0x73, + 0x37, + 0x33, + 0x6d, + 0x69, + 0x46, + 0x57, + 0x2f, + 0x43, + 0x31, + 0x54, + 0x66, + 0x78, + 0x50, + 0x58, + 0x6e, + 0x6b, + 0x53, + 0x0a, + 0x54, + 0x2b, + 0x72, + 0x4f, + 0x4d, + 0x6d, + 0x77, + 0x52, + 0x51, + 0x67, + 0x4c, + 0x46, + 0x50, + 0x6c, + 0x35, + 0x46, + 0x41, + 0x62, + 0x48, + 0x4f, + 0x6b, + 0x31, + 0x41, + 0x58, + 0x5a, + 0x45, + 0x34, + 0x46, + 0x46, + 0x47, + 0x50, + 0x75, + 0x55, + 0x62, + 0x52, + 0x74, + 0x6b, + 0x4f, + 0x45, + 0x72, + 0x64, + 0x77, + 0x34, + 0x74, + 0x5a, + 0x68, + 0x53, + 0x77, + 0x66, + 0x58, + 0x74, + 0x2f, + 0x46, + 0x41, + 0x36, + 0x2b, + 0x32, + 0x4c, + 0x6e, + 0x48, + 0x79, + 0x37, + 0x38, + 0x38, + 0x0a, + 0x58, + 0x4f, + 0x46, + 0x62, + 0x71, + 0x30, + 0x46, + 0x4b, + 0x6e, + 0x63, + 0x6e, + 0x68, + 0x32, + 0x6a, + 0x61, + 0x2b, + 0x64, + 0x37, + 0x65, + 0x4e, + 0x6c, + 0x48, + 0x77, + 0x4c, + 0x4b, + 0x63, + 0x53, + 0x43, + 0x34, + 0x43, + 0x46, + 0x4e, + 0x46, + 0x4b, + 0x51, + 0x6d, + 0x6c, + 0x45, + 0x68, + 0x5a, + 0x5a, + 0x4a, + 0x48, + 0x63, + 0x65, + 0x6e, + 0x2f, + 0x42, + 0x30, + 0x6d, + 0x7a, + 0x72, + 0x48, + 0x6c, + 0x35, + 0x76, + 0x53, + 0x6c, + 0x59, + 0x79, + 0x37, + 0x50, + 0x37, + 0x73, + 0x0a, + 0x63, + 0x32, + 0x52, + 0x34, + 0x33, + 0x54, + 0x6c, + 0x45, + 0x41, + 0x4d, + 0x77, + 0x72, + 0x32, + 0x4a, + 0x30, + 0x4c, + 0x37, + 0x58, + 0x65, + 0x34, + 0x2b, + 0x33, + 0x45, + 0x36, + 0x31, + 0x4f, + 0x64, + 0x43, + 0x4f, + 0x30, + 0x46, + 0x37, + 0x4e, + 0x46, + 0x30, + 0x76, + 0x33, + 0x66, + 0x2f, + 0x34, + 0x5a, + 0x6f, + 0x4a, + 0x49, + 0x6d, + 0x4c, + 0x74, + 0x4e, + 0x7a, + 0x4d, + 0x4f, + 0x31, + 0x68, + 0x69, + 0x61, + 0x51, + 0x34, + 0x69, + 0x67, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char server_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x7a, + 0x43, + 0x43, + 0x41, + 0x77, + 0x4f, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x44, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x77, + 0x4d, + 0x31, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x41, + 0x7a, + 0x57, + 0x6a, + 0x42, + 0x50, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x43, + 0x57, + 0x78, + 0x76, + 0x59, + 0x32, + 0x46, + 0x73, + 0x61, + 0x47, + 0x39, + 0x7a, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x0a, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4d, + 0x74, + 0x58, + 0x71, + 0x68, + 0x69, + 0x59, + 0x68, + 0x47, + 0x6a, + 0x56, + 0x4c, + 0x77, + 0x56, + 0x75, + 0x61, + 0x79, + 0x76, + 0x4d, + 0x4d, + 0x49, + 0x72, + 0x6a, + 0x4b, + 0x37, + 0x53, + 0x34, + 0x4c, + 0x62, + 0x52, + 0x48, + 0x6c, + 0x4e, + 0x55, + 0x44, + 0x0a, + 0x74, + 0x4a, + 0x7a, + 0x46, + 0x6f, + 0x32, + 0x41, + 0x6a, + 0x75, + 0x4c, + 0x75, + 0x6d, + 0x74, + 0x65, + 0x46, + 0x2b, + 0x4b, + 0x4c, + 0x73, + 0x34, + 0x56, + 0x6a, + 0x59, + 0x7a, + 0x78, + 0x75, + 0x64, + 0x61, + 0x43, + 0x51, + 0x72, + 0x79, + 0x76, + 0x4d, + 0x61, + 0x6c, + 0x30, + 0x64, + 0x2f, + 0x38, + 0x32, + 0x38, + 0x75, + 0x6e, + 0x33, + 0x78, + 0x34, + 0x67, + 0x57, + 0x6a, + 0x59, + 0x54, + 0x4f, + 0x43, + 0x35, + 0x35, + 0x63, + 0x51, + 0x39, + 0x4d, + 0x44, + 0x4e, + 0x64, + 0x2b, + 0x0a, + 0x70, + 0x6d, + 0x4d, + 0x45, + 0x45, + 0x34, + 0x2b, + 0x64, + 0x31, + 0x75, + 0x6e, + 0x54, + 0x66, + 0x6f, + 0x39, + 0x38, + 0x38, + 0x32, + 0x6a, + 0x4d, + 0x38, + 0x65, + 0x6a, + 0x6c, + 0x4e, + 0x46, + 0x54, + 0x63, + 0x44, + 0x6f, + 0x54, + 0x50, + 0x68, + 0x6f, + 0x4e, + 0x37, + 0x77, + 0x71, + 0x66, + 0x78, + 0x4f, + 0x31, + 0x35, + 0x46, + 0x6e, + 0x69, + 0x54, + 0x77, + 0x54, + 0x43, + 0x57, + 0x39, + 0x30, + 0x62, + 0x6d, + 0x7a, + 0x37, + 0x32, + 0x74, + 0x36, + 0x7a, + 0x4c, + 0x4f, + 0x6d, + 0x0a, + 0x33, + 0x43, + 0x70, + 0x5a, + 0x75, + 0x75, + 0x64, + 0x48, + 0x53, + 0x6e, + 0x58, + 0x48, + 0x71, + 0x74, + 0x67, + 0x4f, + 0x7a, + 0x36, + 0x6e, + 0x53, + 0x53, + 0x62, + 0x54, + 0x6a, + 0x4f, + 0x31, + 0x51, + 0x52, + 0x33, + 0x4c, + 0x4e, + 0x2b, + 0x44, + 0x76, + 0x4e, + 0x30, + 0x34, + 0x62, + 0x4d, + 0x58, + 0x42, + 0x66, + 0x65, + 0x2f, + 0x30, + 0x4e, + 0x56, + 0x45, + 0x2f, + 0x51, + 0x70, + 0x65, + 0x4f, + 0x4f, + 0x47, + 0x50, + 0x5a, + 0x53, + 0x47, + 0x43, + 0x4f, + 0x74, + 0x4b, + 0x76, + 0x0a, + 0x44, + 0x5a, + 0x34, + 0x4f, + 0x75, + 0x79, + 0x4b, + 0x68, + 0x6d, + 0x32, + 0x52, + 0x6f, + 0x72, + 0x47, + 0x4a, + 0x6e, + 0x4c, + 0x6d, + 0x58, + 0x78, + 0x44, + 0x46, + 0x48, + 0x6d, + 0x42, + 0x4d, + 0x70, + 0x30, + 0x30, + 0x64, + 0x41, + 0x63, + 0x65, + 0x72, + 0x42, + 0x45, + 0x52, + 0x4c, + 0x44, + 0x6b, + 0x4c, + 0x52, + 0x70, + 0x64, + 0x57, + 0x34, + 0x38, + 0x4b, + 0x32, + 0x69, + 0x35, + 0x74, + 0x42, + 0x51, + 0x76, + 0x41, + 0x63, + 0x4a, + 0x5a, + 0x7a, + 0x6c, + 0x4c, + 0x38, + 0x51, + 0x0a, + 0x2b, + 0x48, + 0x32, + 0x66, + 0x67, + 0x75, + 0x53, + 0x62, + 0x42, + 0x32, + 0x32, + 0x65, + 0x55, + 0x79, + 0x74, + 0x42, + 0x62, + 0x62, + 0x4e, + 0x6e, + 0x67, + 0x76, + 0x36, + 0x68, + 0x79, + 0x52, + 0x79, + 0x30, + 0x62, + 0x41, + 0x49, + 0x7a, + 0x61, + 0x59, + 0x48, + 0x77, + 0x61, + 0x70, + 0x52, + 0x49, + 0x78, + 0x72, + 0x4c, + 0x54, + 0x57, + 0x6e, + 0x64, + 0x4d, + 0x69, + 0x42, + 0x37, + 0x79, + 0x4f, + 0x76, + 0x64, + 0x69, + 0x73, + 0x6c, + 0x55, + 0x2b, + 0x31, + 0x53, + 0x6f, + 0x46, + 0x0a, + 0x57, + 0x41, + 0x62, + 0x66, + 0x56, + 0x35, + 0x79, + 0x75, + 0x68, + 0x77, + 0x4f, + 0x6e, + 0x77, + 0x54, + 0x4d, + 0x53, + 0x6f, + 0x64, + 0x52, + 0x30, + 0x39, + 0x4f, + 0x78, + 0x67, + 0x55, + 0x61, + 0x75, + 0x41, + 0x32, + 0x79, + 0x72, + 0x41, + 0x4c, + 0x32, + 0x51, + 0x37, + 0x42, + 0x4d, + 0x35, + 0x47, + 0x6e, + 0x59, + 0x4c, + 0x70, + 0x55, + 0x6f, + 0x35, + 0x71, + 0x35, + 0x46, + 0x67, + 0x41, + 0x77, + 0x6b, + 0x73, + 0x4a, + 0x6c, + 0x38, + 0x77, + 0x79, + 0x53, + 0x44, + 0x70, + 0x59, + 0x0a, + 0x63, + 0x51, + 0x72, + 0x37, + 0x46, + 0x72, + 0x7a, + 0x53, + 0x6a, + 0x71, + 0x63, + 0x31, + 0x6f, + 0x39, + 0x55, + 0x73, + 0x37, + 0x68, + 0x45, + 0x45, + 0x6c, + 0x6b, + 0x76, + 0x4e, + 0x42, + 0x5a, + 0x6b, + 0x31, + 0x2b, + 0x56, + 0x50, + 0x54, + 0x61, + 0x50, + 0x35, + 0x78, + 0x45, + 0x59, + 0x79, + 0x70, + 0x63, + 0x62, + 0x67, + 0x2b, + 0x75, + 0x2b, + 0x59, + 0x43, + 0x7a, + 0x63, + 0x74, + 0x4a, + 0x57, + 0x6e, + 0x4a, + 0x4b, + 0x62, + 0x43, + 0x42, + 0x7a, + 0x32, + 0x65, + 0x73, + 0x36, + 0x0a, + 0x4b, + 0x6b, + 0x70, + 0x66, + 0x43, + 0x34, + 0x61, + 0x57, + 0x63, + 0x38, + 0x55, + 0x49, + 0x4c, + 0x6c, + 0x55, + 0x4b, + 0x2f, + 0x57, + 0x34, + 0x6a, + 0x52, + 0x46, + 0x73, + 0x4e, + 0x47, + 0x33, + 0x55, + 0x68, + 0x41, + 0x31, + 0x76, + 0x6e, + 0x49, + 0x5a, + 0x77, + 0x4b, + 0x38, + 0x36, + 0x69, + 0x71, + 0x6d, + 0x33, + 0x6b, + 0x2b, + 0x50, + 0x45, + 0x62, + 0x61, + 0x68, + 0x63, + 0x45, + 0x78, + 0x41, + 0x34, + 0x71, + 0x42, + 0x2f, + 0x4c, + 0x42, + 0x68, + 0x6f, + 0x67, + 0x76, + 0x73, + 0x0a, + 0x34, + 0x7a, + 0x39, + 0x62, + 0x32, + 0x4d, + 0x51, + 0x33, + 0x35, + 0x74, + 0x69, + 0x49, + 0x6f, + 0x51, + 0x44, + 0x6a, + 0x37, + 0x52, + 0x35, + 0x78, + 0x4e, + 0x38, + 0x63, + 0x35, + 0x41, + 0x79, + 0x77, + 0x44, + 0x79, + 0x62, + 0x38, + 0x78, + 0x47, + 0x76, + 0x32, + 0x59, + 0x6d, + 0x6b, + 0x34, + 0x6a, + 0x47, + 0x74, + 0x44, + 0x6b, + 0x64, + 0x71, + 0x72, + 0x62, + 0x74, + 0x49, + 0x55, + 0x34, + 0x32, + 0x2b, + 0x48, + 0x4d, + 0x31, + 0x79, + 0x4f, + 0x6b, + 0x38, + 0x75, + 0x38, + 0x5a, + 0x0a, + 0x45, + 0x69, + 0x78, + 0x4c, + 0x51, + 0x7a, + 0x6f, + 0x77, + 0x6d, + 0x71, + 0x55, + 0x71, + 0x72, + 0x65, + 0x42, + 0x43, + 0x68, + 0x6f, + 0x41, + 0x6f, + 0x73, + 0x63, + 0x33, + 0x46, + 0x4f, + 0x52, + 0x4f, + 0x6d, + 0x54, + 0x43, + 0x44, + 0x6c, + 0x59, + 0x36, + 0x33, + 0x34, + 0x2f, + 0x61, + 0x63, + 0x49, + 0x49, + 0x50, + 0x33, + 0x31, + 0x64, + 0x65, + 0x64, + 0x48, + 0x4e, + 0x59, + 0x5a, + 0x35, + 0x31, + 0x52, + 0x72, + 0x75, + 0x6b, + 0x41, + 0x64, + 0x41, + 0x36, + 0x32, + 0x44, + 0x62, + 0x0a, + 0x47, + 0x44, + 0x77, + 0x67, + 0x77, + 0x7a, + 0x36, + 0x54, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4b, + 0x56, + 0x76, + 0x2f, + 0x53, + 0x43, + 0x50, + 0x4f, + 0x67, + 0x77, + 0x50, + 0x41, + 0x59, + 0x30, + 0x47, + 0x34, + 0x37, + 0x79, + 0x63, + 0x76, + 0x55, + 0x48, + 0x67, + 0x0a, + 0x6b, + 0x33, + 0x44, + 0x51, + 0x67, + 0x6d, + 0x62, + 0x78, + 0x56, + 0x54, + 0x46, + 0x46, + 0x57, + 0x51, + 0x79, + 0x72, + 0x50, + 0x36, + 0x58, + 0x76, + 0x4b, + 0x48, + 0x4f, + 0x6c, + 0x33, + 0x65, + 0x5a, + 0x55, + 0x32, + 0x52, + 0x65, + 0x4a, + 0x78, + 0x65, + 0x54, + 0x78, + 0x43, + 0x4d, + 0x55, + 0x4b, + 0x63, + 0x4e, + 0x6c, + 0x57, + 0x34, + 0x2b, + 0x65, + 0x4d, + 0x32, + 0x6f, + 0x43, + 0x5a, + 0x37, + 0x57, + 0x31, + 0x76, + 0x69, + 0x74, + 0x61, + 0x75, + 0x70, + 0x6e, + 0x4f, + 0x30, + 0x0a, + 0x7a, + 0x4c, + 0x57, + 0x36, + 0x47, + 0x42, + 0x46, + 0x53, + 0x56, + 0x58, + 0x55, + 0x34, + 0x54, + 0x2b, + 0x59, + 0x5a, + 0x63, + 0x69, + 0x49, + 0x48, + 0x6e, + 0x78, + 0x6b, + 0x71, + 0x6c, + 0x48, + 0x33, + 0x58, + 0x78, + 0x63, + 0x6b, + 0x67, + 0x7a, + 0x4e, + 0x56, + 0x4c, + 0x79, + 0x57, + 0x44, + 0x41, + 0x41, + 0x4a, + 0x6f, + 0x61, + 0x77, + 0x32, + 0x63, + 0x2b, + 0x58, + 0x31, + 0x45, + 0x39, + 0x42, + 0x7a, + 0x59, + 0x62, + 0x49, + 0x54, + 0x2b, + 0x7a, + 0x4d, + 0x59, + 0x6a, + 0x5a, + 0x0a, + 0x31, + 0x62, + 0x59, + 0x41, + 0x61, + 0x76, + 0x79, + 0x51, + 0x4f, + 0x6f, + 0x4d, + 0x50, + 0x64, + 0x2f, + 0x31, + 0x7a, + 0x30, + 0x64, + 0x59, + 0x6d, + 0x49, + 0x39, + 0x46, + 0x72, + 0x77, + 0x78, + 0x5a, + 0x32, + 0x4f, + 0x42, + 0x6e, + 0x76, + 0x2b, + 0x63, + 0x58, + 0x41, + 0x72, + 0x69, + 0x30, + 0x2f, + 0x4a, + 0x4d, + 0x47, + 0x54, + 0x63, + 0x7a, + 0x58, + 0x36, + 0x66, + 0x56, + 0x67, + 0x53, + 0x45, + 0x50, + 0x47, + 0x66, + 0x48, + 0x48, + 0x63, + 0x6f, + 0x36, + 0x53, + 0x4b, + 0x4a, + 0x0a, + 0x52, + 0x5a, + 0x57, + 0x38, + 0x56, + 0x49, + 0x39, + 0x67, + 0x49, + 0x4f, + 0x52, + 0x75, + 0x57, + 0x68, + 0x79, + 0x39, + 0x76, + 0x78, + 0x55, + 0x57, + 0x41, + 0x43, + 0x4a, + 0x6f, + 0x30, + 0x4f, + 0x6b, + 0x4a, + 0x44, + 0x75, + 0x55, + 0x37, + 0x2b, + 0x57, + 0x54, + 0x61, + 0x39, + 0x65, + 0x44, + 0x66, + 0x6a, + 0x46, + 0x2b, + 0x62, + 0x62, + 0x68, + 0x2b, + 0x2f, + 0x49, + 0x73, + 0x38, + 0x36, + 0x66, + 0x72, + 0x4c, + 0x32, + 0x67, + 0x76, + 0x38, + 0x79, + 0x39, + 0x69, + 0x4d, + 0x55, + 0x0a, + 0x74, + 0x32, + 0x36, + 0x37, + 0x75, + 0x58, + 0x39, + 0x52, + 0x52, + 0x64, + 0x30, + 0x46, + 0x70, + 0x69, + 0x4b, + 0x70, + 0x2b, + 0x70, + 0x4b, + 0x4d, + 0x48, + 0x6d, + 0x50, + 0x77, + 0x47, + 0x33, + 0x70, + 0x75, + 0x6b, + 0x78, + 0x57, + 0x64, + 0x38, + 0x76, + 0x6b, + 0x4f, + 0x72, + 0x67, + 0x6a, + 0x4a, + 0x44, + 0x46, + 0x2b, + 0x54, + 0x56, + 0x48, + 0x67, + 0x67, + 0x4c, + 0x31, + 0x4a, + 0x59, + 0x58, + 0x5a, + 0x37, + 0x45, + 0x4a, + 0x39, + 0x79, + 0x6a, + 0x50, + 0x64, + 0x77, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +#ifndef BSC_NETWORK_IFACE +#define BSC_NETWORK_IFACE "127.0.0.1" +#endif +#define BACNET_WEBSOCKET_SERVER_PORT 39004 +#ifdef ZEPHYR_TEST +#define BACNET_WEBSOCKET_SERVER_ADDR CONFIG_NET_CONFIG_MY_IPV4_ADDR +#else +#define BACNET_WEBSOCKET_SERVER_ADDR "127.0.0.1" +#endif +#define BACNET_SOCKET_TIMEOUT 5 +#define BACNET_SOCKET_HEARTBEAT_TIMEOUT 3 +#define MAX_BVLC_LEN 1500 +#define MAX_NDPU_LEN 1500 +#define MAX_SERVER_SOCKETS BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM +#define MAX_CLIENT_SOCKETS BSC_CONF_CLIENT_CONNECTIONS_NUM +#define WAIT_EVENT_MS 100 + +// MbedTLS expects a key in DER format +#ifdef CONFIG_MBEDTLS +#define CLIENT_KEY client_key_der +#else +#define CLIENT_KEY client_key +#endif + +typedef struct { + BSC_SOCKET_EVENT ev_code; + BSC_EVENT *ev; + BACNET_ERROR_CODE err; +} sock_ev_t; + +typedef struct { + BSC_CTX_EVENT ev_code; + BSC_EVENT *ev; +} ctx_ev_t; + +static sock_ev_t srv_ev; +static sock_ev_t cli_ev; +static sock_ev_t cli_ev2; +static ctx_ev_t srv_ctx_ev; +static ctx_ev_t cli_ctx_ev; +static ctx_ev_t cli_ctx_ev2; + +static uint8_t recv_buf[4096]; +static size_t recv_buf_len = 0; +static BSC_SOCKET *srv_sock = NULL; +static BSC_SOCKET srv_socks[MAX_SERVER_SOCKETS]; +static BSC_SOCKET cli_socks[MAX_CLIENT_SOCKETS]; +static BSC_SOCKET cli_socks2[MAX_CLIENT_SOCKETS]; + +static void init_sock_ev(sock_ev_t *ev) +{ + ev->ev_code = -1; + ev->err = -1; + ev->ev = bsc_event_init(); + zassert_not_equal(ev->ev, NULL, 0); +} + +static void deinit_sock_ev(sock_ev_t *ev) +{ + bsc_event_deinit(ev->ev); +} + +static void init_ctx_ev(ctx_ev_t *ev) +{ + ev->ev_code = -1; + ev->ev = bsc_event_init(); + zassert_not_equal(ev->ev, NULL, 0); +} + +static void deinit_ctx_ev(ctx_ev_t *ev) +{ + bsc_event_deinit(ev->ev); +} + +static void call_maintenance_timer(bool reset, int time_passed_ms) +{ + static int total_ms; + if (reset) { + total_ms = 0; + } + + total_ms += time_passed_ms; + + if (total_ms >= 1000) { + bsc_socket_maintenance_timer(1); + total_ms = 0; + } +} + +static void wait_sec(int seconds) +{ + while (seconds >= 0) { + bsc_wait(1); + bsc_socket_maintenance_timer(1); + seconds--; + } +} + +static bool wait_sock_ev(sock_ev_t *ev, BSC_SOCKET_EVENT wait_ev) +{ + call_maintenance_timer(1, 0); + while (!bsc_event_timedwait(ev->ev, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + bws_dispatch_lock(); + debug_printf( + "wait_sock_ev ev = %p ev->ev = %p awaited_ev %d received_ev %d\n", ev, + ev->ev, wait_ev, ev->ev_code); + if (ev->ev_code == wait_ev) { + ev->ev_code = -1; + // reset event if it was signalled while we were blocked in call + // bws_dispatch_lock(). (in that case ev->ev contains code of last + // event.) that's tricky but that allows to avoid using mutexes which + // are platform specific in test code + bsc_event_timedwait(ev->ev, 1); + bws_dispatch_unlock(); + return true; + } else { + debug_printf( + "wait_sock_ev ev = %p ev->ev = %p awaited_ev %d received_ev %d\n", + ev, ev->ev, wait_ev, ev->ev_code); + ev->ev_code = -1; + bws_dispatch_unlock(); + return false; + } +} + +static void +wait_for_srv_specific_socket_state(BSC_SOCKET *s, BSC_SOCKET_STATE st) +{ + int i; + call_maintenance_timer(1, 0); + while (1) { + bsc_wait_ms(WAIT_EVENT_MS); + call_maintenance_timer(0, WAIT_EVENT_MS); + bws_dispatch_lock(); + for (i = 0; i < MAX_SERVER_SOCKETS; i++) { + if (&srv_socks[i] == s && srv_socks[i].state == st) { + bws_dispatch_unlock(); + return; + } + } + bws_dispatch_unlock(); + } +} + +static void wait_specific_sock_ev(sock_ev_t *ev, BSC_SOCKET_EVENT wait_ev) +{ + call_maintenance_timer(1, 0); + while (1) { + while (!bsc_event_timedwait(ev->ev, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + bws_dispatch_lock(); + debug_printf( + "wait_specific: wait_sock_ev ev = %p awaited_ev %d " + "received_ev %d\n", + ev, wait_ev, ev->ev_code); + if (ev->ev_code == wait_ev) { + ev->ev_code = -1; + // reset event if it was signalled while we were blocked in call + // bws_dispatch_lock(). (in that case ev->ev contains code of last + // event.) that's tricky but that allows to avoid using mutexes + // which are platform specific in test code + bsc_event_timedwait(ev->ev, 1); + bws_dispatch_unlock(); + break; + } else { + debug_printf( + "wait_specific: event is ignored wait_sock_ev ev = %p " + "awaited_ev %d received_ev %d\n", + ev, wait_ev, ev->ev_code); + ev->ev_code = -1; + bws_dispatch_unlock(); + continue; + } + } +} + +static void +signal_sock_ev(sock_ev_t *ev, BSC_SOCKET_EVENT s_ev, BACNET_ERROR_CODE err) +{ + debug_printf("signal_sock_ev %p ev->ev %p s_ev %d\n", ev, ev->ev, s_ev); + ev->ev_code = s_ev; + ev->err = err; + bsc_event_signal(ev->ev); +} + +static bool wait_ctx_ev(ctx_ev_t *ev, BSC_CTX_EVENT wait_ev) +{ + call_maintenance_timer(1, 0); + while (!bsc_event_timedwait(ev->ev, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + + bws_dispatch_lock(); + if (ev->ev_code == wait_ev) { + bws_dispatch_unlock(); + return true; + } else { + bws_dispatch_unlock(); + return false; + } +} + +static void signal_ctx_ev(ctx_ev_t *ev, BSC_CTX_EVENT c_ev) +{ + ev->ev_code = c_ev; + bsc_event_signal(ev->ev); +} + +static BSC_SOCKET * +simple_find_connection_for_vmac(BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg) +{ + (void)vmac; + (void)user_arg; + return NULL; +} + +static BSC_SOCKET * +simple_find_connection_for_uuid(BACNET_SC_UUID *uuid, void *user_arg) +{ + (void)uuid; + (void)user_arg; + return NULL; +} + +static BSC_SOCKET * +srv_find_connection_for_uuid(BACNET_SC_UUID *uuid, void *user_arg) +{ + int i; + (void)user_arg; + + for (i = 0; i < sizeof(srv_socks) / sizeof(BSC_SOCKET); i++) { + if (srv_socks[i].state != BSC_SOCK_STATE_IDLE && + !memcmp( + &uuid->uuid[0], &srv_socks[i].uuid.uuid[0], + sizeof(uuid->uuid))) { + return &srv_socks[i]; + } + } + return NULL; +} + +static BSC_SOCKET * +srv_find_connection_for_vmac(BACNET_SC_VMAC_ADDRESS *vmac, void *user_arg) +{ + int i; + (void)user_arg; + + debug_printf( + "srv_find_connection_for_uuid() vmac = %s\n", bsc_vmac_to_string(vmac)); + for (i = 0; i < sizeof(srv_socks) / sizeof(BSC_SOCKET); i++) { + debug_printf( + "c = %p state = %d vmac = %s\n", &srv_socks[i], srv_socks[i].state, + bsc_vmac_to_string(&srv_socks[i].vmac)); + if (srv_socks[i].state != BSC_SOCK_STATE_IDLE && + !memcmp( + &vmac->address[0], &srv_socks[i].vmac.address[0], + sizeof(vmac->address))) { + debug_printf( + "srv_find_connection_for_uuid() ret = %p\n", &srv_socks[i]); + return &srv_socks[i]; + } + } + debug_printf("srv_find_connection_for_uuid() ret = NULL\n"); + return NULL; +} + +static void cli_simple_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + debug_printf("cli ev = %d, reason = %d, ev = %p\n", ev, reason, &cli_ev); + (void)c; + (void)reason_desc; + (void)decoded_pdu; + + if (ev == BSC_SOCKET_EVENT_RECEIVED) { + memcpy(recv_buf, pdu, pdu_len); + recv_buf_len = pdu_len; + } + signal_sock_ev(&cli_ev, ev, reason); +} + +static void cli_simple_socket_event2( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + debug_printf("cli2 ev = %d, reason = %d, ev = %p\n", ev, reason, &cli_ev2); + bws_dispatch_lock(); + (void)c; + (void)reason_desc; + (void)decoded_pdu; + + if (ev == BSC_SOCKET_EVENT_RECEIVED) { + memcpy(recv_buf, pdu, pdu_len); + recv_buf_len = pdu_len; + } + signal_sock_ev(&cli_ev2, ev, reason); + bws_dispatch_unlock(); +} + +static void srv_simple_socket_event( + BSC_SOCKET *c, + BSC_SOCKET_EVENT ev, + BACNET_ERROR_CODE reason, + const char *reason_desc, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + debug_printf("srv ev = %d, reason = %d, ev = %p\n", ev, reason, &srv_ev); + bws_dispatch_lock(); + (void)reason_desc; + (void)decoded_pdu; + srv_sock = c; + if (ev == BSC_SOCKET_EVENT_RECEIVED) { + memcpy(recv_buf, pdu, pdu_len); + recv_buf_len = pdu_len; + } + signal_sock_ev(&srv_ev, ev, reason); + bws_dispatch_unlock(); +} + +static void cli_simple_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev) +{ + (void)ctx; + bws_dispatch_lock(); + signal_ctx_ev(&cli_ctx_ev, ev); + bws_dispatch_unlock(); +} + +static void cli_simple_context_event2(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev) +{ + (void)ctx; + bws_dispatch_lock(); + signal_ctx_ev(&cli_ctx_ev2, ev); + bws_dispatch_unlock(); +} + +static void srv_simple_context_event(BSC_SOCKET_CTX *ctx, BSC_CTX_EVENT ev) +{ + (void)ctx; + bws_dispatch_lock(); + signal_ctx_ev(&srv_ctx_ev, ev); + bws_dispatch_unlock(); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(socket_test_1, test_simple) +#else +static void test_simple(void) +#endif +{ + BSC_CONTEXT_CFG server_cfg; + BSC_CONTEXT_CFG client_cfg; + BACNET_SC_UUID server_uuid; + BACNET_SC_VMAC_ADDRESS server_vmac; + BACNET_SC_UUID client_uuid; + BACNET_SC_VMAC_ADDRESS client_vmac; + BSC_SC_RET ret; + BSC_SOCKET_CTX_FUNCS srv_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + srv_simple_socket_event, + srv_simple_context_event, NULL }; + + BSC_SOCKET_CTX_FUNCS cli_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + cli_simple_socket_event, + cli_simple_context_event, NULL }; + + BSC_SOCKET_CTX srv_ctx; + BSC_SOCKET_CTX cli_ctx; + char url[128]; + uint8_t buf[2048]; + uint8_t npdu[1200]; + size_t len; + init_sock_ev(&cli_ev); + init_sock_ev(&srv_ev); + init_ctx_ev(&cli_ctx_ev); + init_ctx_ev(&srv_ctx_ev); + + memset(&srv_ctx, 0, sizeof(srv_ctx)); + memset(&cli_ctx, 0, sizeof(cli_ctx)); + memset(&server_uuid, 0x1, sizeof(server_uuid)); + memset(&server_vmac, 0x2, sizeof(server_vmac)); + memset(&client_uuid, 0x3, sizeof(server_uuid)); + memset(&client_vmac, 0x4, sizeof(server_vmac)); + sprintf( + url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_ACCEPTOR, &server_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), &server_uuid, &server_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &client_uuid, &client_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + ret = bsc_init_ctx( + &srv_ctx, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + + ret = bsc_init_ctx( + &cli_ctx, &client_cfg, &cli_funcs, cli_socks, MAX_CLIENT_SOCKETS, NULL); + + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_connect(&cli_ctx, &cli_socks[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + zassert_equal(wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + + // test that heartbeat works + // ensure that there were no any events for that 10 seconds + // (connection was not dropped) + wait_sec(10); + bws_dispatch_lock(); + zassert_equal( + cli_socks[0].state == BSC_SOCK_STATE_CONNECTED && + srv_socks[0].state == BSC_SOCK_STATE_CONNECTED, + true, 0); + bws_dispatch_unlock(); + // simple test for data flow + + memset(npdu, 0x55, sizeof(npdu)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 400, NULL, NULL, npdu, sizeof(npdu)); + ret = bsc_send(&cli_socks[0], buf, len); + zassert_equal( + ret, BSC_SC_NO_RESOURCES, 0); /* BUG? should be BSC_SC_SUCCESS*/ + // zassert_equal(ret, BSC_SC_SUCCESS, 0); + // zassert_equal(wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_RECEIVED), true, 0); + + // zassert_equal(len == recv_buf_len, true, 0); + // zassert_equal(!memcmp(buf, recv_buf, len), true, 0); + // memset(npdu, 0x44, sizeof(npdu)); + // len = bvlc_sc_encode_encapsulated_npdu( + // buf, sizeof(buf), 500, NULL, NULL, npdu, sizeof(npdu)); + // ret = bsc_send(srv_sock, buf, len); + // zassert_equal(ret, BSC_SC_SUCCESS, 0); + // zassert_equal(wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_RECEIVED), true, 0); + // zassert_equal(len == recv_buf_len, true, 0); + // zassert_equal(!memcmp(buf, recv_buf, len), true, 0); + bsc_disconnect(&cli_socks[0]); + zassert_equal( + wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_DISCONNECTED), true, 0); + bsc_deinit_ctx(&cli_ctx); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + bsc_deinit_ctx(&srv_ctx); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + deinit_sock_ev(&cli_ev); + deinit_sock_ev(&srv_ev); + deinit_ctx_ev(&cli_ctx_ev); + deinit_ctx_ev(&srv_ctx_ev); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(socket_test_2, test_duplicated_vmac_on_server) +#else +static void test_duplicated_vmac_on_server(void) +#endif +{ + BSC_CONTEXT_CFG server_cfg; + BSC_CONTEXT_CFG client_cfg; + BSC_CONTEXT_CFG client_cfg2; + BACNET_SC_UUID server_uuid; + BACNET_SC_VMAC_ADDRESS server_vmac; + BACNET_SC_UUID client_uuid; + BACNET_SC_VMAC_ADDRESS client_vmac; + BACNET_SC_UUID client_uuid2; + BACNET_SC_VMAC_ADDRESS client_vmac2; + BSC_SC_RET ret; + BSC_SOCKET_CTX_FUNCS srv_funcs = { srv_find_connection_for_vmac, + simple_find_connection_for_uuid, + srv_simple_socket_event, + srv_simple_context_event, NULL }; + + BSC_SOCKET_CTX_FUNCS cli_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + cli_simple_socket_event, + cli_simple_context_event, NULL }; + BSC_SOCKET_CTX_FUNCS cli_funcs2 = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + cli_simple_socket_event2, + cli_simple_context_event2, NULL }; + BSC_SOCKET_CTX srv_ctx; + BSC_SOCKET_CTX cli_ctx; + BSC_SOCKET_CTX cli_ctx2; + char url[128]; + + init_sock_ev(&cli_ev); + init_sock_ev(&cli_ev2); + init_sock_ev(&srv_ev); + init_ctx_ev(&cli_ctx_ev); + init_ctx_ev(&cli_ctx_ev2); + init_ctx_ev(&srv_ctx_ev); + + memset(&srv_ctx, 0, sizeof(srv_ctx)); + memset(&cli_ctx, 0, sizeof(cli_ctx)); + memset(&cli_ctx2, 0, sizeof(cli_ctx)); + memset(&server_uuid, 0x1, sizeof(server_uuid)); + memset(&server_vmac, 0x2, sizeof(server_vmac)); + memset(&client_uuid, 0x3, sizeof(server_uuid)); + memset(&client_vmac, 0x5, sizeof(server_vmac)); + memset(&client_uuid2, 0x4, sizeof(server_uuid)); + memset(&client_vmac2, 0x5, sizeof(server_vmac)); + + sprintf( + url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_ACCEPTOR, &server_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), &server_uuid, &server_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &client_uuid, &client_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg2, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &client_uuid2, &client_vmac2, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + ret = bsc_init_ctx( + &srv_ctx, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_init_ctx( + &cli_ctx, &client_cfg, &cli_funcs, cli_socks, MAX_CLIENT_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_init_ctx( + &cli_ctx2, &client_cfg2, &cli_funcs2, cli_socks2, MAX_CLIENT_SOCKETS, + NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev2, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_connect(&cli_ctx, &cli_socks[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + zassert_equal(wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + ret = bsc_connect(&cli_ctx2, &cli_socks2[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + wait_specific_sock_ev(&srv_ev, BSC_SOCKET_EVENT_DISCONNECTED); + zassert_equal(srv_ev.err, ERROR_CODE_NODE_DUPLICATE_VMAC, NULL); + zassert_equal( + wait_sock_ev(&cli_ev2, BSC_SOCKET_EVENT_DISCONNECTED), true, 0); + zassert_equal(cli_ev2.err, ERROR_CODE_NODE_DUPLICATE_VMAC, NULL); + bsc_deinit_ctx(&cli_ctx); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + bsc_deinit_ctx(&cli_ctx2); + zassert_equal(wait_ctx_ev(&cli_ctx_ev2, BSC_CTX_DEINITIALIZED), true, 0); + bsc_deinit_ctx(&srv_ctx); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + deinit_sock_ev(&cli_ev); + deinit_sock_ev(&cli_ev2); + deinit_sock_ev(&srv_ev); + deinit_ctx_ev(&cli_ctx_ev); + deinit_ctx_ev(&cli_ctx_ev2); + deinit_ctx_ev(&srv_ctx_ev); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(socket_test_3, test_duplicated_vmac_on_server2) +#else +static void test_duplicated_vmac_on_server2(void) +#endif +{ + BSC_CONTEXT_CFG server_cfg; + BSC_CONTEXT_CFG client_cfg; + BACNET_SC_UUID server_uuid; + BACNET_SC_VMAC_ADDRESS server_vmac; + BACNET_SC_UUID client_uuid; + BACNET_SC_VMAC_ADDRESS client_vmac; + BSC_SC_RET ret; + BSC_SOCKET_CTX_FUNCS srv_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + srv_simple_socket_event, + srv_simple_context_event, NULL }; + + BSC_SOCKET_CTX_FUNCS cli_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + cli_simple_socket_event, + cli_simple_context_event, NULL }; + BSC_SOCKET_CTX srv_ctx; + BSC_SOCKET_CTX cli_ctx; + char url[128]; + + init_sock_ev(&cli_ev); + init_sock_ev(&srv_ev); + init_ctx_ev(&cli_ctx_ev); + init_ctx_ev(&srv_ctx_ev); + + memset(&srv_ctx, 0, sizeof(srv_ctx)); + memset(&cli_ctx, 0, sizeof(cli_ctx)); + memset(&server_uuid, 0x1, sizeof(server_uuid)); + memset(&server_vmac, 0x2, sizeof(server_vmac)); + memset(&client_uuid, 0x3, sizeof(server_uuid)); + memset(&client_vmac, 0x2, sizeof(server_vmac)); + + sprintf( + url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_ACCEPTOR, &server_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), &server_uuid, &server_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &client_uuid, &client_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + ret = bsc_init_ctx( + &srv_ctx, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_init_ctx( + &cli_ctx, &client_cfg, &cli_funcs, cli_socks, MAX_CLIENT_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_connect(&cli_ctx, &cli_socks[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal( + wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_DISCONNECTED), true, 0); + zassert_equal(cli_ev.err, ERROR_CODE_NODE_DUPLICATE_VMAC, NULL); + wait_specific_sock_ev(&srv_ev, BSC_SOCKET_EVENT_DISCONNECTED); + zassert_equal(srv_ev.err, ERROR_CODE_NODE_DUPLICATE_VMAC, NULL); + bsc_deinit_ctx(&cli_ctx); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + bsc_deinit_ctx(&srv_ctx); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + deinit_sock_ev(&cli_ev); + deinit_sock_ev(&srv_ev); + deinit_ctx_ev(&cli_ctx_ev); + deinit_ctx_ev(&srv_ctx_ev); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(socket_test_4, test_duplicated_uuid_on_server) +#else +static void test_duplicated_uuid_on_server(void) +#endif +{ + BSC_CONTEXT_CFG server_cfg; + BSC_CONTEXT_CFG client_cfg; + BSC_CONTEXT_CFG client_cfg2; + BACNET_SC_UUID server_uuid; + BACNET_SC_VMAC_ADDRESS server_vmac; + BACNET_SC_UUID client_uuid; + BACNET_SC_VMAC_ADDRESS client_vmac; + BACNET_SC_UUID client_uuid2; + BACNET_SC_VMAC_ADDRESS client_vmac2; + BSC_SC_RET ret; + BSC_SOCKET_CTX_FUNCS srv_funcs = { simple_find_connection_for_vmac, + srv_find_connection_for_uuid, + srv_simple_socket_event, + srv_simple_context_event, NULL }; + + BSC_SOCKET_CTX_FUNCS cli_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + cli_simple_socket_event, + cli_simple_context_event, NULL }; + BSC_SOCKET_CTX_FUNCS cli_funcs2 = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + cli_simple_socket_event2, + cli_simple_context_event2, NULL }; + + BSC_SOCKET_CTX srv_ctx; + BSC_SOCKET_CTX cli_ctx; + BSC_SOCKET_CTX cli_ctx2; + char url[128]; + + init_sock_ev(&cli_ev); + init_sock_ev(&cli_ev2); + init_sock_ev(&srv_ev); + init_ctx_ev(&cli_ctx_ev); + init_ctx_ev(&cli_ctx_ev2); + init_ctx_ev(&srv_ctx_ev); + + memset(&srv_ctx, 0, sizeof(srv_ctx)); + memset(&cli_ctx, 0, sizeof(cli_ctx)); + memset(&cli_ctx2, 0, sizeof(cli_ctx)); + memset(&server_uuid, 0x1, sizeof(server_uuid)); + memset(&server_vmac, 0x2, sizeof(server_vmac)); + memset(&client_uuid, 0x3, sizeof(server_uuid)); + memset(&client_vmac, 0x4, sizeof(server_vmac)); + memset(&client_uuid2, 0x3, sizeof(server_uuid)); + memset(&client_vmac2, 0x5, sizeof(server_vmac)); + + sprintf( + url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_ACCEPTOR, &server_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), &server_uuid, &server_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &client_uuid, &client_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg2, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &client_uuid2, &client_vmac2, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + ret = bsc_init_ctx( + &srv_ctx, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_init_ctx( + &cli_ctx, &client_cfg, &cli_funcs, cli_socks, MAX_CLIENT_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_init_ctx( + &cli_ctx2, &client_cfg2, &cli_funcs2, cli_socks2, MAX_CLIENT_SOCKETS, + NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev2, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_connect(&cli_ctx, &cli_socks[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + zassert_equal(wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + ret = bsc_connect(&cli_ctx2, &cli_socks2[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal( + wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_DISCONNECTED), true, 0); + zassert_equal(cli_ev.err, ERROR_CODE_SUCCESS, NULL); + zassert_equal(wait_sock_ev(&cli_ev2, BSC_SOCKET_EVENT_CONNECTED), true, 0); + wait_for_srv_specific_socket_state(&srv_socks[0], BSC_SOCK_STATE_IDLE); + wait_for_srv_specific_socket_state(&srv_socks[1], BSC_SOCK_STATE_CONNECTED); + bsc_deinit_ctx(&cli_ctx); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + bsc_deinit_ctx(&cli_ctx2); + zassert_equal(wait_ctx_ev(&cli_ctx_ev2, BSC_CTX_DEINITIALIZED), true, 0); + bsc_deinit_ctx(&srv_ctx); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + deinit_sock_ev(&cli_ev); + deinit_sock_ev(&cli_ev2); + deinit_sock_ev(&srv_ev); + deinit_ctx_ev(&cli_ctx_ev); + deinit_ctx_ev(&cli_ctx_ev2); + deinit_ctx_ev(&srv_ctx_ev); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(socket_test_5, test_bad_params) +#else +static void test_bad_params(void) +#endif +{ + BSC_SC_RET ret; + BSC_SOCKET_CTX srv_ctx; + BSC_CONTEXT_CFG server_cfg; + BSC_SOCKET_CTX_FUNCS srv_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + srv_simple_socket_event, + srv_simple_context_event, NULL }; + BACNET_SC_UUID server_uuid; + BACNET_SC_VMAC_ADDRESS server_vmac; + BSC_CONTEXT_CFG client_cfg; + BSC_SOCKET_CTX cli_ctx; + BACNET_SC_UUID client_uuid; + BACNET_SC_VMAC_ADDRESS client_vmac; + BSC_SOCKET_CTX_FUNCS cli_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + cli_simple_socket_event, + cli_simple_context_event, NULL }; + char url[128]; + + memset(&srv_ctx, 0, sizeof(srv_ctx)); + memset(&cli_ctx, 0, sizeof(cli_ctx)); + memset(&server_uuid, 0x1, sizeof(server_uuid)); + memset(&server_vmac, 0x2, sizeof(server_vmac)); + memset(&client_uuid, 0x3, sizeof(server_uuid)); + memset(&client_vmac, 0x4, sizeof(server_vmac)); + sprintf( + url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + init_sock_ev(&srv_ev); + init_ctx_ev(&srv_ctx_ev); + init_sock_ev(&cli_ev); + init_ctx_ev(&cli_ctx_ev); + + // bad params bsc_init_ctx() test + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_ACCEPTOR, &server_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), &server_uuid, &server_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + ret = bsc_init_ctx( + NULL, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + + ret = bsc_init_ctx( + &srv_ctx, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + + ret = bsc_init_ctx( + &srv_ctx, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_INVALID_OPERATION, 0); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + bsc_deinit_ctx(&srv_ctx); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + + // dellocate already deallocated context + bsc_deinit_ctx(&srv_ctx); + + // bsc_connect bad param test + ret = bsc_connect(NULL, NULL, NULL); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, NULL, sizeof(ca_cert), + client_cert, sizeof(client_cert), CLIENT_KEY, sizeof(CLIENT_KEY), + &client_uuid, &client_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + ret = bsc_init_ctx( + &cli_ctx, &client_cfg, &cli_funcs, cli_socks, MAX_CLIENT_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_connect(&cli_ctx, &cli_socks[0], url); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + bsc_deinit_ctx(&cli_ctx); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + + // bsc_send bad params test + ret = bsc_send(NULL, NULL, 0); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + ret = bsc_send(&cli_socks[0], (uint8_t *)&client_uuid, sizeof(client_uuid)); + zassert_equal(ret, BSC_SC_INVALID_OPERATION, 0); + + ret = bsc_init_ctx( + &srv_ctx, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg, BSC_WEBSOCKET_DIRECT_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &client_uuid, &client_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + ret = bsc_init_ctx( + &cli_ctx, &client_cfg, &cli_funcs, cli_socks, MAX_CLIENT_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_connect(&cli_ctx, &cli_socks[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + zassert_equal(wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + ret = bsc_send( + &cli_socks[0], (uint8_t *)&client_uuid, + BSC_CONF_SOCK_TX_BUFFER_SIZE * 2); + zassert_equal(ret, BSC_SC_NO_RESOURCES, 0); + bsc_deinit_ctx(&cli_ctx); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + bsc_deinit_ctx(&srv_ctx); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + + // connect client to non existent server + ret = bsc_init_ctx( + &cli_ctx, &client_cfg, &cli_funcs, cli_socks, MAX_CLIENT_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_connect(&cli_ctx, &cli_socks[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal( + wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_DISCONNECTED), true, 0); + zassert_equal(cli_ev.err, ERROR_CODE_WEBSOCKET_ERROR, NULL); + bsc_deinit_ctx(&cli_ctx); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + + deinit_sock_ev(&srv_ev); + deinit_ctx_ev(&srv_ctx_ev); + deinit_sock_ev(&cli_ev); + deinit_ctx_ev(&cli_ctx_ev); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(socket_test_6, test_error_case1) +#else +static void test_error_case1(void) +#endif +{ + BSC_CONTEXT_CFG server_cfg; + BSC_CONTEXT_CFG client_cfg; + BACNET_SC_UUID server_uuid; + BACNET_SC_VMAC_ADDRESS server_vmac; + BACNET_SC_UUID client_uuid; + BACNET_SC_VMAC_ADDRESS client_vmac; + BSC_SC_RET ret; + BSC_SOCKET_CTX_FUNCS srv_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + srv_simple_socket_event, + srv_simple_context_event, NULL }; + + BSC_SOCKET_CTX_FUNCS cli_funcs = { simple_find_connection_for_vmac, + simple_find_connection_for_uuid, + cli_simple_socket_event, + cli_simple_context_event, NULL }; + + BSC_SOCKET_CTX srv_ctx; + BSC_SOCKET_CTX cli_ctx; + char url[128]; + uint8_t buf[2048]; + uint8_t npdu[1200]; + size_t len; + // BVLC_SC_DECODED_MESSAGE dpdu; + // BACNET_ERROR_CODE error; + // BACNET_ERROR_CLASS class; + // const char *err_desc; + + init_sock_ev(&cli_ev); + init_sock_ev(&srv_ev); + init_ctx_ev(&cli_ctx_ev); + init_ctx_ev(&srv_ctx_ev); + + memset(&srv_ctx, 0, sizeof(srv_ctx)); + memset(&cli_ctx, 0, sizeof(cli_ctx)); + memset(&server_uuid, 0x1, sizeof(server_uuid)); + memset(&server_vmac, 0x2, sizeof(server_vmac)); + memset(&client_uuid, 0x3, sizeof(server_uuid)); + memset(&client_vmac, 0x4, sizeof(server_vmac)); + sprintf( + url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_ACCEPTOR, &server_cfg, BSC_WEBSOCKET_HUB_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), &server_uuid, &server_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + bsc_init_ctx_cfg( + BSC_SOCKET_CTX_INITIATOR, &client_cfg, BSC_WEBSOCKET_HUB_PROTOCOL, + BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, ca_cert, + sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &client_uuid, &client_vmac, MAX_BVLC_LEN, + MAX_NDPU_LEN, BACNET_SOCKET_TIMEOUT, BACNET_SOCKET_HEARTBEAT_TIMEOUT, + BACNET_SOCKET_TIMEOUT); + + ret = bsc_init_ctx( + &srv_ctx, &server_cfg, &srv_funcs, srv_socks, MAX_SERVER_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_init_ctx( + &cli_ctx, &client_cfg, &cli_funcs, cli_socks, MAX_CLIENT_SOCKETS, NULL); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_INITIALIZED), true, 0); + ret = bsc_connect(&cli_ctx, &cli_socks[0], url); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + zassert_equal(wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + zassert_equal(wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_CONNECTED), true, 0); + + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 500, NULL, NULL, npdu, sizeof(npdu)); + ret = bsc_send(srv_sock, buf, len); + zassert_equal( + ret, BSC_SC_NO_RESOURCES, 0); /* BUG? should be BSC_SC_SUCCESS*/ + // zassert_equal(ret, BSC_SC_SUCCESS, 0); + // zassert_equal(wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_RECEIVED), true, 0); + // ret = bvlc_sc_decode_message( + // recv_buf, recv_buf_len, &dpdu, &error, &class, &err_desc); + // zassert_equal(ret, true, NULL); + // zassert_equal( + // dpdu.payload.result.error_code, ERROR_CODE_HEADER_ENCODING_ERROR, + // NULL); + // zassert_equal( + // dpdu.payload.result.error_class, ERROR_CLASS_COMMUNICATION, NULL); + // len = bvlc_sc_encode_encapsulated_npdu( + // buf, sizeof(buf), 505, NULL, NULL, npdu, sizeof(npdu)); + // ret = bsc_send(&cli_socks[0], buf, len); + // zassert_equal(ret, BSC_SC_SUCCESS, 0); + // zassert_equal(wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_RECEIVED), true, 0); + // ret = bvlc_sc_decode_message( + // recv_buf, recv_buf_len, &dpdu, &error, &class, &err_desc); + // zassert_equal(ret, true, NULL); + // zassert_equal( + // dpdu.payload.result.error_code, ERROR_CODE_HEADER_ENCODING_ERROR, + // NULL); + // zassert_equal( + // dpdu.payload.result.error_class, ERROR_CLASS_COMMUNICATION, NULL); + // len = bvlc_sc_encode_encapsulated_npdu( + // buf, sizeof(buf), 506, &server_vmac, &client_vmac, npdu, + // sizeof(npdu)); + // ret = bsc_send(srv_sock, buf, len); + // zassert_equal(ret, BSC_SC_SUCCESS, 0); + // zassert_equal(wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_RECEIVED), true, 0); + // ret = bvlc_sc_decode_message( + // recv_buf, recv_buf_len, &dpdu, &error, &class, &err_desc); + // zassert_equal(ret, true, NULL); + // zassert_equal( + // dpdu.payload.result.error_code, ERROR_CODE_HEADER_ENCODING_ERROR, + // NULL); + // zassert_equal( + // dpdu.payload.result.error_class, ERROR_CLASS_COMMUNICATION, NULL); + // len = bvlc_sc_encode_encapsulated_npdu( + // buf, sizeof(buf), 506, &server_vmac, &client_vmac, npdu, + // sizeof(npdu)); + // ret = bsc_send(&cli_socks[0], buf, len); + // zassert_equal(ret, BSC_SC_SUCCESS, 0); + // zassert_equal(wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_RECEIVED), true, 0); + // ret = bvlc_sc_decode_message( + // recv_buf, recv_buf_len, &dpdu, &error, &class, &err_desc); + // zassert_equal(ret, true, NULL); + // zassert_equal( + // dpdu.payload.result.error_code, ERROR_CODE_HEADER_ENCODING_ERROR, + // NULL); + // zassert_equal( + // dpdu.payload.result.error_class, ERROR_CLASS_COMMUNICATION, NULL); + bsc_disconnect(srv_sock); + zassert_equal( + wait_sock_ev(&srv_ev, BSC_SOCKET_EVENT_DISCONNECTED), true, 0); + zassert_equal( + wait_sock_ev(&cli_ev, BSC_SOCKET_EVENT_DISCONNECTED), true, 0); + bsc_deinit_ctx(&cli_ctx); + zassert_equal(wait_ctx_ev(&cli_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + bsc_deinit_ctx(&srv_ctx); + zassert_equal(wait_ctx_ev(&srv_ctx_ev, BSC_CTX_DEINITIALIZED), true, 0); + deinit_sock_ev(&cli_ev); + deinit_sock_ev(&srv_ev); + deinit_ctx_ev(&cli_ctx_ev); + deinit_ctx_ev(&srv_ctx_ev); +} + +#if defined(CONFIG_ZTEST_NEW_API) +static void *suite_setup(void) +{ + setbuf(stdout, NULL); + return NULL; +} + +ZTEST_SUITE(socket_test_1, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(socket_test_2, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(socket_test_3, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(socket_test_4, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(socket_test_5, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(socket_test_6, NULL, suite_setup, NULL, NULL, NULL); +#else +void test_main(void) +{ + // setbuf(stdout, NULL); + // Tests must not be run in parallel threads! + // Thats why tests functions are in different suites. + ztest_test_suite(socket_test_1, ztest_unit_test(test_simple)); + ztest_test_suite( + socket_test_2, ztest_unit_test(test_duplicated_vmac_on_server)); + ztest_test_suite( + socket_test_3, ztest_unit_test(test_duplicated_vmac_on_server2)); + ztest_test_suite( + socket_test_4, ztest_unit_test(test_duplicated_uuid_on_server)); + ztest_test_suite(socket_test_5, ztest_unit_test(test_bad_params)); + ztest_test_suite(socket_test_6, ztest_unit_test(test_error_case1)); + ztest_run_test_suite(socket_test_1); + ztest_run_test_suite(socket_test_2); + ztest_run_test_suite(socket_test_3); + ztest_run_test_suite(socket_test_4); + ztest_run_test_suite(socket_test_5); + ztest_run_test_suite(socket_test_6); +} +#endif diff --git a/test/bacnet/datalink/bvlc-sc/CMakeLists.txt b/test/bacnet/datalink/bvlc-sc/CMakeLists.txt new file mode 100644 index 00000000..9e8255d2 --- /dev/null +++ b/test/bacnet/datalink/bvlc-sc/CMakeLists.txt @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BSC_CONF_TX_PRE=0 + BACNET_STACK_STATIC_DEFINE + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +add_executable(${PROJECT_NAME} + # File(s) under test + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + # Support files and stubs (pathname alphabetical) + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + +if (CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "AppleClang" OR CMAKE_C_COMPILER_ID MATCHES "GNU") + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) +endif() diff --git a/test/bacnet/datalink/bvlc-sc/src/main.c b/test/bacnet/datalink/bvlc-sc/src/main.c new file mode 100644 index 00000000..45324a25 --- /dev/null +++ b/test/bacnet/datalink/bvlc-sc/src/main.c @@ -0,0 +1,4431 @@ +/** + * @file + * @brief tests for BACnet/SC encode/decode APIs + * @author Kirill Neznamov + * @date May 2022 + * @copyright SPDX-License-Identifier: MIT + */ +#include /* For calloc() */ +#include +#include + +static bool verify_bsc_bvll_header( + BVLC_SC_DECODED_HDR *hdr, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + bool dest_options_absent, + bool data_options_absent, + size_t payload_len) +{ + if (hdr->bvlc_function != bvlc_function) { + return false; + } + + if (hdr->message_id != message_id) { + return false; + } + + if (origin) { + if (memcmp(hdr->origin->address, origin->address, BVLC_SC_VMAC_SIZE) != + 0) { + return false; + } + } else { + if (hdr->origin) { + return false; + } + } + + if (dest) { + if (memcmp(hdr->dest->address, dest->address, BVLC_SC_VMAC_SIZE) != 0) { + return false; + } + } else { + if (hdr->dest) { + return false; + } + } + + if (dest_options_absent) { + if (hdr->dest_options != NULL) { + return false; + } + if (hdr->dest_options_len != 0) { + return false; + } + if (hdr->dest_options_num != 0) { + return false; + } + } else { + if (!hdr->dest_options) { + return false; + } + if (!hdr->dest_options_len) { + return false; + } + if (!hdr->dest_options_num) { + return false; + } + } + + if (data_options_absent) { + if (hdr->data_options != NULL) { + return false; + } + if (hdr->data_options_len != 0) { + return false; + } + if (hdr->data_options_num != 0) { + return false; + } + } else { + if (!hdr->data_options) { + return false; + } + if (!hdr->data_options_len) { + return false; + } + if (!hdr->data_options_num) { + return false; + } + } + + if (hdr->payload_len != payload_len) { + return false; + } + + return true; +} + +static void test_header_modifications( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len, + bool dest_options_absent, + bool data_options_absent) +{ + uint8_t sbuf[BSC_PRE + 256]; + uint8_t *buf = &sbuf[BSC_PRE]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc = NULL; + size_t len; + bool ret; + BACNET_SC_VMAC_ADDRESS test; + BACNET_SC_VMAC_ADDRESS test_dest; + uint8_t *ppdu; + int res; + + memset(&test.address[0], 0x99, sizeof(test.address)); + memcpy(buf, pdu, pdu_size); + + if (dest && !origin) { + bvlc_sc_remove_dest_set_orig(buf, pdu_size, &test); + ret = bvlc_sc_decode_message( + buf, pdu_size, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, &test, NULL, + dest_options_absent, data_options_absent, payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + zassert_equal( + memcmp(&test.address, message.hdr.origin, sizeof(test.address)), 0, + NULL); + zassert_equal(message.hdr.dest, NULL, NULL); + } else { + bvlc_sc_remove_dest_set_orig(buf, pdu_size, &test); + ret = bvlc_sc_decode_message( + buf, pdu_size, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, + dest_options_absent, data_options_absent, payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + + if (origin) { + zassert_equal( + memcmp( + &origin->address, message.hdr.origin, + sizeof(origin->address)), + 0, NULL); + } else { + zassert_equal(message.hdr.origin, NULL, NULL); + } + if (dest) { + zassert_equal( + memcmp(&dest->address, message.hdr.dest, sizeof(dest->address)), + 0, NULL); + zassert_equal(bvlc_sc_pdu_has_no_dest(buf, pdu_size), false, NULL); + ret = bvlc_sc_pdu_get_dest(buf, pdu_size, &test_dest); + zassert_equal(ret, true, NULL); + zassert_equal( + memcmp( + &test_dest.address, message.hdr.dest, + sizeof(dest->address)), + 0, NULL); + } else { + zassert_equal(message.hdr.dest, NULL, NULL); + zassert_equal(bvlc_sc_pdu_has_no_dest(buf, pdu_size), true, NULL); + ret = bvlc_sc_pdu_get_dest(buf, pdu_size, &test_dest); + zassert_equal(ret, false, NULL); + } + } + + memcpy(buf, pdu, pdu_size); + ppdu = buf; + + /* origin address can presented only in specific message types */ + + if (bvlc_function != BVLC_SC_CONNECT_REQUEST && + bvlc_function != BVLC_SC_CONNECT_ACCEPT && + bvlc_function != BVLC_SC_DISCONNECT_REQUEST && + bvlc_function != BVLC_SC_DISCONNECT_ACK && + bvlc_function != BVLC_SC_HEARTBEAT_REQUEST && + bvlc_function != BVLC_SC_HEARTBEAT_ACK) { + len = bvlc_sc_set_orig(&ppdu, pdu_size, &test); + ret = bvlc_sc_decode_message( + ppdu, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, &test, dest, + dest_options_absent, data_options_absent, payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + zassert_equal( + memcmp(&test.address, message.hdr.origin, sizeof(test.address)), 0, + NULL); + } else { + len = pdu_size; + } + + memcpy(buf, pdu, pdu_size); + ppdu = buf; + len = bvlc_sc_remove_orig_and_dest(&ppdu, pdu_size); + ret = + bvlc_sc_decode_message(ppdu, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.origin, NULL, NULL); + zassert_equal(message.hdr.dest, NULL, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, NULL, NULL, + dest_options_absent, data_options_absent, payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); +} + +static void test_1_option_data( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc = NULL; + size_t optlen; + size_t len; + bool ret; + int res; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, pdu_size, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, true, false, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.data_options_num, 1, NULL); + zassert_equal( + message.data_options[0].type, BVLC_SC_OPTION_TYPE_SECURE_PATH, NULL); + zassert_equal(message.data_options[0].must_understand, true, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + test_header_modifications( + buf, len, bvlc_function, message_id, origin, dest, payload, payload_len, + true, false); +} + +/* 3 options are added to pdu in total: 1 sc, 2 proprietary */ + +static void test_3_options_different_buffer_data( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t buf1[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[1]; + int res; + const char *err_desc = NULL; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf1, sizeof(buf1), buf, pdu_size, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf1, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf1, sizeof(buf1), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = + bvlc_sc_decode_message(buf1, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, true, false, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.data_options_num, 3, NULL); + + zassert_equal( + message.data_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[0].must_understand, true, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[0].specific.proprietary.vendor_id, vendor_id2, + NULL); + zassert_equal( + message.data_options[0].specific.proprietary.option_type, + proprietary_option_type2, NULL); + zassert_equal( + message.data_options[0].specific.proprietary.data_len, + sizeof(proprietary_data2), NULL); + res = memcmp( + message.data_options[0].specific.proprietary.data, proprietary_data2, + sizeof(proprietary_data2)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.data_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[1].must_understand, true, NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[2].type, BVLC_SC_OPTION_TYPE_SECURE_PATH, NULL); + zassert_equal(message.data_options[2].must_understand, true, NULL); + zassert_equal( + message.data_options[2].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.data_options[2].packed_header_marker & BVLC_SC_HEADER_DATA, 0, + NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); +} + +/* 3 options are added to pdu in total: 1 sc, 2 proprietary */ + +static void test_3_options_data( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[1]; + int res; + const char *err_desc = NULL; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, true, false, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.data_options_num, 3, NULL); + zassert_equal( + message.data_options[2].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[2].must_understand, true, NULL); + zassert_equal( + message.data_options[2].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.data_options[2].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[2].specific.proprietary.vendor_id, vendor_id2, + NULL); + zassert_equal( + message.data_options[2].specific.proprietary.option_type, + proprietary_option_type2, NULL); + zassert_equal( + message.data_options[2].specific.proprietary.data_len, + sizeof(proprietary_data2), NULL); + res = memcmp( + message.data_options[2].specific.proprietary.data, proprietary_data2, + sizeof(proprietary_data2)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.data_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[1].must_understand, true, NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[0].type, BVLC_SC_OPTION_TYPE_SECURE_PATH, NULL); + zassert_equal(message.data_options[0].must_understand, true, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, 0, + NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + test_header_modifications( + buf, len, bvlc_function, message_id, origin, dest, payload, payload_len, + true, false); +} + +static void test_5_options_data( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len; + bool ret; + uint16_t vendor_id1; + uint8_t proprietary_option_type1; + uint8_t proprietary_data1[17]; + size_t i; + const char *err_desc = NULL; + (void)bvlc_function; + (void)message_id; + (void)origin; + (void)dest; + (void)payload; + (void)payload_len; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = pdu_size; + for (i = 0; i < 5; i++) { + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + } + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_OUT_OF_MEMORY, NULL); + zassert_equal(class, ERROR_CLASS_RESOURCES, NULL); +} + +/* check message decoding when header option has incorrect more bit flag */ + +static void test_options_incorrect_more_bit_data( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[5]; + size_t offs = 4; + const char *err_desc = NULL; + (void)bvlc_function; + (void)message_id; + (void)origin; + (void)dest; + (void)payload; + (void)payload_len; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + if (buf[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + if (buf[1] & BVLC_SC_CONTROL_DEST_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + buf[offs] &= ~(BVLC_SC_HEADER_MORE); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_UNEXPECTED_DATA, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +/* check message decoding when header option has incorrect more bit flag */ + +static void test_options_incorrect_data_bit_data( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint8_t proprietary_option_type1; + uint8_t proprietary_data1[17]; + size_t offs = 4; + const char *err_desc = NULL; + (void)bvlc_function; + (void)message_id; + (void)origin; + (void)dest; + (void)payload; + (void)payload_len; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + if (buf[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + if (buf[1] & BVLC_SC_CONTROL_DEST_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + buf[offs] &= ~(BVLC_SC_HEADER_DATA); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +/* secure path option must not be in dest option headers. this test + checks this case */ + +static void test_1_option_dest_incorrect( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len; + bool ret; + const char *err_desc = NULL; + (void)bvlc_function; + (void)message_id; + (void)origin; + (void)dest; + (void)payload; + (void)payload_len; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, pdu_size, optbuf, optlen); + zassert_equal(len, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, pdu_size, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + buf[1] &= ~(BVLC_SC_CONTROL_DATA_OPTIONS); + buf[1] |= BVLC_SC_CONTROL_DEST_OPTIONS; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +static void test_1_option_dest( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len; + bool ret; + int res; + uint16_t vendor_id1; + uint8_t proprietary_option_type1; + uint8_t proprietary_data1[17]; + const char *err_desc = NULL; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, pdu_size, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, false, true, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.dest_options_num, 1, NULL); + zassert_equal( + message.dest_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[0].must_understand, true, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.vendor_id, vendor_id1, + NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.option_type, + proprietary_option_type1, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.data_len, + sizeof(proprietary_data1), NULL); + res = memcmp( + message.dest_options[0].specific.proprietary.data, proprietary_data1, + sizeof(proprietary_data1)); + zassert_equal(res, 0, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + test_header_modifications( + buf, len, bvlc_function, message_id, origin, dest, payload, payload_len, + false, true); +} + +/* 3 proprietary options are added to pdu */ + +static void test_3_options_dest( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint16_t vendor_id3; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_option_type3; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[1]; + uint8_t proprietary_data3[43]; + int res; + const char *err_desc = NULL; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + + vendor_id3 = 0xF00D; + proprietary_option_type3 = 0x08; + memset(proprietary_data3, 0x55, sizeof(proprietary_data3)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id3, proprietary_option_type3, + proprietary_data3, sizeof(proprietary_data3)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, false, true, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.dest_options_num, 3, NULL); + zassert_equal( + message.dest_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[0].must_understand, true, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.vendor_id, vendor_id3, + NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.option_type, + proprietary_option_type3, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.data_len, + sizeof(proprietary_data3), NULL); + res = memcmp( + message.dest_options[0].specific.proprietary.data, proprietary_data3, + sizeof(proprietary_data3)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.dest_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[1].must_understand, true, NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.vendor_id, vendor_id2, + NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.option_type, + proprietary_option_type2, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.data_len, + sizeof(proprietary_data2), NULL); + res = memcmp( + message.dest_options[1].specific.proprietary.data, proprietary_data2, + sizeof(proprietary_data2)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.dest_options[2].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[2].must_understand, true, NULL); + zassert_equal( + message.dest_options[2].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.dest_options[2].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[2].specific.proprietary.vendor_id, vendor_id1, + NULL); + zassert_equal( + message.dest_options[2].specific.proprietary.option_type, + proprietary_option_type1, NULL); + zassert_equal( + message.dest_options[2].specific.proprietary.data_len, + sizeof(proprietary_data1), NULL); + res = memcmp( + message.dest_options[2].specific.proprietary.data, proprietary_data1, + sizeof(proprietary_data1)); + zassert_equal(res, 0, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + test_header_modifications( + buf, len, bvlc_function, message_id, origin, dest, payload, payload_len, + false, true); +} + +static void test_3_options_dest_different_buffer( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf1[256]; + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint16_t vendor_id3; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_option_type3; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[1]; + uint8_t proprietary_data3[43]; + int res; + const char *err_desc = NULL; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf1, sizeof(buf1), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf1, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + + vendor_id3 = 0xF00D; + proprietary_option_type3 = 0x08; + memset(proprietary_data3, 0x55, sizeof(proprietary_data3)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id3, proprietary_option_type3, + proprietary_data3, sizeof(proprietary_data3)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf1, sizeof(buf1), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = + bvlc_sc_decode_message(buf1, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, false, true, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.dest_options_num, 3, NULL); + zassert_equal( + message.dest_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[0].must_understand, true, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.vendor_id, vendor_id3, + NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.option_type, + proprietary_option_type3, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.data_len, + sizeof(proprietary_data3), NULL); + res = memcmp( + message.dest_options[0].specific.proprietary.data, proprietary_data3, + sizeof(proprietary_data3)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.dest_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[1].must_understand, true, NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.vendor_id, vendor_id2, + NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.option_type, + proprietary_option_type2, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.data_len, + sizeof(proprietary_data2), NULL); + res = memcmp( + message.dest_options[1].specific.proprietary.data, proprietary_data2, + sizeof(proprietary_data2)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.dest_options[2].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[2].must_understand, true, NULL); + zassert_equal( + message.dest_options[2].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.dest_options[2].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[2].specific.proprietary.vendor_id, vendor_id1, + NULL); + zassert_equal( + message.dest_options[2].specific.proprietary.option_type, + proprietary_option_type1, NULL); + zassert_equal( + message.dest_options[2].specific.proprietary.data_len, + sizeof(proprietary_data1), NULL); + res = memcmp( + message.dest_options[2].specific.proprietary.data, proprietary_data1, + sizeof(proprietary_data1)); + zassert_equal(res, 0, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + test_header_modifications( + buf1, len, bvlc_function, message_id, origin, dest, payload, + payload_len, false, true); +} + +static void test_5_options_dest( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint8_t proprietary_option_type1; + uint8_t proprietary_data1[17]; + size_t i; + const char *err_desc = NULL; + (void)bvlc_function; + (void)message_id; + (void)origin; + (void)dest; + (void)payload; + (void)payload_len; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = pdu_size; + for (i = 0; i < 5; i++) { + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + } + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_OUT_OF_MEMORY, NULL); + zassert_equal(class, ERROR_CLASS_RESOURCES, NULL); +} + +static void test_options_incorrect_data_bit_dest( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint8_t proprietary_option_type1; + uint8_t proprietary_data1[17]; + int offs = 4; + const char *err_desc = NULL; + (void)bvlc_function; + (void)message_id; + (void)origin; + (void)dest; + (void)payload; + (void)payload_len; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + if (buf[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + if (buf[1] & BVLC_SC_CONTROL_DEST_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + buf[offs] &= ~(BVLC_SC_HEADER_DATA); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +static void test_options_incorrect_more_bit_dest( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[5]; + size_t offs = 4; + const char *err_desc = NULL; + (void)bvlc_function; + (void)message_id; + (void)origin; + (void)dest; + (void)payload; + (void)payload_len; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0x0; + proprietary_option_type1 = 0x0; + memset(proprietary_data1, 0x0, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + if (buf[1] & BVLC_SC_CONTROL_ORIG_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + if (buf[1] & BVLC_SC_CONTROL_DEST_VADDR) { + offs += BVLC_SC_VMAC_SIZE; + } + + buf[offs] &= ~(BVLC_SC_HEADER_MORE); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + if (error != ERROR_CODE_UNEXPECTED_DATA && + error != ERROR_CODE_PARAMETER_OUT_OF_RANGE) { + zassert_equal(1, 2, NULL); + } + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +static void test_options_mixed_case1( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint16_t vendor_id3; + uint16_t vendor_id4; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_option_type3; + uint8_t proprietary_option_type4; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[1]; + uint8_t proprietary_data3[43]; + uint8_t proprietary_data4[23]; + int res; + const char *err_desc = NULL; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + + vendor_id3 = 0xF00D; + proprietary_option_type3 = 0x08; + memset(proprietary_data3, 0x55, sizeof(proprietary_data3)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id3, proprietary_option_type3, + proprietary_data3, sizeof(proprietary_data3)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id4 = 0xabcd; + proprietary_option_type4 = 0x64; + memset(proprietary_data4, 0x97, sizeof(proprietary_data4)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id4, proprietary_option_type4, + proprietary_data4, sizeof(proprietary_data4)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, false, false, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.dest_options_num, 2, NULL); + zassert_equal(message.hdr.data_options_num, 2, NULL); + zassert_equal( + message.dest_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[0].must_understand, true, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.vendor_id, vendor_id3, + NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.option_type, + proprietary_option_type3, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.data_len, + sizeof(proprietary_data3), NULL); + res = memcmp( + message.dest_options[0].specific.proprietary.data, proprietary_data3, + sizeof(proprietary_data3)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.dest_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[1].must_understand, true, NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.vendor_id, vendor_id1, + NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.option_type, + proprietary_option_type1, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.data_len, + sizeof(proprietary_data1), NULL); + res = memcmp( + message.dest_options[1].specific.proprietary.data, proprietary_data1, + sizeof(proprietary_data1)); + zassert_equal(res, 0, NULL); + /* data options */ + + zassert_equal( + message.data_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[0].must_understand, true, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[0].specific.proprietary.vendor_id, vendor_id4, + NULL); + zassert_equal( + message.data_options[0].specific.proprietary.option_type, + proprietary_option_type4, NULL); + zassert_equal( + message.data_options[0].specific.proprietary.data_len, + sizeof(proprietary_data4), NULL); + res = memcmp( + message.data_options[0].specific.proprietary.data, proprietary_data4, + sizeof(proprietary_data4)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.data_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[1].must_understand, true, NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[1].specific.proprietary.vendor_id, vendor_id2, + NULL); + zassert_equal( + message.data_options[1].specific.proprietary.option_type, + proprietary_option_type2, NULL); + zassert_equal( + message.data_options[1].specific.proprietary.data_len, + sizeof(proprietary_data2), NULL); + res = memcmp( + message.data_options[1].specific.proprietary.data, proprietary_data2, + sizeof(proprietary_data2)); + zassert_equal(res, 0, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + test_header_modifications( + buf, len, bvlc_function, message_id, origin, dest, payload, payload_len, + false, false); +} + +static void test_options_mixed_case2( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint16_t vendor_id3; + uint16_t vendor_id4; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_option_type3; + uint8_t proprietary_option_type4; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[1]; + uint8_t proprietary_data3[43]; + uint8_t proprietary_data4[23]; + int res; + const char *err_desc = NULL; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + + vendor_id3 = 0xF00D; + proprietary_option_type3 = 0x08; + memset(proprietary_data3, 0x55, sizeof(proprietary_data3)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id3, proprietary_option_type3, + proprietary_data3, sizeof(proprietary_data3)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id4 = 0xabcd; + proprietary_option_type4 = 0x64; + memset(proprietary_data4, 0x97, sizeof(proprietary_data4)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id4, proprietary_option_type4, + proprietary_data4, sizeof(proprietary_data4)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, false, false, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.dest_options_num, 2, NULL); + zassert_equal(message.hdr.data_options_num, 2, NULL); + zassert_equal( + message.dest_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[0].must_understand, true, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.vendor_id, vendor_id4, + NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.option_type, + proprietary_option_type4, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.data_len, + sizeof(proprietary_data4), NULL); + res = memcmp( + message.dest_options[0].specific.proprietary.data, proprietary_data4, + sizeof(proprietary_data4)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.dest_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[1].must_understand, true, NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.vendor_id, vendor_id3, + NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.option_type, + proprietary_option_type3, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.data_len, + sizeof(proprietary_data3), NULL); + res = memcmp( + message.dest_options[1].specific.proprietary.data, proprietary_data3, + sizeof(proprietary_data3)); + zassert_equal(res, 0, NULL); + /* data options */ + + zassert_equal( + message.data_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[0].must_understand, true, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[0].specific.proprietary.vendor_id, vendor_id2, + NULL); + zassert_equal( + message.data_options[0].specific.proprietary.option_type, + proprietary_option_type2, NULL); + zassert_equal( + message.data_options[0].specific.proprietary.data_len, + sizeof(proprietary_data2), NULL); + res = memcmp( + message.data_options[0].specific.proprietary.data, proprietary_data2, + sizeof(proprietary_data2)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.data_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[1].must_understand, true, NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[1].specific.proprietary.vendor_id, vendor_id1, + NULL); + zassert_equal( + message.data_options[1].specific.proprietary.option_type, + proprietary_option_type1, NULL); + zassert_equal( + message.data_options[1].specific.proprietary.data_len, + sizeof(proprietary_data1), NULL); + res = memcmp( + message.data_options[1].specific.proprietary.data, proprietary_data1, + sizeof(proprietary_data1)); + zassert_equal(res, 0, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + test_header_modifications( + buf, len, bvlc_function, message_id, origin, dest, payload, payload_len, + false, false); +} + +static void test_options_mixed_case3( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + uint8_t *payload, + size_t payload_len) +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + size_t optlen; + size_t len = pdu_size; + bool ret; + uint16_t vendor_id1; + uint16_t vendor_id2; + uint16_t vendor_id3; + uint16_t vendor_id4; + uint8_t proprietary_option_type1; + uint8_t proprietary_option_type2; + uint8_t proprietary_option_type3; + uint8_t proprietary_option_type4; + uint8_t proprietary_data1[17]; + uint8_t proprietary_data2[1]; + uint8_t proprietary_data3[43]; + uint8_t proprietary_data4[23]; + int res; + const char *err_desc = NULL; + + zassert_equal(true, sizeof(buf) >= pdu_size ? true : false, NULL); + memcpy(buf, pdu, pdu_size); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id2 = 0xbeaf; + proprietary_option_type2 = 0x33; + memset(proprietary_data2, 0x11, sizeof(proprietary_data2)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id2, proprietary_option_type2, + proprietary_data2, sizeof(proprietary_data2)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + + vendor_id3 = 0xF00D; + proprietary_option_type3 = 0x08; + memset(proprietary_data3, 0x55, sizeof(proprietary_data3)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id3, proprietary_option_type3, + proprietary_data3, sizeof(proprietary_data3)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + vendor_id4 = 0xabcd; + proprietary_option_type4 = 0x64; + memset(proprietary_data4, 0x97, sizeof(proprietary_data4)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id4, proprietary_option_type4, + proprietary_data4, sizeof(proprietary_data4)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, bvlc_function, message_id, origin, dest, false, false, + payload_len); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.dest_options_num, 2, NULL); + zassert_equal(message.hdr.data_options_num, 2, NULL); + zassert_equal( + message.dest_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[0].must_understand, true, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.dest_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.vendor_id, vendor_id2, + NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.option_type, + proprietary_option_type2, NULL); + zassert_equal( + message.dest_options[0].specific.proprietary.data_len, + sizeof(proprietary_data2), NULL); + res = memcmp( + message.dest_options[0].specific.proprietary.data, proprietary_data2, + sizeof(proprietary_data2)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.dest_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.dest_options[1].must_understand, true, NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.dest_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.vendor_id, vendor_id1, + NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.option_type, + proprietary_option_type1, NULL); + zassert_equal( + message.dest_options[1].specific.proprietary.data_len, + sizeof(proprietary_data1), NULL); + res = memcmp( + message.dest_options[1].specific.proprietary.data, proprietary_data1, + sizeof(proprietary_data1)); + zassert_equal(res, 0, NULL); + /* data options */ + + zassert_equal( + message.data_options[0].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[0].must_understand, true, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_MORE, + BVLC_SC_HEADER_MORE, NULL); + zassert_equal( + message.data_options[0].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[0].specific.proprietary.vendor_id, vendor_id4, + NULL); + zassert_equal( + message.data_options[0].specific.proprietary.option_type, + proprietary_option_type4, NULL); + zassert_equal( + message.data_options[0].specific.proprietary.data_len, + sizeof(proprietary_data4), NULL); + res = memcmp( + message.data_options[0].specific.proprietary.data, proprietary_data4, + sizeof(proprietary_data4)); + zassert_equal(res, 0, NULL); + zassert_equal( + message.data_options[1].type, BVLC_SC_OPTION_TYPE_PROPRIETARY, NULL); + zassert_equal(message.data_options[1].must_understand, true, NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_MORE, 0, + NULL); + zassert_equal( + message.data_options[1].packed_header_marker & BVLC_SC_HEADER_DATA, + BVLC_SC_HEADER_DATA, NULL); + zassert_equal( + message.data_options[1].specific.proprietary.vendor_id, vendor_id3, + NULL); + zassert_equal( + message.data_options[1].specific.proprietary.option_type, + proprietary_option_type3, NULL); + zassert_equal( + message.data_options[1].specific.proprietary.data_len, + sizeof(proprietary_data3), NULL); + res = memcmp( + message.data_options[1].specific.proprietary.data, proprietary_data3, + sizeof(proprietary_data3)); + zassert_equal(res, 0, NULL); + zassert_equal(message.hdr.payload_len, payload_len, NULL); + res = memcmp(message.hdr.payload, payload, payload_len); + zassert_equal(res, 0, NULL); + test_header_modifications( + buf, len, bvlc_function, message_id, origin, dest, payload, payload_len, + false, false); +} + +static void test_options( + uint8_t *pdu, + size_t pdu_size, + uint8_t bvlc_function, + uint16_t message_id, + BACNET_SC_VMAC_ADDRESS *origin, + BACNET_SC_VMAC_ADDRESS *dest, + bool test_dest_option, + bool test_data_option, + uint8_t *payload, + size_t payload_len, + bool ignore_more_bit_test) +{ + if (!test_dest_option && test_data_option) { + test_1_option_data( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_3_options_data( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_3_options_different_buffer_data( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_5_options_data( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + if (!ignore_more_bit_test) { + test_options_incorrect_more_bit_data( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + } + test_options_incorrect_data_bit_data( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + } else if (test_dest_option && !test_data_option) { + test_1_option_dest_incorrect( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_1_option_dest( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_3_options_dest( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_3_options_dest_different_buffer( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_5_options_dest( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + if (!ignore_more_bit_test) { + test_options_incorrect_more_bit_dest( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + } + test_options_incorrect_data_bit_dest( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + } else if (test_dest_option && test_data_option) { + test_options_mixed_case1( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_options_mixed_case2( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + test_options_mixed_case3( + pdu, pdu_size, bvlc_function, message_id, origin, dest, payload, + payload_len); + } +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_BVLC_RESULT) +#else +static void test_BVLC_RESULT(void) +#endif +{ + uint8_t sbuf[256 + BSC_PRE]; + uint8_t *buf = &sbuf[BSC_PRE]; + int buf_len = 256; + size_t len; + uint8_t optbuf[256]; + size_t optlen; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS new_origin; + BACNET_SC_VMAC_ADDRESS dest; + BACNET_SC_VMAC_ADDRESS test_dest; + uint16_t message_id = 0x7777; + uint8_t result_bvlc_function = 3; + bool ret; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint8_t error_header_marker = 0xcc; + uint16_t error_class = 0xaa; + uint16_t error_code = 0xdd; + char error_details_string[100]; + const char *err_desc = NULL; + uint8_t *pdu; + + sprintf(error_details_string, "%s", "something bad has happend"); + + memset(origin.address, 0x23, BVLC_SC_VMAC_SIZE); + memset(dest.address, 0x44, BVLC_SC_VMAC_SIZE); + memset(new_origin.address, 0x93, BVLC_SC_VMAC_SIZE); + + /* origin and dest presented */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, &origin, &dest, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, &origin, &dest, true, true, + 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, &origin, &dest, true, false, + message.hdr.payload, message.hdr.payload_len, false); + ret = bvlc_sc_pdu_has_no_dest(buf, len); + zassert_equal(ret, false, NULL); + memset(&test_dest, 0, sizeof(test_dest)); + ret = bvlc_sc_pdu_get_dest(buf, len, &test_dest); + zassert_equal(ret, true, NULL); + zassert_equal( + 0, memcmp(&dest.address, &test_dest.address, sizeof(test_dest.address)), + NULL); + + pdu = buf; + len = bvlc_sc_set_orig(&pdu, len, &new_origin); + ret = bvlc_sc_decode_message(pdu, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, &new_origin, &dest, true, + true, 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + pdu, len, BVLC_SC_RESULT, message_id, &new_origin, &dest, true, false, + message.hdr.payload, message.hdr.payload_len, false); + pdu = buf; + len = bvlc_sc_remove_orig_and_dest(&pdu, len); + ret = bvlc_sc_decode_message(pdu, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, NULL, NULL, true, true, 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + pdu, len, BVLC_SC_RESULT, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + + /* origin presented */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, &origin, NULL, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_pdu_has_no_dest(buf, len); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, &origin, NULL, true, true, 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, &origin, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + pdu = buf; + len = bvlc_sc_remove_orig_and_dest(&pdu, len); + ret = bvlc_sc_decode_message(pdu, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, NULL, NULL, true, true, 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + pdu, len, BVLC_SC_RESULT, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + + /* dest presented */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, &dest, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, NULL, &dest, true, true, 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, NULL, &dest, true, false, + message.hdr.payload, message.hdr.payload_len, false); + ret = bvlc_sc_pdu_has_no_dest(buf, len); + zassert_equal(ret, false, NULL); + memset(&test_dest, 0, sizeof(test_dest)); + ret = bvlc_sc_pdu_get_dest(buf, len, &test_dest); + zassert_equal(ret, true, NULL); + zassert_equal( + 0, memcmp(&dest.address, &test_dest.address, sizeof(test_dest.address)), + NULL); + pdu = buf; + len = bvlc_sc_set_orig(&pdu, len, &new_origin); + ret = bvlc_sc_decode_message(pdu, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, &new_origin, &dest, true, + true, 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + pdu, len, BVLC_SC_RESULT, message_id, &new_origin, &dest, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, &dest, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + pdu = buf; + len = bvlc_sc_remove_orig_and_dest(&pdu, len); + ret = bvlc_sc_decode_message(pdu, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, NULL, NULL, true, true, 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + pdu, len, BVLC_SC_RESULT, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, &dest, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + + bvlc_sc_remove_dest_set_orig(buf, len, &new_origin); + + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, &new_origin, NULL, true, true, + 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, &new_origin, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + + /* dest and origin absent */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_pdu_has_no_dest(buf, len); + zassert_equal(ret, true, NULL); + memset(&test_dest, 0, sizeof(test_dest)); + ret = bvlc_sc_pdu_get_dest(buf, len, &test_dest); + zassert_equal(ret, false, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, NULL, NULL, true, true, 2); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 0, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + + /* nak, no details string */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 1, + &error_header_marker, &error_class, &error_code, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, NULL, NULL, true, true, 7); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 1, NULL); + zassert_equal( + message.payload.result.error_header_marker, error_header_marker, NULL); + zassert_equal(message.payload.result.error_class, error_class, NULL); + zassert_equal(message.payload.result.error_code, error_code, NULL); + zassert_equal(message.payload.result.utf8_details_string, NULL, NULL); + zassert_equal(message.payload.result.utf8_details_string_len, 0, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + + /* nak , details string */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 1, + &error_header_marker, &error_class, &error_code, + (uint8_t *)error_details_string); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, NULL, NULL, true, true, + 7 + strlen(error_details_string)); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 1, NULL); + zassert_equal( + message.payload.result.error_header_marker, error_header_marker, NULL); + zassert_equal(message.payload.result.error_class, error_class, NULL); + zassert_equal(message.payload.result.error_code, error_code, NULL); + zassert_equal( + message.payload.result.utf8_details_string_len, + strlen(error_details_string), NULL); + ret = memcmp( + message.payload.result.utf8_details_string, error_details_string, + strlen(error_details_string)) == 0 + ? true + : false; + zassert_equal(ret, true, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + + /* dest and origin, nak , details string */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, &origin, &dest, result_bvlc_function, 1, + &error_header_marker, &error_class, &error_code, + (uint8_t *)error_details_string); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_RESULT, message_id, &origin, &dest, true, true, + 7 + strlen(error_details_string)); + zassert_equal(ret, true, NULL); + zassert_equal( + message.payload.result.bvlc_function, result_bvlc_function, NULL); + zassert_equal(message.payload.result.result, 1, NULL); + zassert_equal( + message.payload.result.error_header_marker, error_header_marker, NULL); + zassert_equal(message.payload.result.error_class, error_class, NULL); + zassert_equal(message.payload.result.error_code, error_code, NULL); + zassert_equal( + message.payload.result.utf8_details_string_len, + strlen(error_details_string), NULL); + ret = memcmp( + message.payload.result.utf8_details_string, error_details_string, + strlen(error_details_string)) == 0 + ? true + : false; + zassert_equal(ret, true, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, &origin, &dest, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + + /* truncated message, case 1 */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 1, + &error_header_marker, &error_class, &error_code, + (uint8_t *)error_details_string); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + + /* origin and dest absent, result ok*/ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + test_options( + buf, len, BVLC_SC_RESULT, message_id, NULL, NULL, true, false, + message.hdr.payload, 2, false); + + /* data option test */ + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, buf_len, buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_INCONSISTENT_PARAMETERS, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* zero payload test */ + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PAYLOAD_EXPECTED, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* bad result code */ + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + buf[5] = 4; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PARAMETER_OUT_OF_RANGE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* utf-8 string with zero symbol inside */ + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 1, + &error_header_marker, &error_class, &error_code, + (uint8_t *)error_details_string); + zassert_not_equal(len, 0, NULL); + buf[13] = 0; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_INCONSISTENT_PARAMETERS, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* bad payload case 1 */ + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 1, + &error_header_marker, &error_class, &error_code, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 10, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* bad payload case 2 */ + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* bad payload case 3 */ + memset(buf, 0, buf_len); + memset(&message, 0, sizeof(message)); + len = bvlc_sc_encode_result( + buf, buf_len, message_id, NULL, NULL, result_bvlc_function, 0, NULL, + NULL, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 7, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_UNEXPECTED_DATA, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_ENCAPSULATED_NPDU) +#else +static void test_ENCAPSULATED_NPDU(void) +#endif +{ + uint8_t buf[256]; + size_t len; + uint8_t npdu[256]; + size_t npdulen; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x1789; + int res; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS dest; + bool ret; + const char *err_desc = NULL; + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + memset(&origin.address, 0x63, BVLC_SC_VMAC_SIZE); + memset(&dest.address, 0x24, BVLC_SC_VMAC_SIZE); + memset(npdu, 0x99, sizeof(npdu)); + npdulen = 50; + + /* dest and origin absent */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, NULL, NULL, npdu, npdulen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ENCAPSULATED_NPDU, message_id, NULL, NULL, true, + true, npdulen); + zassert_equal(ret, true, NULL); + zassert_not_equal(message.hdr.payload, 0, NULL); + zassert_equal(message.hdr.payload_len, npdulen, NULL); + res = memcmp(message.hdr.payload, npdu, npdulen); + zassert_equal(res, 0, NULL); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, NULL, NULL, true, + false, npdu, npdulen, true); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, NULL, NULL, false, + true, npdu, npdulen, true); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, NULL, NULL, true, true, + npdu, npdulen, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* origin is presented, dest is absent */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, &origin, NULL, npdu, npdulen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, NULL, + true, true, npdulen); + zassert_equal(ret, true, NULL); + zassert_not_equal(message.hdr.payload, 0, NULL); + zassert_equal(message.hdr.payload_len, npdulen, NULL); + res = memcmp(message.hdr.payload, npdu, npdulen); + zassert_equal(res, 0, NULL); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, NULL, true, + false, npdu, npdulen, true); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, NULL, false, + true, npdu, npdulen, true); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, NULL, true, + true, npdu, npdulen, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* origin is absent, dest is presented */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, NULL, &dest, npdu, npdulen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ENCAPSULATED_NPDU, message_id, NULL, &dest, true, + true, npdulen); + zassert_equal(ret, true, NULL); + zassert_not_equal(message.hdr.payload, 0, NULL); + zassert_equal(message.hdr.payload_len, npdulen, NULL); + res = memcmp(message.hdr.payload, npdu, npdulen); + zassert_equal(res, 0, NULL); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, NULL, &dest, true, + false, npdu, npdulen, true); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, NULL, &dest, false, + true, npdu, npdulen, true); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, NULL, &dest, true, + true, npdu, npdulen, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* both dest and origin are presented */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, &origin, &dest, npdu, npdulen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, &dest, + true, true, npdulen); + zassert_equal(ret, true, NULL); + zassert_not_equal(message.hdr.payload, 0, NULL); + zassert_equal(message.hdr.payload_len, npdulen, NULL); + res = memcmp(message.hdr.payload, npdu, npdulen); + zassert_equal(res, 0, NULL); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, &dest, true, + false, npdu, npdulen, true); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, &dest, false, + true, npdu, npdulen, true); + test_options( + buf, len, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, &dest, true, + true, npdu, npdulen, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, &origin, &dest, npdu, npdulen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* truncated message, case 3 */ + + ret = bvlc_sc_decode_message(buf, 13, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 4 */ + + ret = bvlc_sc_decode_message(buf, 16, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PAYLOAD_EXPECTED, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 5 */ + + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* zero payload test */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, &origin, &dest, npdu, 0); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PAYLOAD_EXPECTED, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* 1 byte payload test */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, &origin, &dest, npdu, 1); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ENCAPSULATED_NPDU, message_id, &origin, &dest, + true, true, 1); + zassert_equal(ret, true, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_ADDRESS_RESOLUTION) +#else +static void test_ADDRESS_RESOLUTION(void) +#endif +{ + uint8_t buf[256]; + size_t len; + uint8_t optbuf[256]; + size_t optlen; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x514a; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS dest; + bool ret; + const char *err_desc = NULL; + + memset(&origin.address, 0x27, BVLC_SC_VMAC_SIZE); + memset(&dest.address, 0xaa, BVLC_SC_VMAC_SIZE); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* dest and origin absent */ + len = bvlc_sc_encode_address_resolution( + buf, sizeof(buf), message_id, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION, message_id, NULL, NULL, true, + true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION, message_id, NULL, NULL, true, + false, NULL, 0, false); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin is presented, dest is absent */ + len = bvlc_sc_encode_address_resolution( + buf, sizeof(buf), message_id, &origin, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION, message_id, &origin, NULL, + true, true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION, message_id, &origin, NULL, true, + false, NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* origin is absent, dest is presented */ + len = bvlc_sc_encode_address_resolution( + buf, sizeof(buf), message_id, NULL, &dest); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION, message_id, NULL, &dest, true, + true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION, message_id, NULL, &dest, true, + false, NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* origin and dest are presented */ + len = bvlc_sc_encode_address_resolution( + buf, sizeof(buf), message_id, &origin, &dest); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION, message_id, &origin, &dest, + true, true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION, message_id, &origin, &dest, true, + false, NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_address_resolution( + buf, sizeof(buf), message_id, &origin, &dest); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* truncated message, case 3 */ + + ret = bvlc_sc_decode_message(buf, 13, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 5 */ + + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* data options test */ + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_INCONSISTENT_PARAMETERS, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_ADDRESS_RESOLUTION_ACK) +#else +static void test_ADDRESS_RESOLUTION_ACK(void) +#endif +{ + uint8_t buf[256]; + size_t len; + uint8_t optbuf[256]; + size_t optlen; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0xf1d3; + int res; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS dest; + bool ret; + char web_socket_uris[256]; + const char *err_desc = NULL; + + web_socket_uris[0] = 0; + sprintf(web_socket_uris, "%s %s", "web_socket_uri1", "web_socket_uri2"); + memset(&origin.address, 0x91, BVLC_SC_VMAC_SIZE); + memset(&dest.address, 0xef, BVLC_SC_VMAC_SIZE); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* dest and origin absent */ + len = bvlc_sc_encode_address_resolution_ack( + buf, sizeof(buf), message_id, NULL, NULL, (uint8_t *)web_socket_uris, + strlen(web_socket_uris)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, NULL, NULL, + true, true, strlen(web_socket_uris)); + zassert_equal(ret, true, NULL); + res = memcmp(message.hdr.payload, web_socket_uris, strlen(web_socket_uris)); + zassert_equal(res, 0, NULL); + zassert_not_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string, NULL, + NULL); + zassert_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string_len, + strlen(web_socket_uris), NULL); + zassert_equal(message.hdr.payload_len, strlen(web_socket_uris), NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, NULL, NULL, true, + false, (uint8_t *)web_socket_uris, strlen(web_socket_uris), true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin is presented, dest is absent */ + len = bvlc_sc_encode_address_resolution_ack( + buf, sizeof(buf), message_id, &origin, NULL, (uint8_t *)web_socket_uris, + strlen(web_socket_uris)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, &origin, NULL, + true, true, strlen(web_socket_uris)); + zassert_equal(ret, true, NULL); + res = memcmp(message.hdr.payload, web_socket_uris, strlen(web_socket_uris)); + zassert_equal(res, 0, NULL); + zassert_not_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string, NULL, + NULL); + zassert_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string_len, + strlen(web_socket_uris), NULL); + zassert_equal(message.hdr.payload_len, strlen(web_socket_uris), NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, &origin, NULL, + true, false, (uint8_t *)web_socket_uris, strlen(web_socket_uris), true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin is absent, dest is presented */ + len = bvlc_sc_encode_address_resolution_ack( + buf, sizeof(buf), message_id, NULL, &dest, (uint8_t *)web_socket_uris, + strlen(web_socket_uris)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, NULL, &dest, + true, true, strlen(web_socket_uris)); + zassert_equal(ret, true, NULL); + res = memcmp(message.hdr.payload, web_socket_uris, strlen(web_socket_uris)); + zassert_equal(res, 0, NULL); + zassert_not_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string, NULL, + NULL); + zassert_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string_len, + strlen(web_socket_uris), NULL); + zassert_equal(message.hdr.payload_len, strlen(web_socket_uris), NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, NULL, &dest, true, + false, (uint8_t *)web_socket_uris, strlen(web_socket_uris), true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin and dest are presented */ + len = bvlc_sc_encode_address_resolution_ack( + buf, sizeof(buf), message_id, &origin, &dest, + (uint8_t *)web_socket_uris, strlen(web_socket_uris)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, &origin, + &dest, true, true, strlen(web_socket_uris)); + zassert_equal(ret, true, NULL); + res = memcmp(message.hdr.payload, web_socket_uris, strlen(web_socket_uris)); + zassert_equal(res, 0, NULL); + zassert_not_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string, NULL, + NULL); + zassert_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string_len, + strlen(web_socket_uris), NULL); + zassert_equal(message.hdr.payload_len, strlen(web_socket_uris), NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, &origin, &dest, + true, false, (uint8_t *)web_socket_uris, strlen(web_socket_uris), true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* zero payload test */ + len = bvlc_sc_encode_address_resolution_ack( + buf, sizeof(buf), message_id, &origin, &dest, NULL, 0); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, &origin, + &dest, true, true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string, NULL, + NULL); + zassert_equal( + message.payload.address_resolution_ack.utf8_websocket_uri_string_len, 0, + NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, &origin, &dest, + true, false, NULL, 0, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* truncated message, case 1 */ + + len = bvlc_sc_encode_address_resolution_ack( + buf, sizeof(buf), message_id, &origin, &dest, + (uint8_t *)web_socket_uris, strlen(web_socket_uris)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* truncated message, case 3 */ + + ret = bvlc_sc_decode_message(buf, 13, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 4 */ + + ret = bvlc_sc_decode_message(buf, 15, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 5 */ + + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* data options test */ + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_INCONSISTENT_PARAMETERS, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_ADVERTISIMENT) +#else +static void test_ADVERTISIMENT(void) +#endif +{ + uint8_t buf[256]; + size_t len; + uint8_t optbuf[256]; + size_t optlen; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0xe2ad; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS dest; + bool ret; + BACNET_SC_HUB_CONNECTOR_STATE hub_status = + BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY; + BVLC_SC_DIRECT_CONNECTION_SUPPORT support = + BVLC_SC_DIRECT_CONNECTION_ACCEPT_SUPPORTED; + uint16_t max_bvlc_len = 567; + uint16_t max_npdu_len = 1323; + const char *err_desc = NULL; + + memset(&origin.address, 0xe1, BVLC_SC_VMAC_SIZE); + memset(&dest.address, 0x4f, BVLC_SC_VMAC_SIZE); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* dest and origin absent */ + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, NULL, NULL, hub_status, support, + max_bvlc_len, max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADVERTISIMENT, message_id, NULL, NULL, true, true, + 6); + zassert_equal(ret, true, NULL); + zassert_equal(message.payload.advertisiment.hub_status, hub_status, NULL); + zassert_equal(message.payload.advertisiment.support, support, NULL); + zassert_equal( + message.payload.advertisiment.max_bvlc_len, max_bvlc_len, NULL); + zassert_equal( + message.payload.advertisiment.max_npdu_len, max_npdu_len, NULL); + test_options( + buf, len, BVLC_SC_ADVERTISIMENT, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin is presented, dest is absent */ + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, &origin, NULL, hub_status, support, + max_bvlc_len, max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADVERTISIMENT, message_id, &origin, NULL, true, + true, 6); + zassert_equal(ret, true, NULL); + zassert_equal(message.payload.advertisiment.hub_status, hub_status, NULL); + zassert_equal(message.payload.advertisiment.support, support, NULL); + zassert_equal( + message.payload.advertisiment.max_bvlc_len, max_bvlc_len, NULL); + zassert_equal( + message.payload.advertisiment.max_npdu_len, max_npdu_len, NULL); + test_options( + buf, len, BVLC_SC_ADVERTISIMENT, message_id, &origin, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin is absent, dest is presented */ + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, NULL, &dest, hub_status, support, + max_bvlc_len, max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADVERTISIMENT, message_id, NULL, &dest, true, + true, 6); + zassert_equal(ret, true, NULL); + zassert_equal(message.payload.advertisiment.hub_status, hub_status, NULL); + zassert_equal(message.payload.advertisiment.support, support, NULL); + zassert_equal( + message.payload.advertisiment.max_bvlc_len, max_bvlc_len, NULL); + zassert_equal( + message.payload.advertisiment.max_npdu_len, max_npdu_len, NULL); + test_options( + buf, len, BVLC_SC_ADVERTISIMENT, message_id, NULL, &dest, true, false, + message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin and dest are presented */ + + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, &origin, &dest, hub_status, support, + max_bvlc_len, max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADVERTISIMENT, message_id, &origin, &dest, true, + true, 6); + zassert_equal(ret, true, NULL); + zassert_equal(message.payload.advertisiment.hub_status, hub_status, NULL); + zassert_equal(message.payload.advertisiment.support, support, NULL); + zassert_equal( + message.payload.advertisiment.max_bvlc_len, max_bvlc_len, NULL); + zassert_equal( + message.payload.advertisiment.max_npdu_len, max_npdu_len, NULL); + test_options( + buf, len, BVLC_SC_ADVERTISIMENT, message_id, &origin, &dest, true, + false, message.hdr.payload, message.hdr.payload_len, false); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* truncated message, case 1 */ + + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, &origin, &dest, hub_status, support, + max_bvlc_len, max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* truncated message, case 3 */ + + ret = bvlc_sc_decode_message(buf, 13, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 4 */ + + ret = bvlc_sc_decode_message(buf, 15, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 5 */ + + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* bad hub connection param */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, NULL, NULL, 5, support, max_bvlc_len, + max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PARAMETER_OUT_OF_RANGE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* bad support param */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, NULL, NULL, hub_status, 4, max_bvlc_len, + max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PARAMETER_OUT_OF_RANGE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* payload len < 6 */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, NULL, NULL, hub_status, support, + max_bvlc_len, max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* zero payload test */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, NULL, NULL, hub_status, support, + max_bvlc_len, max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PAYLOAD_EXPECTED, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* data options presented */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_advertisiment( + buf, sizeof(buf), message_id, NULL, NULL, hub_status, support, + max_bvlc_len, max_npdu_len); + zassert_not_equal(len, 0, NULL); + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_INCONSISTENT_PARAMETERS, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_ADVERTISIMENT_SOLICITATION) +#else +static void test_ADVERTISIMENT_SOLICITATION(void) +#endif +{ + uint8_t buf[256]; + size_t len; + uint8_t optbuf[256]; + size_t optlen; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0xaf4a; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS dest; + bool ret; + const char *err_desc = NULL; + + memset(&origin.address, 0x17, BVLC_SC_VMAC_SIZE); + memset(&dest.address, 0x1a, BVLC_SC_VMAC_SIZE); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* dest and origin absent */ + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), message_id, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, NULL, + NULL, true, true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, NULL, NULL, + true, false, NULL, 0, false); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin is presented, dest is absent */ + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), message_id, &origin, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, &origin, + NULL, true, true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, &origin, NULL, + true, false, NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* origin is absent, dest is presented */ + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), message_id, NULL, &dest); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, NULL, + &dest, true, true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, NULL, &dest, + true, false, NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* origin and dest are presented */ + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), message_id, &origin, &dest); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, &origin, + &dest, true, true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, &origin, + &dest, true, false, NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), message_id, &origin, &dest); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* truncated message, case 3 */ + + ret = bvlc_sc_decode_message(buf, 13, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 5 */ + + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* data options test */ + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_INCONSISTENT_PARAMETERS, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_CONNECT_REQUEST) +#else +static void test_CONNECT_REQUEST(void) +#endif +{ + uint8_t buf[256]; + size_t len; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x41af; + int res; + bool ret; + uint16_t max_bvlc_len = 9997; + uint16_t max_npdu_len = 3329; + BACNET_SC_VMAC_ADDRESS local_vmac; + BACNET_SC_UUID local_uuid; + const char *err_desc = NULL; + + memset(&local_vmac, 0x88, sizeof(local_vmac)); + memset(&local_uuid, 0x22, sizeof(local_uuid)); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_connect_request( + buf, sizeof(buf), message_id, &local_vmac, &local_uuid, max_bvlc_len, + max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_CONNECT_REQUEST, message_id, NULL, NULL, true, + true, 26); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload_len, 26, NULL); + res = memcmp( + message.payload.connect_request.vmac, &local_vmac, BVLC_SC_VMAC_SIZE); + zassert_equal(res, 0, NULL); + res = memcmp( + message.payload.connect_request.uuid, &local_uuid, BVLC_SC_VMAC_SIZE); + zassert_equal(res, 0, NULL); + zassert_equal( + message.payload.connect_request.max_bvlc_len, max_bvlc_len, NULL); + zassert_equal( + message.payload.connect_request.max_npdu_len, max_npdu_len, NULL); + test_options( + buf, len, BVLC_SC_CONNECT_REQUEST, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_connect_request( + buf, sizeof(buf), message_id, &local_vmac, &local_uuid, max_bvlc_len, + max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* truncated message, case 3 */ + + ret = bvlc_sc_decode_message(buf, 13, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 5 */ + + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PAYLOAD_EXPECTED, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* message has dest */ + buf[1] |= BVLC_SC_CONTROL_DEST_VADDR; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* message has origin */ + buf[1] |= BVLC_SC_CONTROL_ORIG_VADDR; + buf[1] &= ~BVLC_SC_CONTROL_DEST_VADDR; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* message has both dest and orign */ + buf[1] |= BVLC_SC_CONTROL_ORIG_VADDR; + buf[1] |= BVLC_SC_CONTROL_DEST_VADDR; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_CONNECT_ACCEPT) +#else +static void test_CONNECT_ACCEPT(void) +#endif +{ + uint8_t buf[256]; + size_t len; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x0203; + int res; + bool ret; + uint16_t max_bvlc_len = 1027; + uint16_t max_npdu_len = 22; + BACNET_SC_VMAC_ADDRESS local_vmac; + BACNET_SC_UUID local_uuid; + const char *err_desc = NULL; + + memset(&local_vmac, 0x33, sizeof(local_vmac)); + memset(&local_uuid, 0x11, sizeof(local_uuid)); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_connect_accept( + buf, sizeof(buf), message_id, &local_vmac, &local_uuid, max_bvlc_len, + max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_CONNECT_ACCEPT, message_id, NULL, NULL, true, + true, 26); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload_len, 26, NULL); + res = memcmp( + message.payload.connect_accept.vmac, &local_vmac, BVLC_SC_VMAC_SIZE); + zassert_equal(res, 0, NULL); + res = memcmp( + message.payload.connect_accept.uuid, &local_uuid, BVLC_SC_VMAC_SIZE); + zassert_equal(res, 0, NULL); + zassert_equal( + message.payload.connect_accept.max_bvlc_len, max_bvlc_len, NULL); + zassert_equal( + message.payload.connect_accept.max_npdu_len, max_npdu_len, NULL); + test_options( + buf, len, BVLC_SC_CONNECT_ACCEPT, message_id, NULL, NULL, true, false, + message.hdr.payload, message.hdr.payload_len, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_connect_accept( + buf, sizeof(buf), message_id, &local_vmac, &local_uuid, max_bvlc_len, + max_npdu_len); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* truncated message, case 3 */ + + ret = bvlc_sc_decode_message(buf, 13, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 5 */ + + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PAYLOAD_EXPECTED, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* message has dest */ + buf[1] |= BVLC_SC_CONTROL_DEST_VADDR; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* message has origin */ + buf[1] |= BVLC_SC_CONTROL_ORIG_VADDR; + buf[1] &= ~BVLC_SC_CONTROL_DEST_VADDR; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* message has both dest and orign */ + buf[1] |= BVLC_SC_CONTROL_ORIG_VADDR; + buf[1] |= BVLC_SC_CONTROL_DEST_VADDR; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_DISCONNECT_REQUEST) +#else +static void test_DISCONNECT_REQUEST(void) +#endif +{ + uint8_t buf[256]; + size_t len; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x0203; + bool ret; + const char *err_desc = NULL; + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_disconnect_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_DISCONNECT_REQUEST, message_id, NULL, NULL, true, + true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_DISCONNECT_REQUEST, message_id, NULL, NULL, true, + false, NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_disconnect_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_UNEXPECTED_DATA, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + ret = bvlc_sc_decode_message(buf, 3, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_DISCARD, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_DISCONNECT_ACK) +#else +static void test_DISCONNECT_ACK(void) +#endif +{ + uint8_t buf[256]; + size_t len; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x0203; + bool ret; + const char *err_desc = NULL; + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_disconnect_ack(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_DISCONNECT_ACK, message_id, NULL, NULL, true, + true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_DISCONNECT_ACK, message_id, NULL, NULL, true, false, + NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_disconnect_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_UNEXPECTED_DATA, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + ret = bvlc_sc_decode_message(buf, 3, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_DISCARD, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_HEARTBEAT_REQUEST) +#else +static void test_HEARTBEAT_REQUEST(void) +#endif +{ + uint8_t buf[256]; + size_t len; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x0203; + bool ret; + const char *err_desc = NULL; + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_HEARTBEAT_REQUEST, message_id, NULL, NULL, true, + true, 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_HEARTBEAT_REQUEST, message_id, NULL, NULL, true, + false, NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_disconnect_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_UNEXPECTED_DATA, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + ret = bvlc_sc_decode_message(buf, 3, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_DISCARD, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_HEARTBEAT_ACK) +#else +static void test_HEARTBEAT_ACK(void) +#endif +{ + uint8_t buf[256]; + size_t len; + uint8_t optbuf[256]; + size_t optlen; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x0203; + bool ret; + const char *err_desc = NULL; + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + len = bvlc_sc_encode_heartbeat_ack(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_HEARTBEAT_ACK, message_id, NULL, NULL, true, true, + 0); + zassert_equal(ret, true, NULL); + zassert_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 0, NULL); + test_options( + buf, len, BVLC_SC_HEARTBEAT_ACK, message_id, NULL, NULL, true, false, + NULL, 0, false); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message, case 1 */ + len = bvlc_sc_encode_heartbeat_ack(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_UNEXPECTED_DATA, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + ret = bvlc_sc_decode_message(buf, 3, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_DISCARD, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* data options test */ + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_INCONSISTENT_PARAMETERS, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_PROPRIETARY_MESSAGE) +#else +static void test_PROPRIETARY_MESSAGE(void) +#endif +{ + uint8_t buf[256]; + size_t len; + uint8_t optbuf[256]; + size_t optlen; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x4455; + int res; + bool ret; + uint8_t proprietary_function = 0xea; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS dest; + uint16_t vendor_id = 0xaabb; + uint8_t data[34]; + const char *err_desc = NULL; + + memset(data, 0x66, sizeof(data)); + memset(&origin.address, 0x2f, BVLC_SC_VMAC_SIZE); + memset(&dest.address, 0xf3, BVLC_SC_VMAC_SIZE); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* dest and origin absent */ + len = bvlc_sc_encode_proprietary_message( + buf, sizeof(buf), message_id, NULL, NULL, vendor_id, + proprietary_function, data, sizeof(data)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_PROPRIETARY_MESSAGE, message_id, NULL, NULL, true, + true, 3 + sizeof(data)); + zassert_equal(ret, true, NULL); + zassert_not_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 3 + sizeof(data), NULL); + zassert_equal(message.payload.proprietary.vendor_id, vendor_id, NULL); + zassert_equal( + message.payload.proprietary.function, proprietary_function, NULL); + zassert_equal(message.payload.proprietary.data_len, sizeof(data), NULL); + + res = memcmp(message.payload.proprietary.data, data, sizeof(data)); + zassert_equal(res, 0, NULL); + test_options( + buf, len, BVLC_SC_PROPRIETARY_MESSAGE, message_id, NULL, NULL, true, + false, message.hdr.payload, message.hdr.payload_len, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin is presented, dest is absent */ + len = bvlc_sc_encode_proprietary_message( + buf, sizeof(buf), message_id, &origin, NULL, vendor_id, + proprietary_function, data, sizeof(data)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_PROPRIETARY_MESSAGE, message_id, &origin, NULL, + true, true, 3 + sizeof(data)); + zassert_equal(ret, true, NULL); + zassert_not_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 3 + sizeof(data), NULL); + zassert_equal(message.payload.proprietary.vendor_id, vendor_id, NULL); + zassert_equal( + message.payload.proprietary.function, proprietary_function, NULL); + zassert_equal(message.payload.proprietary.data_len, sizeof(data), NULL); + + res = memcmp(message.payload.proprietary.data, data, sizeof(data)); + zassert_equal(res, 0, NULL); + test_options( + buf, len, BVLC_SC_PROPRIETARY_MESSAGE, message_id, &origin, NULL, true, + false, message.hdr.payload, message.hdr.payload_len, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin is absent, dest is presented */ + len = bvlc_sc_encode_proprietary_message( + buf, sizeof(buf), message_id, NULL, &dest, vendor_id, + proprietary_function, data, sizeof(data)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_PROPRIETARY_MESSAGE, message_id, NULL, &dest, + true, true, 3 + sizeof(data)); + zassert_equal(ret, true, NULL); + zassert_not_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 3 + sizeof(data), NULL); + zassert_equal(message.payload.proprietary.vendor_id, vendor_id, NULL); + zassert_equal( + message.payload.proprietary.function, proprietary_function, NULL); + zassert_equal(message.payload.proprietary.data_len, sizeof(data), NULL); + + res = memcmp(message.payload.proprietary.data, data, sizeof(data)); + zassert_equal(res, 0, NULL); + test_options( + buf, len, BVLC_SC_PROPRIETARY_MESSAGE, message_id, NULL, &dest, true, + false, message.hdr.payload, message.hdr.payload_len, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* origin and dest are presented */ + len = bvlc_sc_encode_proprietary_message( + buf, sizeof(buf), message_id, &origin, &dest, vendor_id, + proprietary_function, data, sizeof(data)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = verify_bsc_bvll_header( + &message.hdr, BVLC_SC_PROPRIETARY_MESSAGE, message_id, &origin, &dest, + true, true, 3 + sizeof(data)); + zassert_equal(ret, true, NULL); + zassert_not_equal(message.hdr.payload, NULL, NULL); + zassert_equal(message.hdr.payload_len, 3 + sizeof(data), NULL); + zassert_equal(message.payload.proprietary.vendor_id, vendor_id, NULL); + zassert_equal( + message.payload.proprietary.function, proprietary_function, NULL); + zassert_equal(message.payload.proprietary.data_len, sizeof(data), NULL); + + res = memcmp(message.payload.proprietary.data, data, sizeof(data)); + zassert_equal(res, 0, NULL); + test_options( + buf, len, BVLC_SC_PROPRIETARY_MESSAGE, message_id, &origin, &dest, true, + false, message.hdr.payload, message.hdr.payload_len, true); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* truncated message, case 1 */ + len = bvlc_sc_encode_proprietary_message( + buf, sizeof(buf), message_id, &origin, &dest, vendor_id, + proprietary_function, data, sizeof(data)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 5, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 2 */ + + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* truncated message, case 3 */ + + ret = bvlc_sc_decode_message(buf, 13, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + /* truncated message, case 5 */ + + ret = bvlc_sc_decode_message(buf, 18, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* data options test */ + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_INCONSISTENT_PARAMETERS, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* zero payload test */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* truncated message, case 1 */ + len = bvlc_sc_encode_proprietary_message( + buf, sizeof(buf), message_id, NULL, NULL, vendor_id, + proprietary_function, data, sizeof(data)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_PAYLOAD_EXPECTED, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* zero payload test */ + ret = bvlc_sc_decode_message(buf, 7, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal(message.payload.proprietary.vendor_id, vendor_id, NULL); + zassert_equal( + message.payload.proprietary.function, proprietary_function, NULL); + zassert_equal(message.payload.proprietary.data_len, 0, NULL); + zassert_equal(message.payload.proprietary.data, NULL, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_BAD_HEADER_OPTIONS) +#else +static void test_BAD_HEADER_OPTIONS(void) +#endif +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + uint8_t npdu[256]; + uint16_t npdulen; + size_t len; + size_t optlen; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x0203; + bool ret; + uint16_t vendor_id1; + uint8_t proprietary_option_type1; + uint8_t proprietary_data1[17]; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS dest; + const char *err_desc = NULL; + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* form secure path option with data flag enabled (which is incorrect ) */ + + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + buf[4] |= BVLC_SC_HEADER_DATA; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + /* form unknown header option */ + + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + buf[4] |= 2; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* form unknown header option */ + + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + + optlen = bvlc_sc_encode_secure_path_option(optbuf, sizeof(optbuf), true); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + buf[4] |= 2; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_HEADER_ENCODING_ERROR, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* truncated proprietary option, case 1 */ + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + ret = bvlc_sc_decode_message(buf, 6, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* truncated proprietary option, case 2 */ + + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + ret = bvlc_sc_decode_message(buf, 9, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* truncated message with destination options */ + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, 4, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_MESSAGE_INCOMPLETE, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* call add option func with bad parameters */ + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_not_equal(optlen, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), NULL, len, optbuf, optlen); + zassert_equal(len, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, 3, optbuf, optlen); + zassert_equal(len, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, 64000, optbuf, 64000); + zassert_equal(len, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, 100, buf, 120, optbuf, optlen); + zassert_equal(len, 0, NULL); + len = bvlc_sc_add_option_to_destination_options( + buf, 101, buf, 100, optbuf, optlen); + zassert_equal(len, 0, NULL); + optbuf[0] |= BVLC_SC_HEADER_MORE; + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, 20, optbuf, optlen); + zassert_equal(len, 0, NULL); + optbuf[0] = 23; + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, 20, optbuf, optlen); + zassert_equal(len, 0, NULL); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + /* truncated message with destination options */ + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_not_equal(len, 0, NULL); + buf[4] = 23; + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_equal(len, 0, NULL); + /* message with incorrect dest options */ + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), message_id); + zassert_not_equal(len, 0, NULL); + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + len = bvlc_sc_add_option_to_destination_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + buf[4] = 23; + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, len, optbuf, optlen); + zassert_equal(len, 0, NULL); + + /* one more incorrect call to add options */ + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + memset(&origin.address, 0x63, BVLC_SC_VMAC_SIZE); + memset(&dest.address, 0x24, BVLC_SC_VMAC_SIZE); + memset(npdu, 0x99, sizeof(npdu)); + npdulen = 50; + + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, &origin, &dest, npdu, npdulen); + + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + + len = bvlc_sc_add_option_to_data_options( + buf, sizeof(buf), buf, 4, optbuf, optlen); + zassert_equal(len, 0, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_BAD_ENCODE_PARAMS) +#else +static void test_BAD_ENCODE_PARAMS(void) +#endif +{ + uint8_t buf[256]; + uint8_t optbuf[256]; + uint8_t npdu[256]; + uint16_t npdulen; + size_t len; + size_t optlen; + BVLC_SC_DECODED_MESSAGE message; + uint16_t message_id = 0x0203; + uint16_t vendor_id1; + uint8_t proprietary_option_type1; + uint8_t proprietary_data1[17]; + BACNET_SC_VMAC_ADDRESS origin; + BACNET_SC_VMAC_ADDRESS dest; + uint8_t error_header_marker = 0xcc; + uint16_t error_class = 0xaa; + uint16_t error_code = 0xdd; + char *error_details_string = "something bad has happend"; + BACNET_SC_VMAC_ADDRESS local_vmac; + BACNET_SC_UUID local_uuid; + uint8_t data[34]; + uint8_t proprietary_function = 0xea; + + memset(data, 0x66, sizeof(data)); + memset(&local_uuid, 0x22, sizeof(local_uuid)); + memset(&local_vmac, 0x42, sizeof(local_vmac)); + memset(&origin.address, 0x63, BVLC_SC_VMAC_SIZE); + memset(&dest.address, 0x24, BVLC_SC_VMAC_SIZE); + memset(npdu, 0x99, sizeof(npdu)); + npdulen = 50; + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + vendor_id1 = 0xdead; + proprietary_option_type1 = 0x77; + memset(proprietary_data1, 0x99, sizeof(proprietary_data1)); + /* case 1 */ + optlen = bvlc_sc_encode_proprietary_option( + optbuf, sizeof(optbuf), true, vendor_id1, proprietary_option_type1, + proprietary_data1, BVLC_SC_NPDU_SIZE - 3); + zassert_equal(optlen, 0, NULL); + /* case 2 */ + optlen = bvlc_sc_encode_proprietary_option( + optbuf, 3, true, vendor_id1, proprietary_option_type1, + proprietary_data1, sizeof(proprietary_data1)); + zassert_equal(optlen, 0, NULL); + /* case 3 */ + optlen = bvlc_sc_encode_secure_path_option(optbuf, 0, true); + zassert_equal(optlen, 0, NULL); + /* case 4 */ + len = bvlc_sc_encode_heartbeat_request(buf, 3, message_id); + zassert_equal(len, 0, NULL); + /* case 5 */ + len = bvlc_sc_encode_heartbeat_request(buf, 3, message_id); + zassert_equal(len, 0, NULL); + /* case 6 */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, 5, message_id, &origin, NULL, npdu, npdulen); + zassert_equal(len, 0, NULL); + /* case 7 */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, 6 + BVLC_SC_VMAC_SIZE, message_id, &origin, &dest, npdu, npdulen); + zassert_equal(len, 0, NULL); + /* case 8 */ + len = bvlc_sc_encode_result( + buf, sizeof(buf), message_id, &origin, &dest, 99, 0, NULL, NULL, NULL, + NULL); + zassert_equal(len, 0, NULL); + /* case 9 */ + len = bvlc_sc_encode_result( + buf, sizeof(buf), message_id, &origin, &dest, 1, 4, NULL, NULL, NULL, + NULL); + zassert_equal(len, 0, NULL); + /* case 9 */ + len = bvlc_sc_encode_result( + buf, sizeof(buf), message_id, &origin, &dest, 1, 1, NULL, NULL, NULL, + NULL); + zassert_equal(len, 0, NULL); + /* case 10 */ + len = bvlc_sc_encode_result( + buf, 3, message_id, &origin, &dest, 1, 0, NULL, NULL, NULL, NULL); + zassert_equal(len, 0, NULL); + /* case 11 */ + len = bvlc_sc_encode_result( + buf, 5, message_id, NULL, NULL, 1, 0, NULL, NULL, NULL, NULL); + zassert_equal(len, 0, NULL); + /* case 12 */ + len = bvlc_sc_encode_result( + buf, 7, message_id, NULL, NULL, 1, 0, (uint8_t *)1, NULL, NULL, NULL); + zassert_equal(len, 0, NULL); + /* case 13 */ + len = bvlc_sc_encode_result( + buf, 7, message_id, NULL, NULL, 1, 1, &error_header_marker, &error_code, + &error_class, (uint8_t *)error_details_string); + zassert_equal(len, 0, NULL); + /* case 13 */ + len = bvlc_sc_encode_result( + buf, 12, message_id, NULL, NULL, 1, 1, &error_header_marker, + &error_code, &error_class, (uint8_t *)error_details_string); + zassert_equal(len, 0, NULL); + /* case 14 */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, 3, message_id, NULL, NULL, npdu, npdulen); + zassert_equal(len, 0, NULL); + /* case 15 */ + len = bvlc_sc_encode_encapsulated_npdu( + buf, 6, message_id, NULL, NULL, npdu, npdulen); + zassert_equal(len, 0, NULL); + /* case 16 */ + len = bvlc_sc_encode_address_resolution_ack( + buf, 3, message_id, NULL, NULL, NULL, 0); + zassert_equal(len, 0, NULL); + /* case 17 */ + len = bvlc_sc_encode_advertisiment( + buf, 3, message_id, NULL, NULL, 1, 1, 1, 1); + zassert_equal(len, 0, NULL); + /* case 18 */ + len = bvlc_sc_encode_connect_request( + buf, sizeof(buf), message_id, NULL, NULL, 1, 1); + zassert_equal(len, 0, NULL); + /* case 19 */ + len = bvlc_sc_encode_connect_request( + buf, 3, message_id, &local_vmac, &local_uuid, 1, 1); + zassert_equal(len, 0, NULL); + /* case 20 */ + len = bvlc_sc_encode_connect_request( + buf, 5, message_id, &local_vmac, &local_uuid, 1, 1); + zassert_equal(len, 0, NULL); + /* case 21 */ + len = bvlc_sc_encode_connect_accept( + buf, sizeof(buf), message_id, NULL, NULL, 1, 1); + zassert_equal(len, 0, NULL); + /* case 22 */ + len = bvlc_sc_encode_connect_accept( + buf, 3, message_id, &local_vmac, &local_uuid, 1, 1); + zassert_equal(len, 0, NULL); + /* case 23 */ + len = bvlc_sc_encode_connect_accept( + buf, 5, message_id, &local_vmac, &local_uuid, 1, 1); + zassert_equal(len, 0, NULL); + /* case 24 */ + len = bvlc_sc_encode_proprietary_message( + buf, 3, message_id, NULL, NULL, vendor_id1, proprietary_function, data, + sizeof(data)); + zassert_equal(len, 0, NULL); + /* case 25 */ + len = bvlc_sc_encode_proprietary_message( + buf, 5, message_id, NULL, NULL, vendor_id1, proprietary_function, data, + sizeof(data)); + zassert_equal(len, 0, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_BAD_DECODE_PARAMS) +#else +static void test_BAD_DECODE_PARAMS(void) +#endif +{ + uint8_t buf[256]; + uint8_t npdu[256]; + uint16_t npdulen = 50; + size_t len; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + uint16_t message_id = 0x0203; + bool ret; + const char *err_desc = NULL; + + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); + + /* case 1 */ + + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), message_id, NULL, NULL, npdu, npdulen); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, NULL, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + + /* case 2 */ + buf[0] = 99; + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, false, NULL); + zassert_equal(error, ERROR_CODE_BVLC_FUNCTION_UNKNOWN, NULL); + zassert_equal(class, ERROR_CLASS_COMMUNICATION, NULL); + memset(buf, 0, sizeof(buf)); + memset(&message, 0, sizeof(message)); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(bvlc_sc_tests, test_BROADCAST) +#else +static void test_BROADCAST(void) +#endif +{ + uint8_t buf[256]; + size_t len; + BACNET_SC_VMAC_ADDRESS dest; + BACNET_SC_VMAC_ADDRESS orig; + bool ret; + uint8_t *pdu; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc = NULL; + BACNET_SC_UUID uuid = { { 0x34 } }; + + memset(&dest.address, 0xFF, sizeof(dest.address)); + memset(&orig.address, 0x12, sizeof(orig.address)); + ret = bvlc_sc_is_vmac_broadcast(&dest); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_is_vmac_broadcast(&orig); + zassert_equal(ret, false, NULL); + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), 0xF00D, &orig, &orig); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_pdu_has_dest_broadcast(buf, len); + zassert_equal(ret, false, NULL); + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), 0xF00D, &orig, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_pdu_has_dest_broadcast(buf, len); + zassert_equal(ret, false, NULL); + len = bvlc_sc_encode_disconnect_ack(buf, sizeof(buf), 0xF00D); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_pdu_has_dest_broadcast(buf, len); + zassert_equal(ret, false, NULL); + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), 0xF00D, NULL, &orig); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_pdu_has_dest_broadcast(buf, len); + zassert_equal(ret, false, NULL); + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), 0xF00D, NULL, &dest); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_pdu_has_dest_broadcast(buf, len); + zassert_equal(ret, true, NULL); + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), 0xF00D, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_pdu_has_dest_broadcast(buf, len); + zassert_equal(ret, false, NULL); + pdu = buf; + len = bvlc_sc_set_orig(&pdu, len, &orig); + zassert_equal(len, 4, NULL); + len = bvlc_sc_remove_orig_and_dest(&pdu, len); + zassert_equal(len, 4, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, true, NULL); + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), 0xF00D, NULL, &dest); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, false, NULL); + len = bvlc_sc_encode_connect_request( + buf, sizeof(buf), 0xF00D, &orig, &uuid, 1000, 1000); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, true, NULL); + len = bvlc_sc_encode_connect_accept( + buf, sizeof(buf), 0xF00D, &orig, &uuid, 1000, 1000); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, false, NULL); + len = bvlc_sc_encode_disconnect_request(buf, sizeof(buf), 0xF00D); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, true, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 0xF00D, NULL, NULL, (uint8_t *)&orig, sizeof(orig)); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, true, NULL); + len = bvlc_sc_encode_proprietary_message( + buf, sizeof(buf), 0xF00D, NULL, NULL, 123, 123, NULL, 0); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, false, NULL); + len = bvlc_sc_encode_heartbeat_request(buf, sizeof(buf), 0xF00D); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, true, NULL); + len = + bvlc_sc_encode_address_resolution(buf, sizeof(buf), 0xF00D, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, true, NULL); + len = bvlc_sc_encode_advertisiment_solicitation( + buf, sizeof(buf), 0xF00D, NULL, NULL); + zassert_not_equal(len, 0, NULL); + ret = bvlc_sc_decode_message(buf, len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + ret = bvlc_sc_need_send_bvlc_result(&message); + zassert_equal(ret, true, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST_SUITE(bvlc_sc_tests, NULL, NULL, NULL, NULL, NULL); +#else +void test_main(void) +{ + ztest_test_suite( + bvlc_sc_tests, ztest_unit_test(test_BVLC_RESULT), + ztest_unit_test(test_ENCAPSULATED_NPDU), + ztest_unit_test(test_ADDRESS_RESOLUTION), + ztest_unit_test(test_ADDRESS_RESOLUTION_ACK), + ztest_unit_test(test_ADVERTISIMENT), + ztest_unit_test(test_ADVERTISIMENT_SOLICITATION), + ztest_unit_test(test_CONNECT_REQUEST), + ztest_unit_test(test_CONNECT_ACCEPT), + ztest_unit_test(test_DISCONNECT_REQUEST), + ztest_unit_test(test_DISCONNECT_ACK), + ztest_unit_test(test_HEARTBEAT_REQUEST), + ztest_unit_test(test_HEARTBEAT_ACK), + ztest_unit_test(test_PROPRIETARY_MESSAGE), + ztest_unit_test(test_BAD_HEADER_OPTIONS), + ztest_unit_test(test_BAD_ENCODE_PARAMS), + ztest_unit_test(test_BAD_DECODE_PARAMS), + ztest_unit_test(test_BROADCAST)); + + ztest_run_test_suite(bvlc_sc_tests); +} +#endif diff --git a/test/bacnet/datalink/hub-sc/CMakeLists.txt b/test/bacnet/datalink/hub-sc/CMakeLists.txt new file mode 100644 index 00000000..f9344ae8 --- /dev/null +++ b/test/bacnet/datalink/hub-sc/CMakeLists.txt @@ -0,0 +1,288 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BACDL_BSC + BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM=4 + BSC_CONF_NODE_SWITCH_CONNECTIONS_NUM=4 + BSC_CONF_WSURL_MAX_LEN=128 + BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN=128 + BSC_CONF_WEBSOCKET_SERVERS_NUM=4 + BSC_CONF_HUB_CONNECTORS_NUM=2 + BSC_CONF_HUB_FUNCTIONS_NUM=2 + MAX_TSM_TRANSACTIONS=0 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +if(ZEPHYR_BASE) + message(FATAL_ERROR "ZEPHYR_BASE env variable defined. Use zephyr/CMakeLists.txt for Zephyr build") +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(STATUS "BACNet/SC hub function test: building for linux") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux) + add_compile_definitions(BACNET_PORT=linux) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/linux/websocket-cli.c + ${PORTS_DIR}/linux/websocket-srv.c + ${PORTS_DIR}/linux/websocket-global.c + ${PORTS_DIR}/linux/bsc-event.c + ${PORTS_DIR}/linux/mstimer-init.c + ${PORTS_DIR}/linux/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) +elseif(WIN32) + message(STATUS "BACNet/SC hub function test: building for win32") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/win32) + add_compile_definitions(BACNET_PORT=win32) + add_compile_definitions(BACNET_STACK_STATIC_DEFINE) + add_compile_definitions(BACNET_USE_DOUBLE=0) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + find_package(OpenSSL) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/win32/websocket-cli.c + ${PORTS_DIR}/win32/websocket-srv.c + ${PORTS_DIR}/win32/websocket-global.c + ${PORTS_DIR}/win32/bsc-event.c + ${PORTS_DIR}/win32/mstimer-init.c + ${PORTS_DIR}/win32/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + + # basically if you use vcpkg you should just add ${LIBWEBSOCKETS_LIBRARIES} + # into target_link_libraries() but for some reason it does not work as expected + # so that's why libs have to be hardcoded as workaround + target_link_libraries(${PROJECT_NAME} + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\websockets.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libssl.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libcrypto.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\pthreadVC3.lib + ws2_32.lib + userenv.lib + psapi.lib + iphlpapi.lib + crypt32.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\zlib.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\uv.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + comdlg32.lib + advapi32.lib + ) + +elseif(APPLE) + message(STATUS "BACNet/SC hub function test: building for APPLE") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/bsd) + execute_process ( + COMMAND bash -c "brew --prefix openssl" + OUTPUT_VARIABLE OPEN_SSL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE + ) + include_directories(${OPEN_SSL_DIR}/include) + add_compile_definitions(BACNET_PORT=bsd) + find_package(libwebsockets CONFIG REQUIRED) + include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/bsd/websocket-cli.c + ${PORTS_DIR}/bsd/websocket-srv.c + ${PORTS_DIR}/bsd/websocket-global.c + ${PORTS_DIR}/bsd/bsc-event.c + ${PORTS_DIR}/bsd/mstimer-init.c + ${PORTS_DIR}/bsd/datetime-init.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${SRC_DIR}/bacnet/datalink/bsc/bvlc-sc.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-socket.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-node-switch.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-connector.c + ${SRC_DIR}/bacnet/datalink/bsc/bsc-hub-function.c + ${SRC_DIR}/bacnet/basic/object/bacfile.c + ${SRC_DIR}/bacnet/basic/object/netport.c + ${SRC_DIR}/bacnet/basic/object/sc_netport.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/basic/sys/mstimer.c + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/arf.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/dcc.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/proplist.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/secure_connect.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + # Test and test library files + ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/device_mock.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) + target_compile_options(${PROJECT_NAME} PRIVATE + -Wno-language-extension-token + ) +endif() diff --git a/test/bacnet/datalink/hub-sc/src/main.c b/test/bacnet/datalink/hub-sc/src/main.c new file mode 100644 index 00000000..5ad225d9 --- /dev/null +++ b/test/bacnet/datalink/hub-sc/src/main.c @@ -0,0 +1,9085 @@ +/* + * Copyright (c) 2020 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +/* @file + * @brief test of bsc-socket interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned char ca_key[] = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x52, + 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, + 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x45, + 0x6f, 0x67, 0x49, 0x42, 0x41, 0x41, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, + 0x31, 0x66, 0x7a, 0x73, 0x2b, 0x56, 0x62, 0x46, 0x39, 0x31, 0x65, 0x45, + 0x66, 0x4c, 0x32, 0x67, 0x44, 0x63, 0x6a, 0x79, 0x44, 0x47, 0x68, 0x62, + 0x4d, 0x6f, 0x44, 0x48, 0x6c, 0x71, 0x53, 0x6d, 0x70, 0x78, 0x7a, 0x45, + 0x2b, 0x4a, 0x48, 0x34, 0x6f, 0x46, 0x47, 0x46, 0x42, 0x31, 0x47, 0x48, + 0x0a, 0x69, 0x35, 0x47, 0x7a, 0x33, 0x73, 0x47, 0x51, 0x4d, 0x6e, 0x41, + 0x6e, 0x32, 0x4c, 0x41, 0x6e, 0x51, 0x53, 0x5a, 0x72, 0x46, 0x77, 0x6c, + 0x77, 0x72, 0x48, 0x34, 0x68, 0x43, 0x36, 0x62, 0x6d, 0x4a, 0x33, 0x71, + 0x55, 0x58, 0x72, 0x61, 0x32, 0x64, 0x76, 0x63, 0x45, 0x34, 0x2f, 0x32, + 0x2f, 0x70, 0x74, 0x6b, 0x34, 0x31, 0x47, 0x39, 0x68, 0x75, 0x45, 0x4f, + 0x75, 0x77, 0x69, 0x6b, 0x68, 0x0a, 0x7a, 0x5a, 0x59, 0x65, 0x67, 0x6b, + 0x4b, 0x68, 0x62, 0x78, 0x46, 0x70, 0x77, 0x56, 0x2f, 0x76, 0x48, 0x32, + 0x48, 0x6f, 0x55, 0x45, 0x39, 0x30, 0x4d, 0x43, 0x56, 0x7a, 0x66, 0x59, + 0x56, 0x51, 0x62, 0x6a, 0x38, 0x51, 0x2f, 0x73, 0x51, 0x50, 0x6d, 0x79, + 0x4f, 0x6d, 0x64, 0x76, 0x46, 0x74, 0x4a, 0x41, 0x4e, 0x47, 0x53, 0x2f, + 0x52, 0x58, 0x31, 0x67, 0x6b, 0x2b, 0x6e, 0x7a, 0x67, 0x38, 0x0a, 0x54, + 0x4f, 0x33, 0x6f, 0x4a, 0x45, 0x6b, 0x70, 0x69, 0x6e, 0x67, 0x34, 0x45, + 0x31, 0x4c, 0x4a, 0x2b, 0x63, 0x48, 0x66, 0x45, 0x66, 0x4a, 0x45, 0x49, + 0x5a, 0x4f, 0x4d, 0x69, 0x4e, 0x69, 0x6a, 0x31, 0x4d, 0x72, 0x50, 0x70, + 0x32, 0x4d, 0x63, 0x36, 0x4d, 0x49, 0x7a, 0x54, 0x6f, 0x76, 0x4c, 0x6f, + 0x51, 0x31, 0x38, 0x36, 0x2f, 0x68, 0x63, 0x59, 0x50, 0x36, 0x62, 0x4f, + 0x76, 0x46, 0x59, 0x0a, 0x6d, 0x59, 0x78, 0x68, 0x59, 0x46, 0x30, 0x68, + 0x52, 0x38, 0x4f, 0x57, 0x53, 0x4c, 0x30, 0x55, 0x45, 0x78, 0x69, 0x4e, + 0x6f, 0x64, 0x45, 0x2f, 0x61, 0x70, 0x68, 0x7a, 0x39, 0x41, 0x56, 0x52, + 0x63, 0x6d, 0x79, 0x62, 0x76, 0x62, 0x4f, 0x6c, 0x77, 0x32, 0x4c, 0x71, + 0x44, 0x50, 0x42, 0x43, 0x62, 0x46, 0x6b, 0x72, 0x50, 0x75, 0x4c, 0x7a, + 0x6d, 0x6e, 0x47, 0x6b, 0x78, 0x46, 0x57, 0x41, 0x0a, 0x45, 0x42, 0x65, + 0x52, 0x38, 0x51, 0x43, 0x33, 0x77, 0x72, 0x57, 0x6a, 0x6e, 0x6a, 0x34, + 0x30, 0x71, 0x66, 0x6e, 0x33, 0x78, 0x50, 0x6c, 0x4e, 0x41, 0x73, 0x57, + 0x42, 0x64, 0x7a, 0x52, 0x2f, 0x64, 0x32, 0x43, 0x6d, 0x63, 0x51, 0x49, + 0x44, 0x41, 0x51, 0x41, 0x42, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x43, 0x58, + 0x49, 0x78, 0x7a, 0x38, 0x76, 0x70, 0x7a, 0x30, 0x4a, 0x59, 0x77, 0x64, + 0x7a, 0x0a, 0x70, 0x44, 0x4e, 0x4b, 0x37, 0x42, 0x4a, 0x73, 0x79, 0x73, + 0x32, 0x63, 0x46, 0x36, 0x48, 0x74, 0x36, 0x4d, 0x39, 0x52, 0x4d, 0x53, + 0x61, 0x43, 0x2f, 0x39, 0x65, 0x76, 0x44, 0x55, 0x4a, 0x42, 0x6a, 0x79, + 0x47, 0x42, 0x31, 0x4c, 0x54, 0x63, 0x6b, 0x4d, 0x32, 0x58, 0x4b, 0x44, + 0x49, 0x47, 0x79, 0x4b, 0x65, 0x6b, 0x56, 0x50, 0x78, 0x34, 0x57, 0x6b, + 0x44, 0x61, 0x39, 0x4a, 0x4c, 0x4f, 0x0a, 0x49, 0x59, 0x32, 0x50, 0x41, + 0x32, 0x76, 0x46, 0x37, 0x32, 0x6f, 0x4b, 0x4b, 0x2f, 0x37, 0x6c, 0x36, + 0x31, 0x56, 0x57, 0x76, 0x63, 0x59, 0x6b, 0x39, 0x4b, 0x68, 0x49, 0x71, + 0x79, 0x37, 0x31, 0x66, 0x46, 0x61, 0x45, 0x7a, 0x31, 0x5a, 0x49, 0x31, + 0x61, 0x42, 0x36, 0x2f, 0x71, 0x56, 0x36, 0x66, 0x77, 0x71, 0x58, 0x69, + 0x79, 0x48, 0x44, 0x4a, 0x63, 0x7a, 0x71, 0x6a, 0x2f, 0x33, 0x31, 0x0a, + 0x38, 0x45, 0x48, 0x48, 0x4f, 0x51, 0x55, 0x44, 0x4d, 0x59, 0x34, 0x2f, + 0x4f, 0x55, 0x46, 0x2f, 0x56, 0x37, 0x6f, 0x6f, 0x4b, 0x64, 0x31, 0x33, + 0x67, 0x35, 0x72, 0x7a, 0x4c, 0x41, 0x50, 0x47, 0x4e, 0x74, 0x43, 0x37, + 0x6e, 0x2b, 0x56, 0x6b, 0x6a, 0x41, 0x31, 0x31, 0x78, 0x38, 0x4f, 0x6c, + 0x32, 0x57, 0x62, 0x48, 0x33, 0x64, 0x6a, 0x31, 0x78, 0x4f, 0x47, 0x2f, + 0x66, 0x50, 0x52, 0x57, 0x0a, 0x72, 0x63, 0x69, 0x57, 0x46, 0x61, 0x79, + 0x4a, 0x72, 0x33, 0x71, 0x56, 0x6c, 0x5a, 0x2b, 0x42, 0x79, 0x31, 0x66, + 0x44, 0x43, 0x59, 0x38, 0x31, 0x4d, 0x73, 0x55, 0x56, 0x41, 0x65, 0x55, + 0x57, 0x2f, 0x54, 0x33, 0x6b, 0x39, 0x42, 0x6e, 0x4b, 0x67, 0x36, 0x68, + 0x71, 0x61, 0x33, 0x4a, 0x47, 0x36, 0x37, 0x4e, 0x41, 0x36, 0x42, 0x72, + 0x63, 0x7a, 0x4e, 0x4b, 0x53, 0x4b, 0x47, 0x79, 0x37, 0x0a, 0x70, 0x70, + 0x56, 0x56, 0x41, 0x65, 0x61, 0x47, 0x44, 0x49, 0x73, 0x37, 0x62, 0x73, + 0x43, 0x7a, 0x52, 0x70, 0x45, 0x42, 0x43, 0x67, 0x4e, 0x68, 0x52, 0x42, + 0x5a, 0x45, 0x2f, 0x41, 0x79, 0x65, 0x77, 0x4d, 0x56, 0x6e, 0x42, 0x4a, + 0x62, 0x4b, 0x41, 0x72, 0x6e, 0x72, 0x79, 0x69, 0x69, 0x38, 0x2f, 0x5a, + 0x62, 0x56, 0x51, 0x55, 0x4d, 0x4c, 0x56, 0x48, 0x32, 0x70, 0x68, 0x34, + 0x46, 0x69, 0x0a, 0x6b, 0x2b, 0x30, 0x56, 0x66, 0x56, 0x45, 0x43, 0x67, + 0x59, 0x45, 0x41, 0x2f, 0x5a, 0x45, 0x39, 0x35, 0x71, 0x4b, 0x46, 0x6d, + 0x71, 0x51, 0x65, 0x4f, 0x36, 0x6d, 0x76, 0x42, 0x37, 0x6b, 0x56, 0x6b, + 0x70, 0x56, 0x4b, 0x53, 0x58, 0x47, 0x75, 0x2f, 0x59, 0x35, 0x6b, 0x46, + 0x63, 0x70, 0x4c, 0x57, 0x4f, 0x63, 0x35, 0x45, 0x56, 0x48, 0x4c, 0x36, + 0x46, 0x6d, 0x66, 0x7a, 0x62, 0x71, 0x42, 0x0a, 0x41, 0x78, 0x6f, 0x71, + 0x39, 0x48, 0x68, 0x66, 0x4c, 0x79, 0x39, 0x61, 0x37, 0x4e, 0x7a, 0x73, + 0x61, 0x43, 0x77, 0x30, 0x66, 0x34, 0x56, 0x79, 0x5a, 0x72, 0x73, 0x4f, + 0x61, 0x73, 0x67, 0x42, 0x31, 0x43, 0x42, 0x5a, 0x50, 0x36, 0x30, 0x32, + 0x43, 0x47, 0x69, 0x38, 0x68, 0x62, 0x48, 0x61, 0x6a, 0x4a, 0x4d, 0x77, + 0x4a, 0x67, 0x37, 0x42, 0x39, 0x64, 0x38, 0x52, 0x71, 0x62, 0x67, 0x35, + 0x0a, 0x76, 0x61, 0x6b, 0x45, 0x75, 0x64, 0x70, 0x42, 0x31, 0x68, 0x39, + 0x6a, 0x6d, 0x39, 0x4e, 0x4a, 0x35, 0x61, 0x63, 0x52, 0x37, 0x2f, 0x76, + 0x63, 0x64, 0x56, 0x70, 0x45, 0x67, 0x70, 0x6e, 0x32, 0x6e, 0x50, 0x38, + 0x34, 0x52, 0x6e, 0x2f, 0x4e, 0x36, 0x72, 0x65, 0x75, 0x77, 0x52, 0x56, + 0x61, 0x38, 0x2f, 0x75, 0x2b, 0x59, 0x65, 0x63, 0x43, 0x67, 0x59, 0x45, + 0x41, 0x32, 0x41, 0x70, 0x36, 0x0a, 0x4f, 0x36, 0x78, 0x4d, 0x32, 0x32, + 0x70, 0x4e, 0x44, 0x64, 0x33, 0x70, 0x35, 0x79, 0x50, 0x65, 0x38, 0x36, + 0x38, 0x66, 0x79, 0x66, 0x35, 0x56, 0x35, 0x2f, 0x32, 0x4b, 0x6f, 0x76, + 0x34, 0x76, 0x46, 0x38, 0x77, 0x66, 0x69, 0x73, 0x61, 0x45, 0x63, 0x66, + 0x62, 0x2f, 0x57, 0x62, 0x4a, 0x52, 0x67, 0x77, 0x63, 0x36, 0x2f, 0x6b, + 0x55, 0x55, 0x6b, 0x31, 0x6c, 0x4a, 0x6e, 0x78, 0x6e, 0x47, 0x0a, 0x50, + 0x4b, 0x39, 0x38, 0x48, 0x6d, 0x6a, 0x69, 0x64, 0x35, 0x59, 0x33, 0x39, + 0x64, 0x7a, 0x69, 0x6c, 0x58, 0x67, 0x74, 0x43, 0x36, 0x36, 0x58, 0x56, + 0x41, 0x75, 0x30, 0x4f, 0x4a, 0x30, 0x34, 0x6c, 0x71, 0x69, 0x6c, 0x73, + 0x4d, 0x34, 0x42, 0x53, 0x43, 0x64, 0x6b, 0x6c, 0x69, 0x6d, 0x70, 0x68, + 0x75, 0x46, 0x73, 0x7a, 0x4a, 0x33, 0x50, 0x4f, 0x74, 0x36, 0x55, 0x37, + 0x68, 0x67, 0x42, 0x0a, 0x31, 0x71, 0x73, 0x4b, 0x4d, 0x4b, 0x61, 0x75, + 0x74, 0x6a, 0x33, 0x2f, 0x38, 0x76, 0x32, 0x53, 0x77, 0x63, 0x71, 0x36, + 0x46, 0x49, 0x78, 0x36, 0x6c, 0x36, 0x46, 0x6f, 0x37, 0x71, 0x2b, 0x73, + 0x70, 0x74, 0x56, 0x72, 0x57, 0x65, 0x63, 0x43, 0x67, 0x59, 0x42, 0x2b, + 0x36, 0x63, 0x31, 0x54, 0x76, 0x4a, 0x43, 0x6d, 0x66, 0x2f, 0x4a, 0x70, + 0x35, 0x6c, 0x6f, 0x6d, 0x77, 0x57, 0x71, 0x63, 0x0a, 0x76, 0x59, 0x41, + 0x37, 0x46, 0x6c, 0x32, 0x42, 0x70, 0x31, 0x31, 0x4d, 0x30, 0x72, 0x32, + 0x33, 0x74, 0x37, 0x4f, 0x47, 0x69, 0x61, 0x78, 0x48, 0x6c, 0x57, 0x51, + 0x34, 0x73, 0x6c, 0x71, 0x55, 0x56, 0x4f, 0x71, 0x66, 0x42, 0x67, 0x69, + 0x4f, 0x4d, 0x32, 0x4f, 0x4e, 0x48, 0x6c, 0x35, 0x74, 0x48, 0x59, 0x4d, + 0x42, 0x4f, 0x4b, 0x65, 0x7a, 0x35, 0x33, 0x67, 0x6c, 0x31, 0x67, 0x6d, + 0x6b, 0x0a, 0x52, 0x4c, 0x53, 0x6d, 0x2f, 0x47, 0x6b, 0x49, 0x2b, 0x48, + 0x4d, 0x7a, 0x62, 0x33, 0x74, 0x31, 0x31, 0x4d, 0x33, 0x4b, 0x6e, 0x71, + 0x52, 0x53, 0x44, 0x64, 0x35, 0x6e, 0x56, 0x6b, 0x41, 0x41, 0x50, 0x37, + 0x4b, 0x50, 0x32, 0x30, 0x41, 0x4d, 0x6a, 0x68, 0x56, 0x72, 0x44, 0x75, + 0x76, 0x7a, 0x75, 0x42, 0x56, 0x77, 0x53, 0x6c, 0x31, 0x6a, 0x6c, 0x31, + 0x53, 0x6e, 0x45, 0x61, 0x79, 0x76, 0x0a, 0x6b, 0x38, 0x5a, 0x30, 0x38, + 0x73, 0x37, 0x37, 0x35, 0x67, 0x66, 0x66, 0x75, 0x48, 0x4b, 0x58, 0x6e, + 0x36, 0x38, 0x41, 0x6a, 0x51, 0x4b, 0x42, 0x67, 0x46, 0x58, 0x73, 0x4c, + 0x4e, 0x73, 0x6f, 0x31, 0x74, 0x52, 0x35, 0x50, 0x62, 0x59, 0x6a, 0x4b, + 0x56, 0x44, 0x39, 0x69, 0x6b, 0x47, 0x65, 0x78, 0x2b, 0x54, 0x64, 0x57, + 0x36, 0x74, 0x4e, 0x77, 0x6d, 0x4b, 0x36, 0x39, 0x31, 0x33, 0x65, 0x0a, + 0x6d, 0x44, 0x6a, 0x6f, 0x5a, 0x57, 0x71, 0x79, 0x45, 0x72, 0x4c, 0x49, + 0x34, 0x66, 0x52, 0x62, 0x33, 0x74, 0x47, 0x63, 0x42, 0x65, 0x66, 0x6f, + 0x6e, 0x67, 0x68, 0x43, 0x42, 0x76, 0x37, 0x42, 0x79, 0x48, 0x71, 0x4c, + 0x75, 0x6d, 0x35, 0x58, 0x64, 0x32, 0x41, 0x34, 0x66, 0x6f, 0x46, 0x31, + 0x37, 0x32, 0x78, 0x79, 0x2f, 0x73, 0x71, 0x31, 0x63, 0x50, 0x4d, 0x48, + 0x54, 0x4b, 0x64, 0x57, 0x0a, 0x34, 0x62, 0x63, 0x6b, 0x35, 0x34, 0x75, + 0x62, 0x35, 0x7a, 0x78, 0x31, 0x79, 0x32, 0x2f, 0x53, 0x6e, 0x69, 0x50, + 0x76, 0x4b, 0x36, 0x6b, 0x39, 0x4e, 0x7a, 0x78, 0x4f, 0x6e, 0x67, 0x53, + 0x54, 0x75, 0x42, 0x54, 0x4c, 0x5a, 0x6a, 0x63, 0x6a, 0x42, 0x33, 0x58, + 0x4c, 0x39, 0x68, 0x39, 0x50, 0x45, 0x70, 0x7a, 0x7a, 0x6c, 0x68, 0x70, + 0x53, 0x58, 0x74, 0x70, 0x33, 0x55, 0x68, 0x4a, 0x30, 0x0a, 0x56, 0x53, + 0x4c, 0x48, 0x41, 0x6f, 0x47, 0x41, 0x44, 0x4e, 0x79, 0x6b, 0x52, 0x50, + 0x32, 0x55, 0x38, 0x34, 0x78, 0x70, 0x50, 0x6d, 0x36, 0x77, 0x31, 0x69, + 0x36, 0x72, 0x47, 0x48, 0x71, 0x50, 0x38, 0x67, 0x65, 0x2b, 0x4f, 0x6f, + 0x6f, 0x77, 0x69, 0x47, 0x47, 0x4b, 0x48, 0x54, 0x45, 0x48, 0x72, 0x56, + 0x39, 0x53, 0x79, 0x66, 0x6c, 0x2f, 0x63, 0x42, 0x39, 0x55, 0x4d, 0x65, + 0x79, 0x43, 0x0a, 0x45, 0x49, 0x68, 0x46, 0x37, 0x4e, 0x71, 0x6b, 0x35, + 0x76, 0x2b, 0x4d, 0x68, 0x2b, 0x50, 0x31, 0x52, 0x49, 0x52, 0x71, 0x35, + 0x39, 0x66, 0x4d, 0x41, 0x34, 0x72, 0x4e, 0x7a, 0x6d, 0x61, 0x78, 0x65, + 0x6b, 0x35, 0x72, 0x4b, 0x36, 0x45, 0x50, 0x67, 0x31, 0x62, 0x2f, 0x67, + 0x78, 0x7a, 0x45, 0x73, 0x4c, 0x56, 0x45, 0x45, 0x42, 0x58, 0x49, 0x51, + 0x35, 0x57, 0x57, 0x6e, 0x58, 0x6a, 0x52, 0x0a, 0x75, 0x2b, 0x5a, 0x56, + 0x49, 0x37, 0x33, 0x68, 0x62, 0x36, 0x79, 0x56, 0x56, 0x70, 0x75, 0x6f, + 0x4e, 0x68, 0x52, 0x43, 0x67, 0x59, 0x67, 0x45, 0x70, 0x48, 0x6b, 0x6e, + 0x47, 0x43, 0x45, 0x76, 0x62, 0x55, 0x52, 0x4b, 0x4f, 0x68, 0x79, 0x44, + 0x4d, 0x4e, 0x69, 0x6c, 0x49, 0x6a, 0x31, 0x6d, 0x71, 0x44, 0x4d, 0x3d, + 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x52, 0x53, + 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, + 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a +}; + +unsigned char ca_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x44, + 0x48, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x51, + 0x43, + 0x43, + 0x51, + 0x44, + 0x43, + 0x44, + 0x35, + 0x33, + 0x59, + 0x5a, + 0x4a, + 0x4a, + 0x37, + 0x6c, + 0x6a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x6b, + 0x71, + 0x68, + 0x6b, + 0x69, + 0x47, + 0x39, + 0x77, + 0x30, + 0x42, + 0x41, + 0x51, + 0x73, + 0x46, + 0x41, + 0x44, + 0x42, + 0x50, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x63, + 0x54, + 0x43, + 0x56, + 0x68, + 0x70, + 0x59, + 0x57, + 0x39, + 0x69, + 0x61, + 0x58, + 0x52, + 0x68, + 0x62, + 0x6a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x0a, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x41, + 0x67, + 0x46, + 0x77, + 0x30, + 0x79, + 0x4d, + 0x6a, + 0x41, + 0x33, + 0x4d, + 0x44, + 0x59, + 0x78, + 0x4d, + 0x54, + 0x49, + 0x30, + 0x4d, + 0x6a, + 0x42, + 0x61, + 0x47, + 0x41, + 0x38, + 0x79, + 0x4d, + 0x44, + 0x55, + 0x77, + 0x4d, + 0x44, + 0x63, + 0x78, + 0x4f, + 0x54, + 0x45, + 0x78, + 0x0a, + 0x4d, + 0x6a, + 0x51, + 0x79, + 0x4d, + 0x46, + 0x6f, + 0x77, + 0x54, + 0x7a, + 0x45, + 0x62, + 0x4d, + 0x42, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x68, + 0x4d, + 0x53, + 0x62, + 0x47, + 0x6c, + 0x69, + 0x64, + 0x32, + 0x56, + 0x69, + 0x63, + 0x32, + 0x39, + 0x6a, + 0x61, + 0x32, + 0x56, + 0x30, + 0x63, + 0x79, + 0x31, + 0x30, + 0x5a, + 0x58, + 0x4e, + 0x30, + 0x4d, + 0x52, + 0x49, + 0x77, + 0x45, + 0x41, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x48, + 0x45, + 0x77, + 0x6c, + 0x59, + 0x0a, + 0x61, + 0x57, + 0x46, + 0x76, + 0x59, + 0x6d, + 0x6c, + 0x30, + 0x59, + 0x57, + 0x34, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x67, + 0x54, + 0x42, + 0x6c, + 0x52, + 0x68, + 0x61, + 0x58, + 0x42, + 0x6c, + 0x61, + 0x54, + 0x45, + 0x4c, + 0x4d, + 0x41, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x68, + 0x4d, + 0x43, + 0x56, + 0x46, + 0x63, + 0x77, + 0x67, + 0x67, + 0x45, + 0x69, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x0a, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x41, + 0x51, + 0x55, + 0x41, + 0x41, + 0x34, + 0x49, + 0x42, + 0x44, + 0x77, + 0x41, + 0x77, + 0x67, + 0x67, + 0x45, + 0x4b, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x51, + 0x44, + 0x56, + 0x2f, + 0x4f, + 0x7a, + 0x35, + 0x56, + 0x73, + 0x58, + 0x33, + 0x56, + 0x34, + 0x52, + 0x38, + 0x76, + 0x61, + 0x41, + 0x4e, + 0x79, + 0x50, + 0x49, + 0x4d, + 0x61, + 0x46, + 0x73, + 0x79, + 0x67, + 0x4d, + 0x65, + 0x57, + 0x70, + 0x4b, + 0x61, + 0x6e, + 0x0a, + 0x48, + 0x4d, + 0x54, + 0x34, + 0x6b, + 0x66, + 0x69, + 0x67, + 0x55, + 0x59, + 0x55, + 0x48, + 0x55, + 0x59, + 0x65, + 0x4c, + 0x6b, + 0x62, + 0x50, + 0x65, + 0x77, + 0x5a, + 0x41, + 0x79, + 0x63, + 0x43, + 0x66, + 0x59, + 0x73, + 0x43, + 0x64, + 0x42, + 0x4a, + 0x6d, + 0x73, + 0x58, + 0x43, + 0x58, + 0x43, + 0x73, + 0x66, + 0x69, + 0x45, + 0x4c, + 0x70, + 0x75, + 0x59, + 0x6e, + 0x65, + 0x70, + 0x52, + 0x65, + 0x74, + 0x72, + 0x5a, + 0x32, + 0x39, + 0x77, + 0x54, + 0x6a, + 0x2f, + 0x62, + 0x2b, + 0x6d, + 0x0a, + 0x32, + 0x54, + 0x6a, + 0x55, + 0x62, + 0x32, + 0x47, + 0x34, + 0x51, + 0x36, + 0x37, + 0x43, + 0x4b, + 0x53, + 0x48, + 0x4e, + 0x6c, + 0x68, + 0x36, + 0x43, + 0x51, + 0x71, + 0x46, + 0x76, + 0x45, + 0x57, + 0x6e, + 0x42, + 0x58, + 0x2b, + 0x38, + 0x66, + 0x59, + 0x65, + 0x68, + 0x51, + 0x54, + 0x33, + 0x51, + 0x77, + 0x4a, + 0x58, + 0x4e, + 0x39, + 0x68, + 0x56, + 0x42, + 0x75, + 0x50, + 0x78, + 0x44, + 0x2b, + 0x78, + 0x41, + 0x2b, + 0x62, + 0x49, + 0x36, + 0x5a, + 0x32, + 0x38, + 0x57, + 0x30, + 0x6b, + 0x0a, + 0x41, + 0x30, + 0x5a, + 0x4c, + 0x39, + 0x46, + 0x66, + 0x57, + 0x43, + 0x54, + 0x36, + 0x66, + 0x4f, + 0x44, + 0x78, + 0x4d, + 0x37, + 0x65, + 0x67, + 0x6b, + 0x53, + 0x53, + 0x6d, + 0x4b, + 0x65, + 0x44, + 0x67, + 0x54, + 0x55, + 0x73, + 0x6e, + 0x35, + 0x77, + 0x64, + 0x38, + 0x52, + 0x38, + 0x6b, + 0x51, + 0x68, + 0x6b, + 0x34, + 0x79, + 0x49, + 0x32, + 0x4b, + 0x50, + 0x55, + 0x79, + 0x73, + 0x2b, + 0x6e, + 0x59, + 0x78, + 0x7a, + 0x6f, + 0x77, + 0x6a, + 0x4e, + 0x4f, + 0x69, + 0x38, + 0x75, + 0x68, + 0x0a, + 0x44, + 0x58, + 0x7a, + 0x72, + 0x2b, + 0x46, + 0x78, + 0x67, + 0x2f, + 0x70, + 0x73, + 0x36, + 0x38, + 0x56, + 0x69, + 0x5a, + 0x6a, + 0x47, + 0x46, + 0x67, + 0x58, + 0x53, + 0x46, + 0x48, + 0x77, + 0x35, + 0x5a, + 0x49, + 0x76, + 0x52, + 0x51, + 0x54, + 0x47, + 0x49, + 0x32, + 0x68, + 0x30, + 0x54, + 0x39, + 0x71, + 0x6d, + 0x48, + 0x50, + 0x30, + 0x42, + 0x56, + 0x46, + 0x79, + 0x62, + 0x4a, + 0x75, + 0x39, + 0x73, + 0x36, + 0x58, + 0x44, + 0x59, + 0x75, + 0x6f, + 0x4d, + 0x38, + 0x45, + 0x4a, + 0x73, + 0x0a, + 0x57, + 0x53, + 0x73, + 0x2b, + 0x34, + 0x76, + 0x4f, + 0x61, + 0x63, + 0x61, + 0x54, + 0x45, + 0x56, + 0x59, + 0x41, + 0x51, + 0x46, + 0x35, + 0x48, + 0x78, + 0x41, + 0x4c, + 0x66, + 0x43, + 0x74, + 0x61, + 0x4f, + 0x65, + 0x50, + 0x6a, + 0x53, + 0x70, + 0x2b, + 0x66, + 0x66, + 0x45, + 0x2b, + 0x55, + 0x30, + 0x43, + 0x78, + 0x59, + 0x46, + 0x33, + 0x4e, + 0x48, + 0x39, + 0x33, + 0x59, + 0x4b, + 0x5a, + 0x78, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x0a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x49, + 0x6d, + 0x4a, + 0x76, + 0x71, + 0x6e, + 0x70, + 0x50, + 0x64, + 0x53, + 0x75, + 0x46, + 0x4f, + 0x5a, + 0x54, + 0x36, + 0x76, + 0x74, + 0x48, + 0x31, + 0x70, + 0x4a, + 0x43, + 0x45, + 0x76, + 0x4a, + 0x39, + 0x62, + 0x53, + 0x78, + 0x31, + 0x43, + 0x41, + 0x76, + 0x36, + 0x46, + 0x34, + 0x46, + 0x44, + 0x6f, + 0x77, + 0x4b, + 0x77, + 0x0a, + 0x43, + 0x71, + 0x4b, + 0x53, + 0x6c, + 0x59, + 0x45, + 0x6a, + 0x72, + 0x49, + 0x4c, + 0x64, + 0x6c, + 0x42, + 0x30, + 0x39, + 0x32, + 0x31, + 0x4f, + 0x54, + 0x30, + 0x76, + 0x61, + 0x68, + 0x33, + 0x6c, + 0x55, + 0x76, + 0x2f, + 0x6b, + 0x47, + 0x4e, + 0x4c, + 0x76, + 0x58, + 0x55, + 0x71, + 0x54, + 0x69, + 0x42, + 0x61, + 0x6b, + 0x77, + 0x66, + 0x52, + 0x47, + 0x30, + 0x39, + 0x61, + 0x49, + 0x45, + 0x6e, + 0x53, + 0x68, + 0x6d, + 0x79, + 0x6f, + 0x30, + 0x68, + 0x63, + 0x65, + 0x4f, + 0x68, + 0x33, + 0x0a, + 0x4f, + 0x31, + 0x4b, + 0x4b, + 0x59, + 0x76, + 0x4a, + 0x32, + 0x6a, + 0x4a, + 0x47, + 0x6b, + 0x36, + 0x50, + 0x6c, + 0x52, + 0x78, + 0x65, + 0x53, + 0x67, + 0x37, + 0x64, + 0x35, + 0x4d, + 0x69, + 0x37, + 0x58, + 0x67, + 0x6e, + 0x41, + 0x64, + 0x65, + 0x61, + 0x78, + 0x77, + 0x68, + 0x75, + 0x76, + 0x5a, + 0x5a, + 0x6d, + 0x61, + 0x49, + 0x7a, + 0x62, + 0x68, + 0x41, + 0x57, + 0x50, + 0x38, + 0x71, + 0x67, + 0x49, + 0x30, + 0x36, + 0x50, + 0x32, + 0x52, + 0x42, + 0x53, + 0x35, + 0x42, + 0x4a, + 0x76, + 0x0a, + 0x72, + 0x44, + 0x44, + 0x33, + 0x44, + 0x68, + 0x77, + 0x38, + 0x4e, + 0x38, + 0x47, + 0x77, + 0x42, + 0x44, + 0x31, + 0x52, + 0x59, + 0x32, + 0x79, + 0x4b, + 0x72, + 0x79, + 0x46, + 0x51, + 0x2b, + 0x34, + 0x55, + 0x32, + 0x31, + 0x45, + 0x72, + 0x73, + 0x77, + 0x2f, + 0x33, + 0x38, + 0x63, + 0x59, + 0x38, + 0x55, + 0x41, + 0x46, + 0x54, + 0x6b, + 0x67, + 0x33, + 0x72, + 0x57, + 0x72, + 0x34, + 0x44, + 0x57, + 0x78, + 0x36, + 0x74, + 0x6e, + 0x49, + 0x66, + 0x64, + 0x72, + 0x4e, + 0x31, + 0x49, + 0x49, + 0x0a, + 0x70, + 0x52, + 0x71, + 0x53, + 0x78, + 0x48, + 0x51, + 0x57, + 0x34, + 0x6b, + 0x5a, + 0x61, + 0x67, + 0x31, + 0x75, + 0x5a, + 0x64, + 0x46, + 0x63, + 0x53, + 0x69, + 0x61, + 0x59, + 0x54, + 0x2b, + 0x65, + 0x62, + 0x4c, + 0x57, + 0x56, + 0x58, + 0x41, + 0x7a, + 0x62, + 0x66, + 0x4a, + 0x4c, + 0x4f, + 0x56, + 0x38, + 0x45, + 0x46, + 0x46, + 0x33, + 0x52, + 0x44, + 0x43, + 0x2f, + 0x41, + 0x33, + 0x55, + 0x33, + 0x56, + 0x63, + 0x4f, + 0x52, + 0x44, + 0x42, + 0x44, + 0x57, + 0x44, + 0x68, + 0x6d, + 0x68, + 0x0a, + 0x38, + 0x72, + 0x34, + 0x54, + 0x2b, + 0x36, + 0x46, + 0x67, + 0x67, + 0x59, + 0x4c, + 0x32, + 0x65, + 0x6a, + 0x4d, + 0x6d, + 0x66, + 0x77, + 0x62, + 0x65, + 0x77, + 0x7a, + 0x49, + 0x2b, + 0x48, + 0x6d, + 0x34, + 0x53, + 0x69, + 0x64, + 0x67, + 0x4f, + 0x78, + 0x4c, + 0x66, + 0x31, + 0x46, + 0x78, + 0x31, + 0x64, + 0x32, + 0x76, + 0x34, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char client_key[] = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x52, + 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, + 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x4a, + 0x4b, 0x51, 0x49, 0x42, 0x41, 0x41, 0x4b, 0x43, 0x41, 0x67, 0x45, 0x41, + 0x72, 0x77, 0x53, 0x79, 0x4e, 0x6e, 0x68, 0x66, 0x76, 0x44, 0x69, 0x63, + 0x47, 0x4a, 0x4f, 0x42, 0x66, 0x55, 0x51, 0x4c, 0x47, 0x45, 0x70, 0x4b, + 0x43, 0x32, 0x2f, 0x4e, 0x32, 0x35, 0x54, 0x6f, 0x32, 0x4b, 0x66, 0x36, + 0x36, 0x7a, 0x6b, 0x44, 0x62, 0x33, 0x6d, 0x34, 0x49, 0x50, 0x77, 0x4c, + 0x0a, 0x63, 0x63, 0x78, 0x47, 0x57, 0x48, 0x57, 0x75, 0x65, 0x78, 0x69, + 0x70, 0x6d, 0x76, 0x41, 0x77, 0x46, 0x2b, 0x58, 0x32, 0x36, 0x46, 0x47, + 0x7a, 0x35, 0x66, 0x4a, 0x64, 0x71, 0x38, 0x46, 0x68, 0x77, 0x6c, 0x71, + 0x2b, 0x6e, 0x71, 0x6a, 0x34, 0x50, 0x68, 0x6c, 0x2f, 0x51, 0x2b, 0x77, + 0x55, 0x42, 0x6c, 0x51, 0x2b, 0x58, 0x4d, 0x6a, 0x71, 0x49, 0x70, 0x77, + 0x79, 0x7a, 0x70, 0x5a, 0x6d, 0x0a, 0x34, 0x48, 0x6d, 0x2b, 0x4f, 0x4f, + 0x43, 0x4b, 0x66, 0x73, 0x79, 0x58, 0x64, 0x6d, 0x4c, 0x4c, 0x70, 0x46, + 0x79, 0x56, 0x6b, 0x34, 0x72, 0x72, 0x32, 0x47, 0x51, 0x34, 0x4b, 0x46, + 0x53, 0x51, 0x58, 0x51, 0x58, 0x79, 0x5a, 0x78, 0x78, 0x62, 0x4e, 0x46, + 0x64, 0x67, 0x6e, 0x43, 0x72, 0x76, 0x6d, 0x74, 0x6b, 0x49, 0x42, 0x51, + 0x62, 0x76, 0x6d, 0x6e, 0x42, 0x79, 0x50, 0x2b, 0x77, 0x6e, 0x0a, 0x65, + 0x7a, 0x68, 0x55, 0x51, 0x4b, 0x4c, 0x6b, 0x6d, 0x51, 0x50, 0x32, 0x53, + 0x6a, 0x37, 0x65, 0x52, 0x7a, 0x4c, 0x6f, 0x79, 0x58, 0x32, 0x71, 0x41, + 0x49, 0x75, 0x43, 0x46, 0x59, 0x62, 0x57, 0x41, 0x36, 0x44, 0x57, 0x53, + 0x39, 0x50, 0x4c, 0x56, 0x75, 0x67, 0x71, 0x53, 0x76, 0x4c, 0x65, 0x51, + 0x71, 0x7a, 0x31, 0x74, 0x70, 0x32, 0x66, 0x5a, 0x70, 0x68, 0x42, 0x64, + 0x6a, 0x66, 0x49, 0x0a, 0x6c, 0x65, 0x44, 0x42, 0x75, 0x7a, 0x59, 0x67, + 0x42, 0x30, 0x61, 0x63, 0x39, 0x79, 0x44, 0x48, 0x31, 0x6d, 0x65, 0x65, + 0x51, 0x4f, 0x43, 0x4e, 0x50, 0x61, 0x7a, 0x52, 0x41, 0x63, 0x57, 0x57, + 0x77, 0x75, 0x2b, 0x6f, 0x56, 0x57, 0x6d, 0x45, 0x42, 0x45, 0x6b, 0x50, + 0x44, 0x49, 0x31, 0x70, 0x68, 0x75, 0x6e, 0x44, 0x39, 0x33, 0x33, 0x47, + 0x31, 0x52, 0x63, 0x57, 0x64, 0x6d, 0x51, 0x55, 0x0a, 0x72, 0x37, 0x6b, + 0x48, 0x65, 0x50, 0x4d, 0x50, 0x77, 0x35, 0x70, 0x33, 0x42, 0x7a, 0x2b, + 0x71, 0x6a, 0x71, 0x61, 0x48, 0x2b, 0x36, 0x6f, 0x43, 0x54, 0x68, 0x5a, + 0x43, 0x6f, 0x2b, 0x59, 0x55, 0x39, 0x68, 0x51, 0x2f, 0x34, 0x2b, 0x48, + 0x77, 0x4d, 0x4e, 0x79, 0x38, 0x51, 0x73, 0x79, 0x6a, 0x61, 0x6d, 0x66, + 0x37, 0x31, 0x7a, 0x35, 0x67, 0x34, 0x43, 0x68, 0x65, 0x32, 0x34, 0x51, + 0x50, 0x0a, 0x66, 0x63, 0x6e, 0x30, 0x33, 0x57, 0x79, 0x2f, 0x5a, 0x52, + 0x61, 0x75, 0x53, 0x67, 0x73, 0x51, 0x6e, 0x79, 0x7a, 0x68, 0x72, 0x5a, + 0x7a, 0x39, 0x57, 0x4a, 0x35, 0x2b, 0x38, 0x77, 0x55, 0x71, 0x6a, 0x34, + 0x34, 0x51, 0x38, 0x45, 0x36, 0x70, 0x59, 0x52, 0x4c, 0x30, 0x68, 0x34, + 0x78, 0x65, 0x49, 0x73, 0x45, 0x4f, 0x76, 0x2b, 0x49, 0x77, 0x4e, 0x62, + 0x56, 0x59, 0x6d, 0x38, 0x57, 0x31, 0x0a, 0x62, 0x48, 0x5a, 0x62, 0x64, + 0x57, 0x31, 0x53, 0x65, 0x4d, 0x77, 0x70, 0x74, 0x45, 0x62, 0x37, 0x47, + 0x50, 0x67, 0x55, 0x61, 0x4f, 0x2f, 0x79, 0x4a, 0x7a, 0x2f, 0x68, 0x4b, + 0x71, 0x63, 0x72, 0x70, 0x49, 0x4a, 0x37, 0x2f, 0x49, 0x6f, 0x61, 0x41, + 0x54, 0x4b, 0x2b, 0x6a, 0x39, 0x70, 0x38, 0x32, 0x62, 0x33, 0x51, 0x69, + 0x32, 0x2f, 0x55, 0x54, 0x70, 0x71, 0x49, 0x44, 0x49, 0x67, 0x79, 0x0a, + 0x79, 0x5a, 0x41, 0x4c, 0x55, 0x6b, 0x36, 0x68, 0x57, 0x48, 0x39, 0x37, + 0x53, 0x6f, 0x4d, 0x61, 0x71, 0x6b, 0x68, 0x55, 0x6a, 0x73, 0x44, 0x38, + 0x36, 0x7a, 0x50, 0x32, 0x31, 0x4b, 0x48, 0x45, 0x4e, 0x35, 0x47, 0x35, + 0x6b, 0x58, 0x33, 0x36, 0x41, 0x77, 0x79, 0x66, 0x53, 0x45, 0x41, 0x6d, + 0x52, 0x76, 0x6c, 0x48, 0x63, 0x7a, 0x47, 0x6a, 0x78, 0x66, 0x71, 0x51, + 0x50, 0x42, 0x71, 0x7a, 0x0a, 0x4d, 0x63, 0x65, 0x39, 0x46, 0x53, 0x78, + 0x39, 0x4e, 0x2b, 0x52, 0x39, 0x62, 0x77, 0x2f, 0x56, 0x42, 0x73, 0x66, + 0x43, 0x63, 0x7a, 0x46, 0x35, 0x4c, 0x4b, 0x6d, 0x37, 0x52, 0x6f, 0x6a, + 0x48, 0x44, 0x35, 0x7a, 0x77, 0x4f, 0x62, 0x4f, 0x33, 0x36, 0x57, 0x7a, + 0x74, 0x75, 0x50, 0x61, 0x62, 0x62, 0x6f, 0x66, 0x7a, 0x30, 0x6d, 0x46, + 0x69, 0x56, 0x79, 0x75, 0x46, 0x33, 0x63, 0x4f, 0x62, 0x0a, 0x2f, 0x74, + 0x71, 0x47, 0x56, 0x56, 0x39, 0x33, 0x6e, 0x49, 0x72, 0x41, 0x42, 0x62, + 0x79, 0x2f, 0x44, 0x62, 0x42, 0x62, 0x49, 0x51, 0x4a, 0x58, 0x4c, 0x73, + 0x73, 0x74, 0x6e, 0x70, 0x77, 0x70, 0x6c, 0x5a, 0x62, 0x61, 0x5a, 0x6b, + 0x59, 0x71, 0x50, 0x76, 0x4f, 0x37, 0x47, 0x52, 0x56, 0x37, 0x30, 0x48, + 0x38, 0x77, 0x4e, 0x4f, 0x35, 0x4f, 0x66, 0x33, 0x4d, 0x43, 0x41, 0x77, + 0x45, 0x41, 0x0a, 0x41, 0x51, 0x4b, 0x43, 0x41, 0x67, 0x42, 0x6c, 0x78, + 0x33, 0x56, 0x62, 0x39, 0x2b, 0x53, 0x30, 0x73, 0x4c, 0x63, 0x57, 0x45, + 0x37, 0x48, 0x61, 0x42, 0x78, 0x66, 0x73, 0x71, 0x45, 0x63, 0x6e, 0x48, + 0x33, 0x32, 0x33, 0x6c, 0x49, 0x46, 0x55, 0x66, 0x56, 0x75, 0x4f, 0x4c, + 0x7a, 0x6d, 0x77, 0x4f, 0x6a, 0x69, 0x35, 0x39, 0x64, 0x6b, 0x78, 0x39, + 0x48, 0x6c, 0x30, 0x4e, 0x2f, 0x75, 0x66, 0x0a, 0x32, 0x6c, 0x66, 0x48, + 0x6f, 0x71, 0x5a, 0x56, 0x50, 0x34, 0x61, 0x32, 0x30, 0x38, 0x79, 0x71, + 0x6a, 0x4b, 0x65, 0x73, 0x6d, 0x6d, 0x6b, 0x66, 0x66, 0x57, 0x59, 0x64, + 0x48, 0x6d, 0x59, 0x2b, 0x74, 0x74, 0x55, 0x72, 0x79, 0x72, 0x35, 0x61, + 0x62, 0x2b, 0x4e, 0x55, 0x55, 0x67, 0x4c, 0x57, 0x33, 0x62, 0x38, 0x75, + 0x4a, 0x49, 0x6a, 0x76, 0x51, 0x64, 0x30, 0x39, 0x64, 0x6c, 0x63, 0x55, + 0x0a, 0x4a, 0x69, 0x5a, 0x75, 0x30, 0x6b, 0x56, 0x61, 0x37, 0x2f, 0x79, + 0x4d, 0x4d, 0x4e, 0x32, 0x32, 0x6d, 0x5a, 0x47, 0x4f, 0x34, 0x70, 0x36, + 0x52, 0x65, 0x6b, 0x50, 0x64, 0x63, 0x73, 0x41, 0x58, 0x55, 0x44, 0x6a, + 0x2b, 0x6d, 0x48, 0x6c, 0x2b, 0x73, 0x33, 0x66, 0x57, 0x64, 0x4a, 0x49, + 0x69, 0x58, 0x67, 0x49, 0x53, 0x36, 0x6d, 0x4b, 0x4c, 0x5a, 0x64, 0x61, + 0x5a, 0x51, 0x43, 0x46, 0x77, 0x0a, 0x57, 0x72, 0x31, 0x2f, 0x72, 0x38, + 0x2f, 0x54, 0x31, 0x2b, 0x64, 0x49, 0x52, 0x61, 0x76, 0x33, 0x5a, 0x53, + 0x6e, 0x68, 0x47, 0x75, 0x69, 0x61, 0x63, 0x34, 0x34, 0x72, 0x79, 0x70, + 0x38, 0x56, 0x69, 0x79, 0x34, 0x4e, 0x4a, 0x2b, 0x2f, 0x5a, 0x46, 0x6e, + 0x78, 0x4f, 0x46, 0x70, 0x76, 0x38, 0x4c, 0x63, 0x37, 0x6a, 0x30, 0x4d, + 0x7a, 0x31, 0x58, 0x42, 0x39, 0x4e, 0x6e, 0x38, 0x78, 0x41, 0x0a, 0x62, + 0x76, 0x41, 0x5a, 0x52, 0x78, 0x62, 0x76, 0x75, 0x4a, 0x4a, 0x76, 0x61, + 0x43, 0x61, 0x37, 0x46, 0x79, 0x54, 0x30, 0x77, 0x74, 0x4e, 0x4a, 0x79, + 0x64, 0x55, 0x36, 0x31, 0x6f, 0x48, 0x50, 0x66, 0x43, 0x30, 0x6b, 0x50, + 0x35, 0x68, 0x2b, 0x76, 0x4c, 0x4d, 0x5a, 0x32, 0x69, 0x73, 0x6e, 0x41, + 0x4b, 0x59, 0x76, 0x63, 0x30, 0x51, 0x55, 0x62, 0x4b, 0x58, 0x4c, 0x30, + 0x49, 0x33, 0x36, 0x0a, 0x55, 0x6e, 0x6d, 0x6c, 0x33, 0x59, 0x42, 0x79, + 0x4e, 0x4b, 0x59, 0x66, 0x31, 0x46, 0x35, 0x43, 0x79, 0x75, 0x38, 0x32, + 0x49, 0x54, 0x64, 0x50, 0x59, 0x37, 0x64, 0x43, 0x39, 0x39, 0x55, 0x6f, + 0x6e, 0x2f, 0x47, 0x35, 0x66, 0x52, 0x55, 0x56, 0x6e, 0x61, 0x6d, 0x58, + 0x34, 0x41, 0x2b, 0x4e, 0x62, 0x66, 0x47, 0x45, 0x68, 0x6c, 0x4a, 0x32, + 0x58, 0x58, 0x55, 0x51, 0x58, 0x54, 0x69, 0x68, 0x0a, 0x41, 0x77, 0x46, + 0x75, 0x5a, 0x48, 0x72, 0x54, 0x48, 0x41, 0x4a, 0x58, 0x39, 0x64, 0x6f, + 0x77, 0x34, 0x4c, 0x51, 0x51, 0x75, 0x49, 0x68, 0x44, 0x50, 0x4f, 0x59, + 0x55, 0x4e, 0x7a, 0x5a, 0x6f, 0x34, 0x57, 0x30, 0x34, 0x44, 0x44, 0x4a, + 0x2b, 0x62, 0x75, 0x66, 0x34, 0x67, 0x7a, 0x70, 0x4e, 0x68, 0x31, 0x39, + 0x59, 0x52, 0x65, 0x42, 0x44, 0x75, 0x7a, 0x67, 0x33, 0x43, 0x54, 0x62, + 0x59, 0x0a, 0x4b, 0x38, 0x52, 0x45, 0x6e, 0x58, 0x76, 0x4d, 0x59, 0x34, + 0x4b, 0x44, 0x36, 0x63, 0x69, 0x4d, 0x4f, 0x72, 0x61, 0x78, 0x4a, 0x53, + 0x31, 0x43, 0x49, 0x6d, 0x67, 0x53, 0x70, 0x68, 0x56, 0x70, 0x50, 0x37, + 0x54, 0x77, 0x4f, 0x4f, 0x6c, 0x62, 0x75, 0x78, 0x6a, 0x39, 0x76, 0x31, + 0x45, 0x63, 0x54, 0x41, 0x78, 0x34, 0x49, 0x68, 0x31, 0x38, 0x71, 0x72, + 0x71, 0x6c, 0x36, 0x32, 0x32, 0x56, 0x0a, 0x34, 0x45, 0x70, 0x34, 0x6c, + 0x62, 0x2f, 0x4a, 0x78, 0x57, 0x6e, 0x58, 0x4e, 0x48, 0x77, 0x2f, 0x7a, + 0x32, 0x64, 0x70, 0x64, 0x4d, 0x55, 0x6b, 0x4a, 0x4a, 0x66, 0x33, 0x6c, + 0x6d, 0x6e, 0x2b, 0x2f, 0x34, 0x5a, 0x56, 0x2b, 0x67, 0x4e, 0x42, 0x74, + 0x42, 0x79, 0x71, 0x4c, 0x52, 0x49, 0x50, 0x54, 0x47, 0x32, 0x6c, 0x37, + 0x4f, 0x34, 0x55, 0x6e, 0x52, 0x2f, 0x32, 0x69, 0x75, 0x71, 0x78, 0x0a, + 0x30, 0x77, 0x4b, 0x70, 0x59, 0x37, 0x54, 0x68, 0x34, 0x49, 0x66, 0x58, + 0x6a, 0x53, 0x56, 0x57, 0x36, 0x73, 0x4f, 0x63, 0x2b, 0x4a, 0x77, 0x37, + 0x64, 0x35, 0x68, 0x73, 0x56, 0x2f, 0x7a, 0x37, 0x41, 0x55, 0x52, 0x45, + 0x45, 0x4a, 0x34, 0x57, 0x58, 0x36, 0x4e, 0x4e, 0x70, 0x42, 0x77, 0x51, + 0x5a, 0x4e, 0x32, 0x56, 0x78, 0x48, 0x30, 0x37, 0x49, 0x78, 0x79, 0x76, + 0x6b, 0x33, 0x5a, 0x51, 0x0a, 0x58, 0x67, 0x56, 0x51, 0x58, 0x4e, 0x68, + 0x72, 0x79, 0x4e, 0x43, 0x49, 0x57, 0x70, 0x74, 0x2f, 0x66, 0x57, 0x69, + 0x76, 0x75, 0x50, 0x43, 0x7a, 0x77, 0x74, 0x32, 0x55, 0x74, 0x4f, 0x72, + 0x70, 0x2f, 0x35, 0x57, 0x43, 0x35, 0x37, 0x77, 0x31, 0x65, 0x65, 0x4d, + 0x73, 0x58, 0x63, 0x68, 0x72, 0x41, 0x51, 0x4b, 0x43, 0x41, 0x51, 0x45, + 0x41, 0x35, 0x4b, 0x48, 0x7a, 0x4c, 0x34, 0x76, 0x57, 0x0a, 0x42, 0x58, + 0x65, 0x66, 0x6f, 0x36, 0x2f, 0x4b, 0x68, 0x77, 0x49, 0x4a, 0x41, 0x37, + 0x79, 0x35, 0x54, 0x53, 0x67, 0x41, 0x62, 0x44, 0x68, 0x59, 0x70, 0x51, + 0x69, 0x34, 0x32, 0x4c, 0x6c, 0x74, 0x33, 0x37, 0x4e, 0x4d, 0x6e, 0x4a, + 0x63, 0x79, 0x4b, 0x54, 0x56, 0x6d, 0x79, 0x78, 0x70, 0x66, 0x59, 0x47, + 0x38, 0x65, 0x6e, 0x70, 0x31, 0x46, 0x71, 0x7a, 0x54, 0x56, 0x59, 0x74, + 0x63, 0x65, 0x0a, 0x5a, 0x49, 0x77, 0x33, 0x65, 0x39, 0x54, 0x35, 0x48, + 0x47, 0x4e, 0x71, 0x55, 0x6a, 0x70, 0x6f, 0x78, 0x43, 0x70, 0x35, 0x33, + 0x4b, 0x63, 0x44, 0x75, 0x38, 0x69, 0x47, 0x46, 0x36, 0x43, 0x51, 0x71, + 0x4a, 0x4d, 0x46, 0x72, 0x6b, 0x38, 0x47, 0x6b, 0x56, 0x51, 0x55, 0x38, + 0x31, 0x71, 0x58, 0x75, 0x57, 0x6f, 0x41, 0x50, 0x32, 0x79, 0x4a, 0x66, + 0x51, 0x53, 0x6d, 0x42, 0x75, 0x68, 0x67, 0x0a, 0x50, 0x64, 0x48, 0x4b, + 0x55, 0x4b, 0x31, 0x68, 0x47, 0x31, 0x6f, 0x36, 0x32, 0x44, 0x48, 0x32, + 0x67, 0x57, 0x33, 0x65, 0x6e, 0x37, 0x6a, 0x33, 0x70, 0x78, 0x43, 0x70, + 0x34, 0x4e, 0x68, 0x70, 0x39, 0x49, 0x70, 0x2f, 0x35, 0x39, 0x67, 0x30, + 0x4c, 0x73, 0x50, 0x44, 0x42, 0x67, 0x36, 0x6e, 0x59, 0x69, 0x38, 0x78, + 0x35, 0x61, 0x44, 0x4e, 0x6f, 0x59, 0x6d, 0x76, 0x45, 0x56, 0x45, 0x41, + 0x0a, 0x35, 0x46, 0x52, 0x7a, 0x37, 0x75, 0x2f, 0x77, 0x77, 0x67, 0x79, + 0x6d, 0x47, 0x49, 0x50, 0x67, 0x57, 0x70, 0x59, 0x47, 0x33, 0x63, 0x35, + 0x6b, 0x73, 0x31, 0x6e, 0x51, 0x41, 0x5a, 0x47, 0x53, 0x61, 0x37, 0x64, + 0x4c, 0x46, 0x74, 0x44, 0x4d, 0x45, 0x59, 0x4f, 0x78, 0x34, 0x37, 0x36, + 0x77, 0x42, 0x75, 0x48, 0x41, 0x56, 0x53, 0x57, 0x53, 0x41, 0x70, 0x64, + 0x55, 0x4d, 0x38, 0x56, 0x77, 0x0a, 0x6b, 0x2f, 0x6b, 0x63, 0x66, 0x2f, + 0x76, 0x61, 0x6f, 0x33, 0x4f, 0x7a, 0x71, 0x65, 0x33, 0x6f, 0x73, 0x37, + 0x37, 0x43, 0x6f, 0x46, 0x52, 0x77, 0x5a, 0x4d, 0x4e, 0x61, 0x7a, 0x2f, + 0x46, 0x2f, 0x7a, 0x35, 0x53, 0x77, 0x63, 0x47, 0x77, 0x42, 0x6f, 0x65, + 0x34, 0x51, 0x67, 0x69, 0x42, 0x6f, 0x33, 0x39, 0x4b, 0x58, 0x52, 0x33, + 0x5a, 0x63, 0x57, 0x33, 0x48, 0x63, 0x68, 0x76, 0x7a, 0x7a, 0x0a, 0x46, + 0x38, 0x54, 0x36, 0x6d, 0x38, 0x30, 0x78, 0x61, 0x7a, 0x67, 0x54, 0x6a, + 0x51, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, 0x77, 0x2f, 0x66, 0x55, 0x62, + 0x53, 0x78, 0x69, 0x6b, 0x33, 0x78, 0x38, 0x5a, 0x70, 0x59, 0x50, 0x71, + 0x53, 0x4a, 0x5a, 0x6f, 0x62, 0x30, 0x58, 0x76, 0x4b, 0x6b, 0x74, 0x67, + 0x37, 0x52, 0x35, 0x44, 0x4e, 0x32, 0x57, 0x2f, 0x65, 0x4c, 0x6e, 0x46, + 0x34, 0x63, 0x55, 0x0a, 0x45, 0x33, 0x70, 0x2b, 0x38, 0x5a, 0x33, 0x38, + 0x4c, 0x57, 0x76, 0x37, 0x6c, 0x36, 0x72, 0x72, 0x4e, 0x47, 0x4d, 0x41, + 0x4e, 0x48, 0x6a, 0x59, 0x44, 0x47, 0x53, 0x5a, 0x2f, 0x67, 0x79, 0x2b, + 0x48, 0x31, 0x6f, 0x75, 0x38, 0x69, 0x72, 0x54, 0x2b, 0x55, 0x64, 0x42, + 0x6c, 0x6c, 0x58, 0x4b, 0x70, 0x71, 0x44, 0x35, 0x39, 0x5a, 0x56, 0x55, + 0x73, 0x2b, 0x6f, 0x76, 0x4e, 0x73, 0x7a, 0x52, 0x0a, 0x68, 0x50, 0x37, + 0x77, 0x6c, 0x4c, 0x4d, 0x30, 0x42, 0x67, 0x45, 0x44, 0x58, 0x33, 0x6b, + 0x6e, 0x36, 0x78, 0x52, 0x41, 0x63, 0x5a, 0x2b, 0x74, 0x6b, 0x59, 0x53, + 0x64, 0x57, 0x79, 0x63, 0x6f, 0x2b, 0x5a, 0x4b, 0x4f, 0x48, 0x63, 0x66, + 0x71, 0x75, 0x54, 0x2f, 0x72, 0x4a, 0x34, 0x4b, 0x35, 0x42, 0x43, 0x65, + 0x75, 0x33, 0x31, 0x48, 0x6f, 0x78, 0x74, 0x51, 0x6c, 0x78, 0x4a, 0x44, + 0x51, 0x0a, 0x52, 0x36, 0x4b, 0x6a, 0x42, 0x4e, 0x2b, 0x61, 0x63, 0x37, + 0x63, 0x33, 0x6c, 0x32, 0x57, 0x52, 0x47, 0x7a, 0x2f, 0x70, 0x4b, 0x6a, + 0x30, 0x47, 0x39, 0x35, 0x32, 0x71, 0x42, 0x43, 0x72, 0x58, 0x4c, 0x71, + 0x32, 0x32, 0x30, 0x54, 0x4e, 0x71, 0x44, 0x4b, 0x44, 0x44, 0x4c, 0x50, + 0x54, 0x32, 0x5a, 0x46, 0x41, 0x6b, 0x65, 0x48, 0x77, 0x7a, 0x50, 0x58, + 0x32, 0x65, 0x65, 0x71, 0x53, 0x75, 0x0a, 0x62, 0x34, 0x6b, 0x48, 0x45, + 0x49, 0x44, 0x7a, 0x72, 0x66, 0x4d, 0x55, 0x44, 0x35, 0x63, 0x6d, 0x54, + 0x73, 0x42, 0x50, 0x71, 0x30, 0x4c, 0x59, 0x75, 0x6c, 0x67, 0x75, 0x42, + 0x59, 0x78, 0x5a, 0x57, 0x73, 0x50, 0x63, 0x6d, 0x2b, 0x66, 0x57, 0x63, + 0x53, 0x6e, 0x77, 0x39, 0x73, 0x32, 0x4c, 0x2b, 0x45, 0x4c, 0x32, 0x37, + 0x52, 0x39, 0x39, 0x49, 0x67, 0x7a, 0x43, 0x76, 0x64, 0x47, 0x49, 0x0a, + 0x71, 0x52, 0x45, 0x75, 0x2f, 0x69, 0x65, 0x5a, 0x66, 0x79, 0x6d, 0x31, + 0x6e, 0x67, 0x65, 0x58, 0x33, 0x56, 0x70, 0x73, 0x50, 0x4e, 0x54, 0x36, + 0x77, 0x43, 0x55, 0x58, 0x4c, 0x61, 0x63, 0x70, 0x79, 0x4a, 0x46, 0x6d, + 0x59, 0x57, 0x71, 0x65, 0x2f, 0x77, 0x4b, 0x43, 0x41, 0x51, 0x42, 0x44, + 0x75, 0x49, 0x53, 0x6a, 0x7a, 0x4c, 0x4f, 0x30, 0x49, 0x74, 0x36, 0x79, + 0x53, 0x56, 0x75, 0x66, 0x0a, 0x36, 0x63, 0x5a, 0x70, 0x79, 0x50, 0x6a, + 0x4b, 0x46, 0x64, 0x4d, 0x71, 0x4f, 0x76, 0x5a, 0x6d, 0x79, 0x39, 0x4b, + 0x55, 0x76, 0x7a, 0x67, 0x41, 0x54, 0x73, 0x65, 0x65, 0x69, 0x6c, 0x70, + 0x64, 0x51, 0x6d, 0x67, 0x55, 0x4f, 0x4e, 0x65, 0x50, 0x5a, 0x4e, 0x71, + 0x59, 0x2b, 0x4e, 0x53, 0x75, 0x42, 0x5a, 0x51, 0x2f, 0x46, 0x71, 0x44, + 0x31, 0x2f, 0x32, 0x4a, 0x66, 0x31, 0x35, 0x47, 0x43, 0x0a, 0x43, 0x79, + 0x42, 0x76, 0x41, 0x73, 0x59, 0x4e, 0x64, 0x4e, 0x64, 0x72, 0x75, 0x44, + 0x79, 0x75, 0x33, 0x70, 0x4a, 0x35, 0x5a, 0x53, 0x48, 0x30, 0x44, 0x4c, + 0x68, 0x65, 0x44, 0x53, 0x4a, 0x51, 0x34, 0x61, 0x72, 0x69, 0x2b, 0x69, + 0x35, 0x2b, 0x79, 0x52, 0x73, 0x52, 0x72, 0x6b, 0x42, 0x37, 0x4f, 0x32, + 0x6c, 0x43, 0x47, 0x6f, 0x71, 0x48, 0x52, 0x53, 0x43, 0x38, 0x44, 0x4e, + 0x37, 0x36, 0x0a, 0x6a, 0x78, 0x74, 0x6d, 0x39, 0x6b, 0x57, 0x68, 0x79, + 0x4c, 0x31, 0x73, 0x61, 0x67, 0x6c, 0x51, 0x2f, 0x75, 0x71, 0x53, 0x6f, + 0x77, 0x65, 0x66, 0x57, 0x33, 0x62, 0x50, 0x59, 0x59, 0x62, 0x7a, 0x59, + 0x79, 0x44, 0x64, 0x4a, 0x6e, 0x49, 0x46, 0x37, 0x78, 0x7a, 0x45, 0x65, + 0x34, 0x44, 0x74, 0x48, 0x43, 0x38, 0x79, 0x33, 0x64, 0x39, 0x35, 0x77, + 0x6c, 0x6f, 0x6b, 0x71, 0x41, 0x57, 0x55, 0x0a, 0x4e, 0x4e, 0x4c, 0x74, + 0x36, 0x41, 0x49, 0x49, 0x55, 0x75, 0x2f, 0x74, 0x75, 0x7a, 0x69, 0x77, + 0x74, 0x79, 0x57, 0x5a, 0x6b, 0x56, 0x6a, 0x68, 0x64, 0x77, 0x56, 0x53, + 0x32, 0x4c, 0x33, 0x5a, 0x59, 0x6a, 0x7a, 0x4b, 0x7a, 0x4b, 0x79, 0x76, + 0x48, 0x53, 0x63, 0x76, 0x45, 0x56, 0x58, 0x53, 0x56, 0x71, 0x69, 0x6d, + 0x50, 0x52, 0x45, 0x2f, 0x67, 0x30, 0x59, 0x68, 0x30, 0x71, 0x50, 0x4d, + 0x0a, 0x54, 0x6e, 0x55, 0x6c, 0x48, 0x45, 0x63, 0x56, 0x46, 0x71, 0x66, + 0x6e, 0x66, 0x5a, 0x74, 0x63, 0x2f, 0x56, 0x42, 0x6b, 0x7a, 0x34, 0x2b, + 0x67, 0x47, 0x4e, 0x61, 0x36, 0x4e, 0x67, 0x4f, 0x31, 0x78, 0x79, 0x49, + 0x30, 0x71, 0x51, 0x7a, 0x6e, 0x58, 0x79, 0x56, 0x59, 0x45, 0x59, 0x4d, + 0x42, 0x77, 0x78, 0x51, 0x6a, 0x46, 0x38, 0x47, 0x5a, 0x68, 0x4d, 0x73, + 0x47, 0x59, 0x78, 0x30, 0x45, 0x0a, 0x4f, 0x54, 0x50, 0x78, 0x41, 0x6f, + 0x49, 0x42, 0x41, 0x51, 0x43, 0x36, 0x67, 0x31, 0x72, 0x4c, 0x32, 0x59, + 0x32, 0x73, 0x76, 0x37, 0x4f, 0x4b, 0x30, 0x39, 0x48, 0x74, 0x38, 0x51, + 0x4b, 0x4c, 0x2f, 0x48, 0x50, 0x6d, 0x48, 0x4d, 0x4a, 0x7a, 0x38, 0x73, + 0x57, 0x76, 0x61, 0x41, 0x74, 0x34, 0x63, 0x6f, 0x66, 0x74, 0x6a, 0x61, + 0x4f, 0x65, 0x38, 0x6c, 0x78, 0x31, 0x7a, 0x42, 0x36, 0x69, 0x0a, 0x67, + 0x48, 0x37, 0x42, 0x6d, 0x47, 0x77, 0x70, 0x76, 0x50, 0x77, 0x4a, 0x39, + 0x4f, 0x58, 0x7a, 0x7a, 0x5a, 0x2f, 0x2b, 0x2b, 0x74, 0x50, 0x4d, 0x39, + 0x55, 0x46, 0x76, 0x50, 0x54, 0x48, 0x6f, 0x74, 0x46, 0x67, 0x4a, 0x4a, + 0x48, 0x67, 0x75, 0x35, 0x56, 0x46, 0x68, 0x32, 0x70, 0x48, 0x32, 0x64, + 0x72, 0x49, 0x66, 0x48, 0x77, 0x74, 0x65, 0x34, 0x47, 0x48, 0x4c, 0x74, + 0x55, 0x2b, 0x54, 0x0a, 0x55, 0x30, 0x32, 0x4a, 0x68, 0x62, 0x39, 0x6e, + 0x62, 0x79, 0x76, 0x79, 0x75, 0x4c, 0x34, 0x79, 0x6e, 0x4a, 0x78, 0x44, + 0x6b, 0x46, 0x37, 0x67, 0x51, 0x67, 0x73, 0x2b, 0x37, 0x76, 0x4a, 0x4a, + 0x42, 0x44, 0x76, 0x6b, 0x71, 0x2f, 0x63, 0x35, 0x72, 0x43, 0x63, 0x30, + 0x35, 0x7a, 0x6c, 0x38, 0x57, 0x35, 0x66, 0x69, 0x6f, 0x4d, 0x32, 0x4c, + 0x45, 0x4a, 0x44, 0x46, 0x5a, 0x36, 0x67, 0x52, 0x0a, 0x38, 0x46, 0x52, + 0x5a, 0x70, 0x4e, 0x4a, 0x5a, 0x74, 0x42, 0x64, 0x51, 0x47, 0x79, 0x74, + 0x52, 0x61, 0x74, 0x37, 0x51, 0x2f, 0x70, 0x45, 0x31, 0x48, 0x53, 0x4b, + 0x39, 0x73, 0x34, 0x69, 0x41, 0x6f, 0x6c, 0x57, 0x41, 0x71, 0x56, 0x6b, + 0x45, 0x6d, 0x6e, 0x35, 0x4c, 0x71, 0x6a, 0x48, 0x2b, 0x6d, 0x56, 0x76, + 0x48, 0x63, 0x49, 0x75, 0x52, 0x48, 0x6b, 0x4c, 0x36, 0x41, 0x7a, 0x46, + 0x70, 0x0a, 0x34, 0x73, 0x75, 0x36, 0x46, 0x75, 0x72, 0x66, 0x30, 0x77, + 0x74, 0x41, 0x66, 0x65, 0x76, 0x57, 0x56, 0x32, 0x7a, 0x64, 0x33, 0x50, + 0x6d, 0x43, 0x65, 0x4a, 0x32, 0x30, 0x53, 0x52, 0x43, 0x42, 0x50, 0x69, + 0x44, 0x59, 0x56, 0x55, 0x4f, 0x64, 0x73, 0x75, 0x5a, 0x66, 0x51, 0x7a, + 0x51, 0x4f, 0x76, 0x30, 0x52, 0x74, 0x38, 0x30, 0x70, 0x31, 0x56, 0x79, + 0x52, 0x35, 0x77, 0x75, 0x6a, 0x46, 0x0a, 0x37, 0x63, 0x57, 0x73, 0x74, + 0x5a, 0x39, 0x6c, 0x6a, 0x74, 0x55, 0x72, 0x68, 0x4b, 0x31, 0x76, 0x53, + 0x52, 0x33, 0x70, 0x56, 0x6f, 0x74, 0x66, 0x47, 0x48, 0x76, 0x76, 0x78, + 0x64, 0x47, 0x7a, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x51, 0x43, 0x36, 0x59, + 0x70, 0x5a, 0x64, 0x71, 0x4d, 0x38, 0x7a, 0x68, 0x32, 0x45, 0x41, 0x30, + 0x75, 0x69, 0x4e, 0x55, 0x4f, 0x55, 0x2f, 0x5a, 0x63, 0x6d, 0x54, 0x0a, + 0x31, 0x51, 0x6c, 0x4b, 0x67, 0x2b, 0x69, 0x58, 0x4b, 0x66, 0x57, 0x5a, + 0x72, 0x2b, 0x61, 0x64, 0x50, 0x56, 0x50, 0x6b, 0x31, 0x64, 0x54, 0x39, + 0x79, 0x55, 0x78, 0x2f, 0x6e, 0x4e, 0x77, 0x6e, 0x66, 0x43, 0x62, 0x4b, + 0x63, 0x54, 0x45, 0x79, 0x56, 0x74, 0x37, 0x65, 0x41, 0x7a, 0x69, 0x69, + 0x4c, 0x73, 0x5a, 0x77, 0x36, 0x65, 0x58, 0x4f, 0x50, 0x4b, 0x39, 0x55, + 0x38, 0x7a, 0x76, 0x43, 0x0a, 0x4a, 0x50, 0x55, 0x71, 0x67, 0x67, 0x44, + 0x5a, 0x33, 0x2b, 0x6b, 0x62, 0x6b, 0x34, 0x6a, 0x50, 0x44, 0x52, 0x41, + 0x73, 0x30, 0x4a, 0x74, 0x49, 0x6b, 0x43, 0x30, 0x62, 0x75, 0x48, 0x7a, + 0x6b, 0x58, 0x46, 0x30, 0x72, 0x67, 0x41, 0x34, 0x62, 0x4a, 0x7a, 0x75, + 0x55, 0x47, 0x30, 0x44, 0x45, 0x73, 0x58, 0x4a, 0x58, 0x59, 0x71, 0x48, + 0x6f, 0x4f, 0x6d, 0x31, 0x4f, 0x56, 0x4a, 0x66, 0x73, 0x0a, 0x78, 0x6f, + 0x4c, 0x50, 0x74, 0x68, 0x71, 0x55, 0x44, 0x30, 0x44, 0x32, 0x73, 0x43, + 0x59, 0x37, 0x46, 0x62, 0x4a, 0x58, 0x67, 0x4e, 0x4d, 0x61, 0x33, 0x76, + 0x59, 0x62, 0x31, 0x39, 0x6b, 0x78, 0x65, 0x4a, 0x37, 0x76, 0x37, 0x70, + 0x69, 0x42, 0x72, 0x69, 0x68, 0x4c, 0x6b, 0x35, 0x4c, 0x57, 0x64, 0x41, + 0x78, 0x32, 0x6c, 0x53, 0x56, 0x30, 0x6c, 0x61, 0x46, 0x45, 0x39, 0x4f, + 0x38, 0x43, 0x0a, 0x68, 0x36, 0x48, 0x61, 0x5a, 0x65, 0x52, 0x39, 0x4a, + 0x49, 0x69, 0x42, 0x79, 0x63, 0x5a, 0x39, 0x71, 0x48, 0x49, 0x58, 0x50, + 0x50, 0x43, 0x79, 0x5a, 0x57, 0x51, 0x4f, 0x6c, 0x6c, 0x43, 0x49, 0x56, + 0x69, 0x31, 0x71, 0x53, 0x51, 0x6b, 0x70, 0x51, 0x4e, 0x34, 0x76, 0x59, + 0x4e, 0x68, 0x63, 0x2f, 0x49, 0x45, 0x37, 0x4d, 0x76, 0x51, 0x31, 0x4a, + 0x42, 0x61, 0x4f, 0x62, 0x6c, 0x4a, 0x48, 0x0a, 0x63, 0x73, 0x63, 0x49, + 0x4c, 0x43, 0x38, 0x61, 0x69, 0x4a, 0x56, 0x46, 0x4f, 0x53, 0x5a, 0x6e, + 0x4e, 0x6a, 0x4b, 0x2f, 0x66, 0x6c, 0x6f, 0x72, 0x78, 0x74, 0x54, 0x4f, + 0x44, 0x44, 0x39, 0x49, 0x47, 0x33, 0x67, 0x48, 0x66, 0x78, 0x54, 0x52, + 0x31, 0x73, 0x66, 0x61, 0x4c, 0x76, 0x35, 0x7a, 0x43, 0x51, 0x66, 0x39, + 0x77, 0x4f, 0x49, 0x35, 0x52, 0x49, 0x2f, 0x78, 0x0a, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x52, 0x53, 0x41, 0x20, 0x50, 0x52, + 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a +}; + +unsigned char client_key_der[] = { + 0x30, 0x82, 0x09, 0x29, 0x02, 0x01, 0x00, 0x02, 0x82, 0x02, 0x01, 0x00, + 0xAF, 0x04, 0xB2, 0x36, 0x78, 0x5F, 0xBC, 0x38, 0x9C, 0x18, 0x93, 0x81, + 0x7D, 0x44, 0x0B, 0x18, 0x4A, 0x4A, 0x0B, 0x6F, 0xCD, 0xDB, 0x94, 0xE8, + 0xD8, 0xA7, 0xFA, 0xEB, 0x39, 0x03, 0x6F, 0x79, 0xB8, 0x20, 0xFC, 0x0B, + 0x71, 0xCC, 0x46, 0x58, 0x75, 0xAE, 0x7B, 0x18, 0xA9, 0x9A, 0xF0, 0x30, + 0x17, 0xE5, 0xF6, 0xE8, 0x51, 0xB3, 0xE5, 0xF2, 0x5D, 0xAB, 0xC1, 0x61, + 0xC2, 0x5A, 0xBE, 0x9E, 0xA8, 0xF8, 0x3E, 0x19, 0x7F, 0x43, 0xEC, 0x14, + 0x06, 0x54, 0x3E, 0x5C, 0xC8, 0xEA, 0x22, 0x9C, 0x32, 0xCE, 0x96, 0x66, + 0xE0, 0x79, 0xBE, 0x38, 0xE0, 0x8A, 0x7E, 0xCC, 0x97, 0x76, 0x62, 0xCB, + 0xA4, 0x5C, 0x95, 0x93, 0x8A, 0xEB, 0xD8, 0x64, 0x38, 0x28, 0x54, 0x90, + 0x5D, 0x05, 0xF2, 0x67, 0x1C, 0x5B, 0x34, 0x57, 0x60, 0x9C, 0x2A, 0xEF, + 0x9A, 0xD9, 0x08, 0x05, 0x06, 0xEF, 0x9A, 0x70, 0x72, 0x3F, 0xEC, 0x27, + 0x7B, 0x38, 0x54, 0x40, 0xA2, 0xE4, 0x99, 0x03, 0xF6, 0x4A, 0x3E, 0xDE, + 0x47, 0x32, 0xE8, 0xC9, 0x7D, 0xAA, 0x00, 0x8B, 0x82, 0x15, 0x86, 0xD6, + 0x03, 0xA0, 0xD6, 0x4B, 0xD3, 0xCB, 0x56, 0xE8, 0x2A, 0x4A, 0xF2, 0xDE, + 0x42, 0xAC, 0xF5, 0xB6, 0x9D, 0x9F, 0x66, 0x98, 0x41, 0x76, 0x37, 0xC8, + 0x95, 0xE0, 0xC1, 0xBB, 0x36, 0x20, 0x07, 0x46, 0x9C, 0xF7, 0x20, 0xC7, + 0xD6, 0x67, 0x9E, 0x40, 0xE0, 0x8D, 0x3D, 0xAC, 0xD1, 0x01, 0xC5, 0x96, + 0xC2, 0xEF, 0xA8, 0x55, 0x69, 0x84, 0x04, 0x49, 0x0F, 0x0C, 0x8D, 0x69, + 0x86, 0xE9, 0xC3, 0xF7, 0x7D, 0xC6, 0xD5, 0x17, 0x16, 0x76, 0x64, 0x14, + 0xAF, 0xB9, 0x07, 0x78, 0xF3, 0x0F, 0xC3, 0x9A, 0x77, 0x07, 0x3F, 0xAA, + 0x8E, 0xA6, 0x87, 0xFB, 0xAA, 0x02, 0x4E, 0x16, 0x42, 0xA3, 0xE6, 0x14, + 0xF6, 0x14, 0x3F, 0xE3, 0xE1, 0xF0, 0x30, 0xDC, 0xBC, 0x42, 0xCC, 0xA3, + 0x6A, 0x67, 0xFB, 0xD7, 0x3E, 0x60, 0xE0, 0x28, 0x5E, 0xDB, 0x84, 0x0F, + 0x7D, 0xC9, 0xF4, 0xDD, 0x6C, 0xBF, 0x65, 0x16, 0xAE, 0x4A, 0x0B, 0x10, + 0x9F, 0x2C, 0xE1, 0xAD, 0x9C, 0xFD, 0x58, 0x9E, 0x7E, 0xF3, 0x05, 0x2A, + 0x8F, 0x8E, 0x10, 0xF0, 0x4E, 0xA9, 0x61, 0x12, 0xF4, 0x87, 0x8C, 0x5E, + 0x22, 0xC1, 0x0E, 0xBF, 0xE2, 0x30, 0x35, 0xB5, 0x58, 0x9B, 0xC5, 0xB5, + 0x6C, 0x76, 0x5B, 0x75, 0x6D, 0x52, 0x78, 0xCC, 0x29, 0xB4, 0x46, 0xFB, + 0x18, 0xF8, 0x14, 0x68, 0xEF, 0xF2, 0x27, 0x3F, 0xE1, 0x2A, 0xA7, 0x2B, + 0xA4, 0x82, 0x7B, 0xFC, 0x8A, 0x1A, 0x01, 0x32, 0xBE, 0x8F, 0xDA, 0x7C, + 0xD9, 0xBD, 0xD0, 0x8B, 0x6F, 0xD4, 0x4E, 0x9A, 0x88, 0x0C, 0x88, 0x32, + 0xC9, 0x90, 0x0B, 0x52, 0x4E, 0xA1, 0x58, 0x7F, 0x7B, 0x4A, 0x83, 0x1A, + 0xAA, 0x48, 0x54, 0x8E, 0xC0, 0xFC, 0xEB, 0x33, 0xF6, 0xD4, 0xA1, 0xC4, + 0x37, 0x91, 0xB9, 0x91, 0x7D, 0xFA, 0x03, 0x0C, 0x9F, 0x48, 0x40, 0x26, + 0x46, 0xF9, 0x47, 0x73, 0x31, 0xA3, 0xC5, 0xFA, 0x90, 0x3C, 0x1A, 0xB3, + 0x31, 0xC7, 0xBD, 0x15, 0x2C, 0x7D, 0x37, 0xE4, 0x7D, 0x6F, 0x0F, 0xD5, + 0x06, 0xC7, 0xC2, 0x73, 0x31, 0x79, 0x2C, 0xA9, 0xBB, 0x46, 0x88, 0xC7, + 0x0F, 0x9C, 0xF0, 0x39, 0xB3, 0xB7, 0xE9, 0x6C, 0xED, 0xB8, 0xF6, 0x9B, + 0x6E, 0x87, 0xF3, 0xD2, 0x61, 0x62, 0x57, 0x2B, 0x85, 0xDD, 0xC3, 0x9B, + 0xFE, 0xDA, 0x86, 0x55, 0x5F, 0x77, 0x9C, 0x8A, 0xC0, 0x05, 0xBC, 0xBF, + 0x0D, 0xB0, 0x5B, 0x21, 0x02, 0x57, 0x2E, 0xCB, 0x2D, 0x9E, 0x9C, 0x29, + 0x95, 0x96, 0xDA, 0x66, 0x46, 0x2A, 0x3E, 0xF3, 0xBB, 0x19, 0x15, 0x7B, + 0xD0, 0x7F, 0x30, 0x34, 0xEE, 0x4E, 0x7F, 0x73, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x02, 0x00, 0x65, 0xC7, 0x75, 0x5B, 0xF7, 0xE4, 0xB4, + 0xB0, 0xB7, 0x16, 0x13, 0xB1, 0xDA, 0x07, 0x17, 0xEC, 0xA8, 0x47, 0x27, + 0x1F, 0x7D, 0xB7, 0x94, 0x81, 0x54, 0x7D, 0x5B, 0x8E, 0x2F, 0x39, 0xB0, + 0x3A, 0x38, 0xB9, 0xF5, 0xD9, 0x31, 0xF4, 0x79, 0x74, 0x37, 0xFB, 0x9F, + 0xDA, 0x57, 0xC7, 0xA2, 0xA6, 0x55, 0x3F, 0x86, 0xB6, 0xD3, 0xCC, 0xAA, + 0x8C, 0xA7, 0xAC, 0x9A, 0x69, 0x1F, 0x7D, 0x66, 0x1D, 0x1E, 0x66, 0x3E, + 0xB6, 0xD5, 0x2B, 0xCA, 0xBE, 0x5A, 0x6F, 0xE3, 0x54, 0x52, 0x02, 0xD6, + 0xDD, 0xBF, 0x2E, 0x24, 0x88, 0xEF, 0x41, 0xDD, 0x3D, 0x76, 0x57, 0x14, + 0x26, 0x26, 0x6E, 0xD2, 0x45, 0x5A, 0xEF, 0xFC, 0x8C, 0x30, 0xDD, 0xB6, + 0x99, 0x91, 0x8E, 0xE2, 0x9E, 0x91, 0x7A, 0x43, 0xDD, 0x72, 0xC0, 0x17, + 0x50, 0x38, 0xFE, 0x98, 0x79, 0x7E, 0xB3, 0x77, 0xD6, 0x74, 0x92, 0x22, + 0x5E, 0x02, 0x12, 0xEA, 0x62, 0x8B, 0x65, 0xD6, 0x99, 0x40, 0x21, 0x70, + 0x5A, 0xBD, 0x7F, 0xAF, 0xCF, 0xD3, 0xD7, 0xE7, 0x48, 0x45, 0xAB, 0xF7, + 0x65, 0x29, 0xE1, 0x1A, 0xE8, 0x9A, 0x73, 0x8E, 0x2B, 0xCA, 0x9F, 0x15, + 0x8B, 0x2E, 0x0D, 0x27, 0xEF, 0xD9, 0x16, 0x7C, 0x4E, 0x16, 0x9B, 0xFC, + 0x2D, 0xCE, 0xE3, 0xD0, 0xCC, 0xF5, 0x5C, 0x1F, 0x4D, 0x9F, 0xCC, 0x40, + 0x6E, 0xF0, 0x19, 0x47, 0x16, 0xEF, 0xB8, 0x92, 0x6F, 0x68, 0x26, 0xBB, + 0x17, 0x24, 0xF4, 0xC2, 0xD3, 0x49, 0xC9, 0xD5, 0x3A, 0xD6, 0x81, 0xCF, + 0x7C, 0x2D, 0x24, 0x3F, 0x98, 0x7E, 0xBC, 0xB3, 0x19, 0xDA, 0x2B, 0x27, + 0x00, 0xA6, 0x2F, 0x73, 0x44, 0x14, 0x6C, 0xA5, 0xCB, 0xD0, 0x8D, 0xFA, + 0x52, 0x79, 0xA5, 0xDD, 0x80, 0x72, 0x34, 0xA6, 0x1F, 0xD4, 0x5E, 0x42, + 0xCA, 0xEF, 0x36, 0x21, 0x37, 0x4F, 0x63, 0xB7, 0x42, 0xF7, 0xD5, 0x28, + 0x9F, 0xF1, 0xB9, 0x7D, 0x15, 0x15, 0x9D, 0xA9, 0x97, 0xE0, 0x0F, 0x8D, + 0x6D, 0xF1, 0x84, 0x86, 0x52, 0x76, 0x5D, 0x75, 0x10, 0x5D, 0x38, 0xA1, + 0x03, 0x01, 0x6E, 0x64, 0x7A, 0xD3, 0x1C, 0x02, 0x57, 0xF5, 0xDA, 0x30, + 0xE0, 0xB4, 0x10, 0xB8, 0x88, 0x43, 0x3C, 0xE6, 0x14, 0x37, 0x36, 0x68, + 0xE1, 0x6D, 0x38, 0x0C, 0x32, 0x7E, 0x6E, 0xE7, 0xF8, 0x83, 0x3A, 0x4D, + 0x87, 0x5F, 0x58, 0x45, 0xE0, 0x43, 0xBB, 0x38, 0x37, 0x09, 0x36, 0xD8, + 0x2B, 0xC4, 0x44, 0x9D, 0x7B, 0xCC, 0x63, 0x82, 0x83, 0xE9, 0xC8, 0x8C, + 0x3A, 0xB6, 0xB1, 0x25, 0x2D, 0x42, 0x22, 0x68, 0x12, 0xA6, 0x15, 0x69, + 0x3F, 0xB4, 0xF0, 0x38, 0xE9, 0x5B, 0xBB, 0x18, 0xFD, 0xBF, 0x51, 0x1C, + 0x4C, 0x0C, 0x78, 0x22, 0x1D, 0x7C, 0xAA, 0xBA, 0xA5, 0xEB, 0x6D, 0x95, + 0xE0, 0x4A, 0x78, 0x95, 0xBF, 0xC9, 0xC5, 0x69, 0xD7, 0x34, 0x7C, 0x3F, + 0xCF, 0x67, 0x69, 0x74, 0xC5, 0x24, 0x24, 0x97, 0xF7, 0x96, 0x69, 0xFE, + 0xFF, 0x86, 0x55, 0xFA, 0x03, 0x41, 0xB4, 0x1C, 0xAA, 0x2D, 0x12, 0x0F, + 0x4C, 0x6D, 0xA5, 0xEC, 0xEE, 0x14, 0x9D, 0x1F, 0xF6, 0x8A, 0xEA, 0xB1, + 0xD3, 0x02, 0xA9, 0x63, 0xB4, 0xE1, 0xE0, 0x87, 0xD7, 0x8D, 0x25, 0x56, + 0xEA, 0xC3, 0x9C, 0xF8, 0x9C, 0x3B, 0x77, 0x98, 0x6C, 0x57, 0xFC, 0xFB, + 0x01, 0x44, 0x44, 0x10, 0x9E, 0x16, 0x5F, 0xA3, 0x4D, 0xA4, 0x1C, 0x10, + 0x64, 0xDD, 0x95, 0xC4, 0x7D, 0x3B, 0x23, 0x1C, 0xAF, 0x93, 0x76, 0x50, + 0x5E, 0x05, 0x50, 0x5C, 0xD8, 0x6B, 0xC8, 0xD0, 0x88, 0x5A, 0x9B, 0x7F, + 0x7D, 0x68, 0xAF, 0xB8, 0xF0, 0xB3, 0xC2, 0xDD, 0x94, 0xB4, 0xEA, 0xE9, + 0xFF, 0x95, 0x82, 0xE7, 0xBC, 0x35, 0x79, 0xE3, 0x2C, 0x5D, 0xC8, 0x6B, + 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE4, 0xA1, 0xF3, 0x2F, 0x8B, 0xD6, + 0x05, 0x77, 0x9F, 0xA3, 0xAF, 0xCA, 0x87, 0x02, 0x09, 0x03, 0xBC, 0xB9, + 0x4D, 0x28, 0x00, 0x6C, 0x38, 0x58, 0xA5, 0x08, 0xB8, 0xD8, 0xB9, 0x6D, + 0xDF, 0xB3, 0x4C, 0x9C, 0x97, 0x32, 0x29, 0x35, 0x66, 0xCB, 0x1A, 0x5F, + 0x60, 0x6F, 0x1E, 0x9E, 0x9D, 0x45, 0xAB, 0x34, 0xD5, 0x62, 0xD7, 0x1E, + 0x64, 0x8C, 0x37, 0x7B, 0xD4, 0xF9, 0x1C, 0x63, 0x6A, 0x52, 0x3A, 0x68, + 0xC4, 0x2A, 0x79, 0xDC, 0xA7, 0x03, 0xBB, 0xC8, 0x86, 0x17, 0xA0, 0x90, + 0xA8, 0x93, 0x05, 0xAE, 0x4F, 0x06, 0x91, 0x54, 0x14, 0xF3, 0x5A, 0x97, + 0xB9, 0x6A, 0x00, 0x3F, 0x6C, 0x89, 0x7D, 0x04, 0xA6, 0x06, 0xE8, 0x60, + 0x3D, 0xD1, 0xCA, 0x50, 0xAD, 0x61, 0x1B, 0x5A, 0x3A, 0xD8, 0x31, 0xF6, + 0x81, 0x6D, 0xDE, 0x9F, 0xB8, 0xF7, 0xA7, 0x10, 0xA9, 0xE0, 0xD8, 0x69, + 0xF4, 0x8A, 0x7F, 0xE7, 0xD8, 0x34, 0x2E, 0xC3, 0xC3, 0x06, 0x0E, 0xA7, + 0x62, 0x2F, 0x31, 0xE5, 0xA0, 0xCD, 0xA1, 0x89, 0xAF, 0x11, 0x51, 0x00, + 0xE4, 0x54, 0x73, 0xEE, 0xEF, 0xF0, 0xC2, 0x0C, 0xA6, 0x18, 0x83, 0xE0, + 0x5A, 0x96, 0x06, 0xDD, 0xCE, 0x64, 0xB3, 0x59, 0xD0, 0x01, 0x91, 0x92, + 0x6B, 0xB7, 0x4B, 0x16, 0xD0, 0xCC, 0x11, 0x83, 0xB1, 0xE3, 0xBE, 0xB0, + 0x06, 0xE1, 0xC0, 0x55, 0x25, 0x92, 0x02, 0x97, 0x54, 0x33, 0xC5, 0x70, + 0x93, 0xF9, 0x1C, 0x7F, 0xFB, 0xDA, 0xA3, 0x73, 0xB3, 0xA9, 0xED, 0xE8, + 0xB3, 0xBE, 0xC2, 0xA0, 0x54, 0x70, 0x64, 0xC3, 0x5A, 0xCF, 0xF1, 0x7F, + 0xCF, 0x94, 0xB0, 0x70, 0x6C, 0x01, 0xA1, 0xEE, 0x10, 0x82, 0x20, 0x68, + 0xDF, 0xD2, 0x97, 0x47, 0x76, 0x5C, 0x5B, 0x71, 0xDC, 0x86, 0xFC, 0xF3, + 0x17, 0xC4, 0xFA, 0x9B, 0xCD, 0x31, 0x6B, 0x38, 0x13, 0x8D, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xC3, 0xF7, 0xD4, 0x6D, 0x2C, 0x62, 0x93, 0x7C, 0x7C, + 0x66, 0x96, 0x0F, 0xA9, 0x22, 0x59, 0xA1, 0xBD, 0x17, 0xBC, 0xA9, 0x2D, + 0x83, 0xB4, 0x79, 0x0C, 0xDD, 0x96, 0xFD, 0xE2, 0xE7, 0x17, 0x87, 0x14, + 0x13, 0x7A, 0x7E, 0xF1, 0x9D, 0xFC, 0x2D, 0x6B, 0xFB, 0x97, 0xAA, 0xEB, + 0x34, 0x63, 0x00, 0x34, 0x78, 0xD8, 0x0C, 0x64, 0x99, 0xFE, 0x0C, 0xBE, + 0x1F, 0x5A, 0x2E, 0xF2, 0x2A, 0xD3, 0xF9, 0x47, 0x41, 0x96, 0x55, 0xCA, + 0xA6, 0xA0, 0xF9, 0xF5, 0x95, 0x54, 0xB3, 0xEA, 0x2F, 0x36, 0xCC, 0xD1, + 0x84, 0xFE, 0xF0, 0x94, 0xB3, 0x34, 0x06, 0x01, 0x03, 0x5F, 0x79, 0x27, + 0xEB, 0x14, 0x40, 0x71, 0x9F, 0xAD, 0x91, 0x84, 0x9D, 0x5B, 0x27, 0x28, + 0xF9, 0x92, 0x8E, 0x1D, 0xC7, 0xEA, 0xB9, 0x3F, 0xEB, 0x27, 0x82, 0xB9, + 0x04, 0x27, 0xAE, 0xDF, 0x51, 0xE8, 0xC6, 0xD4, 0x25, 0xC4, 0x90, 0xD0, + 0x47, 0xA2, 0xA3, 0x04, 0xDF, 0x9A, 0x73, 0xB7, 0x37, 0x97, 0x65, 0x91, + 0x1B, 0x3F, 0xE9, 0x2A, 0x3D, 0x06, 0xF7, 0x9D, 0xAA, 0x04, 0x2A, 0xD7, + 0x2E, 0xAD, 0xB6, 0xD1, 0x33, 0x6A, 0x0C, 0xA0, 0xC3, 0x2C, 0xF4, 0xF6, + 0x64, 0x50, 0x24, 0x78, 0x7C, 0x33, 0x3D, 0x7D, 0x9E, 0x7A, 0xA4, 0xAE, + 0x6F, 0x89, 0x07, 0x10, 0x80, 0xF3, 0xAD, 0xF3, 0x14, 0x0F, 0x97, 0x26, + 0x4E, 0xC0, 0x4F, 0xAB, 0x42, 0xD8, 0xBA, 0x58, 0x2E, 0x05, 0x8C, 0x59, + 0x5A, 0xC3, 0xDC, 0x9B, 0xE7, 0xD6, 0x71, 0x29, 0xF0, 0xF6, 0xCD, 0x8B, + 0xF8, 0x42, 0xF6, 0xED, 0x1F, 0x7D, 0x22, 0x0C, 0xC2, 0xBD, 0xD1, 0x88, + 0xA9, 0x11, 0x2E, 0xFE, 0x27, 0x99, 0x7F, 0x29, 0xB5, 0x9E, 0x07, 0x97, + 0xDD, 0x5A, 0x6C, 0x3C, 0xD4, 0xFA, 0xC0, 0x25, 0x17, 0x2D, 0xA7, 0x29, + 0xC8, 0x91, 0x66, 0x61, 0x6A, 0x9E, 0xFF, 0x02, 0x82, 0x01, 0x00, 0x43, + 0xB8, 0x84, 0xA3, 0xCC, 0xB3, 0xB4, 0x22, 0xDE, 0xB2, 0x49, 0x5B, 0x9F, + 0xE9, 0xC6, 0x69, 0xC8, 0xF8, 0xCA, 0x15, 0xD3, 0x2A, 0x3A, 0xF6, 0x66, + 0xCB, 0xD2, 0x94, 0xBF, 0x38, 0x00, 0x4E, 0xC7, 0x9E, 0x8A, 0x5A, 0x5D, + 0x42, 0x68, 0x14, 0x38, 0xD7, 0x8F, 0x64, 0xDA, 0x98, 0xF8, 0xD4, 0xAE, + 0x05, 0x94, 0x3F, 0x16, 0xA0, 0xF5, 0xFF, 0x62, 0x5F, 0xD7, 0x91, 0x82, + 0x0B, 0x20, 0x6F, 0x02, 0xC6, 0x0D, 0x74, 0xD7, 0x6B, 0xB8, 0x3C, 0xAE, + 0xDE, 0x92, 0x79, 0x65, 0x21, 0xF4, 0x0C, 0xB8, 0x5E, 0x0D, 0x22, 0x50, + 0xE1, 0xAA, 0xE2, 0xFA, 0x2E, 0x7E, 0xC9, 0x1B, 0x11, 0xAE, 0x40, 0x7B, + 0x3B, 0x69, 0x42, 0x1A, 0x8A, 0x87, 0x45, 0x20, 0xBC, 0x0C, 0xDE, 0xFA, + 0x8F, 0x1B, 0x66, 0xF6, 0x45, 0xA1, 0xC8, 0xBD, 0x6C, 0x6A, 0x09, 0x50, + 0xFE, 0xEA, 0x92, 0xA3, 0x07, 0x9F, 0x5B, 0x76, 0xCF, 0x61, 0x86, 0xF3, + 0x63, 0x20, 0xDD, 0x26, 0x72, 0x05, 0xEF, 0x1C, 0xC4, 0x7B, 0x80, 0xED, + 0x1C, 0x2F, 0x32, 0xDD, 0xDF, 0x79, 0xC2, 0x5A, 0x24, 0xA8, 0x05, 0x94, + 0x34, 0xD2, 0xED, 0xE8, 0x02, 0x08, 0x52, 0xEF, 0xED, 0xBB, 0x38, 0xB0, + 0xB7, 0x25, 0x99, 0x91, 0x58, 0xE1, 0x77, 0x05, 0x52, 0xD8, 0xBD, 0xD9, + 0x62, 0x3C, 0xCA, 0xCC, 0xAC, 0xAF, 0x1D, 0x27, 0x2F, 0x11, 0x55, 0xD2, + 0x56, 0xA8, 0xA6, 0x3D, 0x11, 0x3F, 0x83, 0x46, 0x21, 0xD2, 0xA3, 0xCC, + 0x4E, 0x75, 0x25, 0x1C, 0x47, 0x15, 0x16, 0xA7, 0xE7, 0x7D, 0x9B, 0x5C, + 0xFD, 0x50, 0x64, 0xCF, 0x8F, 0xA0, 0x18, 0xD6, 0xBA, 0x36, 0x03, 0xB5, + 0xC7, 0x22, 0x34, 0xA9, 0x0C, 0xE7, 0x5F, 0x25, 0x58, 0x11, 0x83, 0x01, + 0xC3, 0x14, 0x23, 0x17, 0xC1, 0x99, 0x84, 0xCB, 0x06, 0x63, 0x1D, 0x04, + 0x39, 0x33, 0xF1, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x83, 0x5A, 0xCB, + 0xD9, 0x8D, 0xAC, 0xBF, 0xB3, 0x8A, 0xD3, 0xD1, 0xED, 0xF1, 0x02, 0x8B, + 0xFC, 0x73, 0xE6, 0x1C, 0xC2, 0x73, 0xF2, 0xC5, 0xAF, 0x68, 0x0B, 0x78, + 0x72, 0x87, 0xED, 0x8D, 0xA3, 0x9E, 0xF2, 0x5C, 0x75, 0xCC, 0x1E, 0xA2, + 0x80, 0x7E, 0xC1, 0x98, 0x6C, 0x29, 0xBC, 0xFC, 0x09, 0xF4, 0xE5, 0xF3, + 0xCD, 0x9F, 0xFE, 0xFA, 0xD3, 0xCC, 0xF5, 0x41, 0x6F, 0x3D, 0x31, 0xE8, + 0xB4, 0x58, 0x09, 0x24, 0x78, 0x2E, 0xE5, 0x51, 0x61, 0xDA, 0x91, 0xF6, + 0x76, 0xB2, 0x1F, 0x1F, 0x0B, 0x5E, 0xE0, 0x61, 0xCB, 0xB5, 0x4F, 0x93, + 0x53, 0x4D, 0x89, 0x85, 0xBF, 0x67, 0x6F, 0x2B, 0xF2, 0xB8, 0xBE, 0x32, + 0x9C, 0x9C, 0x43, 0x90, 0x5E, 0xE0, 0x42, 0x0B, 0x3E, 0xEE, 0xF2, 0x49, + 0x04, 0x3B, 0xE4, 0xAB, 0xF7, 0x39, 0xAC, 0x27, 0x34, 0xE7, 0x39, 0x7C, + 0x5B, 0x97, 0xE2, 0xA0, 0xCD, 0x8B, 0x10, 0x90, 0xC5, 0x67, 0xA8, 0x11, + 0xF0, 0x54, 0x59, 0xA4, 0xD2, 0x59, 0xB4, 0x17, 0x50, 0x1B, 0x2B, 0x51, + 0x6A, 0xDE, 0xD0, 0xFE, 0x91, 0x35, 0x1D, 0x22, 0xBD, 0xB3, 0x88, 0x80, + 0xA2, 0x55, 0x80, 0xA9, 0x59, 0x04, 0x9A, 0x7E, 0x4B, 0xAA, 0x31, 0xFE, + 0x99, 0x5B, 0xC7, 0x70, 0x8B, 0x91, 0x1E, 0x42, 0xFA, 0x03, 0x31, 0x69, + 0xE2, 0xCB, 0xBA, 0x16, 0xEA, 0xDF, 0xD3, 0x0B, 0x40, 0x7D, 0xEB, 0xD6, + 0x57, 0x6C, 0xDD, 0xDC, 0xF9, 0x82, 0x78, 0x9D, 0xB4, 0x49, 0x10, 0x81, + 0x3E, 0x20, 0xD8, 0x55, 0x43, 0x9D, 0xB2, 0xE6, 0x5F, 0x43, 0x34, 0x0E, + 0xBF, 0x44, 0x6D, 0xF3, 0x4A, 0x75, 0x57, 0x24, 0x79, 0xC2, 0xE8, 0xC5, + 0xED, 0xC5, 0xAC, 0xB5, 0x9F, 0x65, 0x8E, 0xD5, 0x2B, 0x84, 0xAD, 0x6F, + 0x49, 0x1D, 0xE9, 0x56, 0x8B, 0x5F, 0x18, 0x7B, 0xEF, 0xC5, 0xD1, 0xB3, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x62, 0x96, 0x5D, 0xA8, 0xCF, 0x33, + 0x87, 0x61, 0x00, 0xD2, 0xE8, 0x8D, 0x50, 0xE5, 0x3F, 0x65, 0xC9, 0x93, + 0xD5, 0x09, 0x4A, 0x83, 0xE8, 0x97, 0x29, 0xF5, 0x99, 0xAF, 0xE6, 0x9D, + 0x3D, 0x53, 0xE4, 0xD5, 0xD4, 0xFD, 0xC9, 0x4C, 0x7F, 0x9C, 0xDC, 0x27, + 0x7C, 0x26, 0xCA, 0x71, 0x31, 0x32, 0x56, 0xDE, 0xDE, 0x03, 0x38, 0xA2, + 0x2E, 0xC6, 0x70, 0xE9, 0xE5, 0xCE, 0x3C, 0xAF, 0x54, 0xF3, 0x3B, 0xC2, + 0x24, 0xF5, 0x2A, 0x82, 0x00, 0xD9, 0xDF, 0xE9, 0x1B, 0x93, 0x88, 0xCF, + 0x0D, 0x10, 0x2C, 0xD0, 0x9B, 0x48, 0x90, 0x2D, 0x1B, 0xB8, 0x7C, 0xE4, + 0x5C, 0x5D, 0x2B, 0x80, 0x0E, 0x1B, 0x27, 0x3B, 0x94, 0x1B, 0x40, 0xC4, + 0xB1, 0x72, 0x57, 0x62, 0xA1, 0xE8, 0x3A, 0x6D, 0x4E, 0x54, 0x97, 0xEC, + 0xC6, 0x82, 0xCF, 0xB6, 0x1A, 0x94, 0x0F, 0x40, 0xF6, 0xB0, 0x26, 0x3B, + 0x15, 0xB2, 0x57, 0x80, 0xD3, 0x1A, 0xDE, 0xF6, 0x1B, 0xD7, 0xD9, 0x31, + 0x78, 0x9E, 0xEF, 0xEE, 0x98, 0x81, 0xAE, 0x28, 0x4B, 0x93, 0x92, 0xD6, + 0x74, 0x0C, 0x76, 0x95, 0x25, 0x74, 0x95, 0xA1, 0x44, 0xF4, 0xEF, 0x02, + 0x87, 0xA1, 0xDA, 0x65, 0xE4, 0x7D, 0x24, 0x88, 0x81, 0xC9, 0xC6, 0x7D, + 0xA8, 0x72, 0x17, 0x3C, 0xF0, 0xB2, 0x65, 0x64, 0x0E, 0x96, 0x50, 0x88, + 0x56, 0x2D, 0x6A, 0x49, 0x09, 0x29, 0x40, 0xDE, 0x2F, 0x60, 0xD8, 0x5C, + 0xFC, 0x81, 0x3B, 0x32, 0xF4, 0x35, 0x24, 0x16, 0x8E, 0x6E, 0x52, 0x47, + 0x72, 0xC7, 0x08, 0x2C, 0x2F, 0x1A, 0x88, 0x95, 0x45, 0x39, 0x26, 0x67, + 0x36, 0x32, 0xBF, 0x7E, 0x5A, 0x2B, 0xC6, 0xD4, 0xCE, 0x0C, 0x3F, 0x48, + 0x1B, 0x78, 0x07, 0x7F, 0x14, 0xD1, 0xD6, 0xC7, 0xDA, 0x2E, 0xFE, 0x73, + 0x09, 0x07, 0xFD, 0xC0, 0xE2, 0x39, 0x44, 0x8F, 0xF1 +}; + +unsigned char client_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x44, + 0x43, + 0x43, + 0x41, + 0x77, + 0x43, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x45, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x78, + 0x4f, + 0x46, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x45, + 0x34, + 0x57, + 0x6a, + 0x42, + 0x4d, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x42, + 0x6d, + 0x4e, + 0x73, + 0x61, + 0x57, + 0x56, + 0x75, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x0a, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4b, + 0x38, + 0x45, + 0x73, + 0x6a, + 0x5a, + 0x34, + 0x58, + 0x37, + 0x77, + 0x34, + 0x6e, + 0x42, + 0x69, + 0x54, + 0x67, + 0x58, + 0x31, + 0x45, + 0x43, + 0x78, + 0x68, + 0x4b, + 0x53, + 0x67, + 0x74, + 0x76, + 0x7a, + 0x64, + 0x75, + 0x55, + 0x36, + 0x4e, + 0x69, + 0x6e, + 0x2b, + 0x75, + 0x73, + 0x35, + 0x0a, + 0x41, + 0x32, + 0x39, + 0x35, + 0x75, + 0x43, + 0x44, + 0x38, + 0x43, + 0x33, + 0x48, + 0x4d, + 0x52, + 0x6c, + 0x68, + 0x31, + 0x72, + 0x6e, + 0x73, + 0x59, + 0x71, + 0x5a, + 0x72, + 0x77, + 0x4d, + 0x42, + 0x66, + 0x6c, + 0x39, + 0x75, + 0x68, + 0x52, + 0x73, + 0x2b, + 0x58, + 0x79, + 0x58, + 0x61, + 0x76, + 0x42, + 0x59, + 0x63, + 0x4a, + 0x61, + 0x76, + 0x70, + 0x36, + 0x6f, + 0x2b, + 0x44, + 0x34, + 0x5a, + 0x66, + 0x30, + 0x50, + 0x73, + 0x46, + 0x41, + 0x5a, + 0x55, + 0x50, + 0x6c, + 0x7a, + 0x49, + 0x0a, + 0x36, + 0x69, + 0x4b, + 0x63, + 0x4d, + 0x73, + 0x36, + 0x57, + 0x5a, + 0x75, + 0x42, + 0x35, + 0x76, + 0x6a, + 0x6a, + 0x67, + 0x69, + 0x6e, + 0x37, + 0x4d, + 0x6c, + 0x33, + 0x5a, + 0x69, + 0x79, + 0x36, + 0x52, + 0x63, + 0x6c, + 0x5a, + 0x4f, + 0x4b, + 0x36, + 0x39, + 0x68, + 0x6b, + 0x4f, + 0x43, + 0x68, + 0x55, + 0x6b, + 0x46, + 0x30, + 0x46, + 0x38, + 0x6d, + 0x63, + 0x63, + 0x57, + 0x7a, + 0x52, + 0x58, + 0x59, + 0x4a, + 0x77, + 0x71, + 0x37, + 0x35, + 0x72, + 0x5a, + 0x43, + 0x41, + 0x55, + 0x47, + 0x0a, + 0x37, + 0x35, + 0x70, + 0x77, + 0x63, + 0x6a, + 0x2f, + 0x73, + 0x4a, + 0x33, + 0x73, + 0x34, + 0x56, + 0x45, + 0x43, + 0x69, + 0x35, + 0x4a, + 0x6b, + 0x44, + 0x39, + 0x6b, + 0x6f, + 0x2b, + 0x33, + 0x6b, + 0x63, + 0x79, + 0x36, + 0x4d, + 0x6c, + 0x39, + 0x71, + 0x67, + 0x43, + 0x4c, + 0x67, + 0x68, + 0x57, + 0x47, + 0x31, + 0x67, + 0x4f, + 0x67, + 0x31, + 0x6b, + 0x76, + 0x54, + 0x79, + 0x31, + 0x62, + 0x6f, + 0x4b, + 0x6b, + 0x72, + 0x79, + 0x33, + 0x6b, + 0x4b, + 0x73, + 0x39, + 0x62, + 0x61, + 0x64, + 0x0a, + 0x6e, + 0x32, + 0x61, + 0x59, + 0x51, + 0x58, + 0x59, + 0x33, + 0x79, + 0x4a, + 0x58, + 0x67, + 0x77, + 0x62, + 0x73, + 0x32, + 0x49, + 0x41, + 0x64, + 0x47, + 0x6e, + 0x50, + 0x63, + 0x67, + 0x78, + 0x39, + 0x5a, + 0x6e, + 0x6e, + 0x6b, + 0x44, + 0x67, + 0x6a, + 0x54, + 0x32, + 0x73, + 0x30, + 0x51, + 0x48, + 0x46, + 0x6c, + 0x73, + 0x4c, + 0x76, + 0x71, + 0x46, + 0x56, + 0x70, + 0x68, + 0x41, + 0x52, + 0x4a, + 0x44, + 0x77, + 0x79, + 0x4e, + 0x61, + 0x59, + 0x62, + 0x70, + 0x77, + 0x2f, + 0x64, + 0x39, + 0x0a, + 0x78, + 0x74, + 0x55, + 0x58, + 0x46, + 0x6e, + 0x5a, + 0x6b, + 0x46, + 0x4b, + 0x2b, + 0x35, + 0x42, + 0x33, + 0x6a, + 0x7a, + 0x44, + 0x38, + 0x4f, + 0x61, + 0x64, + 0x77, + 0x63, + 0x2f, + 0x71, + 0x6f, + 0x36, + 0x6d, + 0x68, + 0x2f, + 0x75, + 0x71, + 0x41, + 0x6b, + 0x34, + 0x57, + 0x51, + 0x71, + 0x50, + 0x6d, + 0x46, + 0x50, + 0x59, + 0x55, + 0x50, + 0x2b, + 0x50, + 0x68, + 0x38, + 0x44, + 0x44, + 0x63, + 0x76, + 0x45, + 0x4c, + 0x4d, + 0x6f, + 0x32, + 0x70, + 0x6e, + 0x2b, + 0x39, + 0x63, + 0x2b, + 0x0a, + 0x59, + 0x4f, + 0x41, + 0x6f, + 0x58, + 0x74, + 0x75, + 0x45, + 0x44, + 0x33, + 0x33, + 0x4a, + 0x39, + 0x4e, + 0x31, + 0x73, + 0x76, + 0x32, + 0x55, + 0x57, + 0x72, + 0x6b, + 0x6f, + 0x4c, + 0x45, + 0x4a, + 0x38, + 0x73, + 0x34, + 0x61, + 0x32, + 0x63, + 0x2f, + 0x56, + 0x69, + 0x65, + 0x66, + 0x76, + 0x4d, + 0x46, + 0x4b, + 0x6f, + 0x2b, + 0x4f, + 0x45, + 0x50, + 0x42, + 0x4f, + 0x71, + 0x57, + 0x45, + 0x53, + 0x39, + 0x49, + 0x65, + 0x4d, + 0x58, + 0x69, + 0x4c, + 0x42, + 0x44, + 0x72, + 0x2f, + 0x69, + 0x0a, + 0x4d, + 0x44, + 0x57, + 0x31, + 0x57, + 0x4a, + 0x76, + 0x46, + 0x74, + 0x57, + 0x78, + 0x32, + 0x57, + 0x33, + 0x56, + 0x74, + 0x55, + 0x6e, + 0x6a, + 0x4d, + 0x4b, + 0x62, + 0x52, + 0x47, + 0x2b, + 0x78, + 0x6a, + 0x34, + 0x46, + 0x47, + 0x6a, + 0x76, + 0x38, + 0x69, + 0x63, + 0x2f, + 0x34, + 0x53, + 0x71, + 0x6e, + 0x4b, + 0x36, + 0x53, + 0x43, + 0x65, + 0x2f, + 0x79, + 0x4b, + 0x47, + 0x67, + 0x45, + 0x79, + 0x76, + 0x6f, + 0x2f, + 0x61, + 0x66, + 0x4e, + 0x6d, + 0x39, + 0x30, + 0x49, + 0x74, + 0x76, + 0x0a, + 0x31, + 0x45, + 0x36, + 0x61, + 0x69, + 0x41, + 0x79, + 0x49, + 0x4d, + 0x73, + 0x6d, + 0x51, + 0x43, + 0x31, + 0x4a, + 0x4f, + 0x6f, + 0x56, + 0x68, + 0x2f, + 0x65, + 0x30, + 0x71, + 0x44, + 0x47, + 0x71, + 0x70, + 0x49, + 0x56, + 0x49, + 0x37, + 0x41, + 0x2f, + 0x4f, + 0x73, + 0x7a, + 0x39, + 0x74, + 0x53, + 0x68, + 0x78, + 0x44, + 0x65, + 0x52, + 0x75, + 0x5a, + 0x46, + 0x39, + 0x2b, + 0x67, + 0x4d, + 0x4d, + 0x6e, + 0x30, + 0x68, + 0x41, + 0x4a, + 0x6b, + 0x62, + 0x35, + 0x52, + 0x33, + 0x4d, + 0x78, + 0x0a, + 0x6f, + 0x38, + 0x58, + 0x36, + 0x6b, + 0x44, + 0x77, + 0x61, + 0x73, + 0x7a, + 0x48, + 0x48, + 0x76, + 0x52, + 0x55, + 0x73, + 0x66, + 0x54, + 0x66, + 0x6b, + 0x66, + 0x57, + 0x38, + 0x50, + 0x31, + 0x51, + 0x62, + 0x48, + 0x77, + 0x6e, + 0x4d, + 0x78, + 0x65, + 0x53, + 0x79, + 0x70, + 0x75, + 0x30, + 0x61, + 0x49, + 0x78, + 0x77, + 0x2b, + 0x63, + 0x38, + 0x44, + 0x6d, + 0x7a, + 0x74, + 0x2b, + 0x6c, + 0x73, + 0x37, + 0x62, + 0x6a, + 0x32, + 0x6d, + 0x32, + 0x36, + 0x48, + 0x38, + 0x39, + 0x4a, + 0x68, + 0x0a, + 0x59, + 0x6c, + 0x63, + 0x72, + 0x68, + 0x64, + 0x33, + 0x44, + 0x6d, + 0x2f, + 0x37, + 0x61, + 0x68, + 0x6c, + 0x56, + 0x66, + 0x64, + 0x35, + 0x79, + 0x4b, + 0x77, + 0x41, + 0x57, + 0x38, + 0x76, + 0x77, + 0x32, + 0x77, + 0x57, + 0x79, + 0x45, + 0x43, + 0x56, + 0x79, + 0x37, + 0x4c, + 0x4c, + 0x5a, + 0x36, + 0x63, + 0x4b, + 0x5a, + 0x57, + 0x57, + 0x32, + 0x6d, + 0x5a, + 0x47, + 0x4b, + 0x6a, + 0x37, + 0x7a, + 0x75, + 0x78, + 0x6b, + 0x56, + 0x65, + 0x39, + 0x42, + 0x2f, + 0x4d, + 0x44, + 0x54, + 0x75, + 0x0a, + 0x54, + 0x6e, + 0x39, + 0x7a, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4d, + 0x4c, + 0x70, + 0x6f, + 0x2b, + 0x53, + 0x58, + 0x43, + 0x46, + 0x2b, + 0x35, + 0x73, + 0x4d, + 0x35, + 0x68, + 0x57, + 0x62, + 0x4a, + 0x6d, + 0x38, + 0x77, + 0x52, + 0x64, + 0x4e, + 0x36, + 0x45, + 0x63, + 0x0a, + 0x4d, + 0x48, + 0x6a, + 0x54, + 0x33, + 0x74, + 0x61, + 0x2b, + 0x7a, + 0x79, + 0x74, + 0x6d, + 0x33, + 0x6f, + 0x79, + 0x71, + 0x59, + 0x63, + 0x53, + 0x2f, + 0x62, + 0x53, + 0x43, + 0x56, + 0x74, + 0x30, + 0x6a, + 0x38, + 0x65, + 0x71, + 0x72, + 0x5a, + 0x54, + 0x33, + 0x73, + 0x63, + 0x43, + 0x77, + 0x77, + 0x69, + 0x77, + 0x53, + 0x79, + 0x32, + 0x73, + 0x37, + 0x5a, + 0x63, + 0x65, + 0x74, + 0x35, + 0x75, + 0x6d, + 0x6b, + 0x46, + 0x55, + 0x48, + 0x62, + 0x36, + 0x4a, + 0x65, + 0x6e, + 0x30, + 0x51, + 0x0a, + 0x6c, + 0x58, + 0x4b, + 0x42, + 0x6f, + 0x61, + 0x53, + 0x47, + 0x54, + 0x57, + 0x42, + 0x4f, + 0x5a, + 0x62, + 0x6b, + 0x69, + 0x55, + 0x36, + 0x4f, + 0x36, + 0x6d, + 0x36, + 0x52, + 0x4c, + 0x4d, + 0x72, + 0x4d, + 0x6d, + 0x62, + 0x70, + 0x32, + 0x6b, + 0x68, + 0x4e, + 0x61, + 0x67, + 0x36, + 0x59, + 0x71, + 0x39, + 0x70, + 0x33, + 0x73, + 0x66, + 0x73, + 0x59, + 0x7a, + 0x38, + 0x69, + 0x45, + 0x31, + 0x38, + 0x7a, + 0x36, + 0x43, + 0x31, + 0x76, + 0x70, + 0x75, + 0x4b, + 0x63, + 0x39, + 0x51, + 0x54, + 0x0a, + 0x50, + 0x51, + 0x4c, + 0x51, + 0x65, + 0x7a, + 0x6c, + 0x68, + 0x77, + 0x5a, + 0x5a, + 0x57, + 0x52, + 0x4e, + 0x77, + 0x6b, + 0x43, + 0x79, + 0x4e, + 0x52, + 0x42, + 0x6a, + 0x69, + 0x39, + 0x4e, + 0x37, + 0x55, + 0x2f, + 0x75, + 0x4f, + 0x52, + 0x4f, + 0x49, + 0x38, + 0x64, + 0x6b, + 0x36, + 0x4b, + 0x48, + 0x6e, + 0x4d, + 0x5a, + 0x52, + 0x32, + 0x69, + 0x52, + 0x34, + 0x65, + 0x43, + 0x53, + 0x42, + 0x32, + 0x67, + 0x74, + 0x6b, + 0x76, + 0x68, + 0x50, + 0x65, + 0x4c, + 0x55, + 0x58, + 0x77, + 0x58, + 0x0a, + 0x38, + 0x4a, + 0x79, + 0x6d, + 0x32, + 0x51, + 0x48, + 0x58, + 0x35, + 0x43, + 0x59, + 0x37, + 0x2f, + 0x59, + 0x4d, + 0x66, + 0x73, + 0x34, + 0x44, + 0x30, + 0x49, + 0x38, + 0x66, + 0x70, + 0x6a, + 0x53, + 0x4f, + 0x65, + 0x65, + 0x52, + 0x59, + 0x33, + 0x63, + 0x6e, + 0x4a, + 0x76, + 0x33, + 0x37, + 0x4c, + 0x75, + 0x51, + 0x4c, + 0x35, + 0x72, + 0x4a, + 0x70, + 0x75, + 0x76, + 0x49, + 0x5a, + 0x78, + 0x46, + 0x4a, + 0x33, + 0x6e, + 0x56, + 0x72, + 0x37, + 0x56, + 0x2b, + 0x44, + 0x54, + 0x4d, + 0x72, + 0x0a, + 0x4e, + 0x71, + 0x4e, + 0x45, + 0x34, + 0x69, + 0x57, + 0x48, + 0x4d, + 0x56, + 0x58, + 0x71, + 0x63, + 0x45, + 0x62, + 0x4d, + 0x53, + 0x2f, + 0x59, + 0x4a, + 0x6a, + 0x38, + 0x34, + 0x69, + 0x63, + 0x47, + 0x65, + 0x31, + 0x70, + 0x43, + 0x55, + 0x30, + 0x31, + 0x65, + 0x49, + 0x2f, + 0x61, + 0x33, + 0x34, + 0x72, + 0x79, + 0x53, + 0x67, + 0x55, + 0x75, + 0x2b, + 0x33, + 0x53, + 0x43, + 0x55, + 0x55, + 0x61, + 0x58, + 0x5a, + 0x73, + 0x2f, + 0x2b, + 0x51, + 0x41, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char server_key[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x4a, + 0x4b, + 0x41, + 0x49, + 0x42, + 0x41, + 0x41, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x79, + 0x31, + 0x65, + 0x71, + 0x47, + 0x4a, + 0x69, + 0x45, + 0x61, + 0x4e, + 0x55, + 0x76, + 0x42, + 0x57, + 0x35, + 0x72, + 0x4b, + 0x38, + 0x77, + 0x77, + 0x69, + 0x75, + 0x4d, + 0x72, + 0x74, + 0x4c, + 0x67, + 0x74, + 0x74, + 0x45, + 0x65, + 0x55, + 0x31, + 0x51, + 0x4f, + 0x30, + 0x6e, + 0x4d, + 0x57, + 0x6a, + 0x59, + 0x43, + 0x4f, + 0x34, + 0x75, + 0x36, + 0x61, + 0x31, + 0x0a, + 0x34, + 0x58, + 0x34, + 0x6f, + 0x75, + 0x7a, + 0x68, + 0x57, + 0x4e, + 0x6a, + 0x50, + 0x47, + 0x35, + 0x31, + 0x6f, + 0x4a, + 0x43, + 0x76, + 0x4b, + 0x38, + 0x78, + 0x71, + 0x58, + 0x52, + 0x33, + 0x2f, + 0x7a, + 0x62, + 0x79, + 0x36, + 0x66, + 0x66, + 0x48, + 0x69, + 0x42, + 0x61, + 0x4e, + 0x68, + 0x4d, + 0x34, + 0x4c, + 0x6e, + 0x6c, + 0x78, + 0x44, + 0x30, + 0x77, + 0x4d, + 0x31, + 0x33, + 0x36, + 0x6d, + 0x59, + 0x77, + 0x51, + 0x54, + 0x6a, + 0x35, + 0x33, + 0x57, + 0x36, + 0x64, + 0x4e, + 0x2b, + 0x0a, + 0x6a, + 0x33, + 0x7a, + 0x7a, + 0x61, + 0x4d, + 0x7a, + 0x78, + 0x36, + 0x4f, + 0x55, + 0x30, + 0x56, + 0x4e, + 0x77, + 0x4f, + 0x68, + 0x4d, + 0x2b, + 0x47, + 0x67, + 0x33, + 0x76, + 0x43, + 0x70, + 0x2f, + 0x45, + 0x37, + 0x58, + 0x6b, + 0x57, + 0x65, + 0x4a, + 0x50, + 0x42, + 0x4d, + 0x4a, + 0x62, + 0x33, + 0x52, + 0x75, + 0x62, + 0x50, + 0x76, + 0x61, + 0x33, + 0x72, + 0x4d, + 0x73, + 0x36, + 0x62, + 0x63, + 0x4b, + 0x6c, + 0x6d, + 0x36, + 0x35, + 0x30, + 0x64, + 0x4b, + 0x64, + 0x63, + 0x65, + 0x71, + 0x0a, + 0x32, + 0x41, + 0x37, + 0x50, + 0x71, + 0x64, + 0x4a, + 0x4a, + 0x74, + 0x4f, + 0x4d, + 0x37, + 0x56, + 0x42, + 0x48, + 0x63, + 0x73, + 0x33, + 0x34, + 0x4f, + 0x38, + 0x33, + 0x54, + 0x68, + 0x73, + 0x78, + 0x63, + 0x46, + 0x39, + 0x37, + 0x2f, + 0x51, + 0x31, + 0x55, + 0x54, + 0x39, + 0x43, + 0x6c, + 0x34, + 0x34, + 0x34, + 0x59, + 0x39, + 0x6c, + 0x49, + 0x59, + 0x49, + 0x36, + 0x30, + 0x71, + 0x38, + 0x4e, + 0x6e, + 0x67, + 0x36, + 0x37, + 0x49, + 0x71, + 0x47, + 0x62, + 0x5a, + 0x47, + 0x69, + 0x73, + 0x0a, + 0x59, + 0x6d, + 0x63, + 0x75, + 0x5a, + 0x66, + 0x45, + 0x4d, + 0x55, + 0x65, + 0x59, + 0x45, + 0x79, + 0x6e, + 0x54, + 0x52, + 0x30, + 0x42, + 0x78, + 0x36, + 0x73, + 0x45, + 0x52, + 0x45, + 0x73, + 0x4f, + 0x51, + 0x74, + 0x47, + 0x6c, + 0x31, + 0x62, + 0x6a, + 0x77, + 0x72, + 0x61, + 0x4c, + 0x6d, + 0x30, + 0x46, + 0x43, + 0x38, + 0x42, + 0x77, + 0x6c, + 0x6e, + 0x4f, + 0x55, + 0x76, + 0x78, + 0x44, + 0x34, + 0x66, + 0x5a, + 0x2b, + 0x43, + 0x35, + 0x4a, + 0x73, + 0x48, + 0x62, + 0x5a, + 0x35, + 0x54, + 0x0a, + 0x4b, + 0x30, + 0x46, + 0x74, + 0x73, + 0x32, + 0x65, + 0x43, + 0x2f, + 0x71, + 0x48, + 0x4a, + 0x48, + 0x4c, + 0x52, + 0x73, + 0x41, + 0x6a, + 0x4e, + 0x70, + 0x67, + 0x66, + 0x42, + 0x71, + 0x6c, + 0x45, + 0x6a, + 0x47, + 0x73, + 0x74, + 0x4e, + 0x61, + 0x64, + 0x30, + 0x79, + 0x49, + 0x48, + 0x76, + 0x49, + 0x36, + 0x39, + 0x32, + 0x4b, + 0x79, + 0x56, + 0x54, + 0x37, + 0x56, + 0x4b, + 0x67, + 0x56, + 0x59, + 0x42, + 0x74, + 0x39, + 0x58, + 0x6e, + 0x4b, + 0x36, + 0x48, + 0x41, + 0x36, + 0x66, + 0x42, + 0x0a, + 0x4d, + 0x78, + 0x4b, + 0x68, + 0x31, + 0x48, + 0x54, + 0x30, + 0x37, + 0x47, + 0x42, + 0x52, + 0x71, + 0x34, + 0x44, + 0x62, + 0x4b, + 0x73, + 0x41, + 0x76, + 0x5a, + 0x44, + 0x73, + 0x45, + 0x7a, + 0x6b, + 0x61, + 0x64, + 0x67, + 0x75, + 0x6c, + 0x53, + 0x6a, + 0x6d, + 0x72, + 0x6b, + 0x57, + 0x41, + 0x44, + 0x43, + 0x53, + 0x77, + 0x6d, + 0x58, + 0x7a, + 0x44, + 0x4a, + 0x49, + 0x4f, + 0x6c, + 0x68, + 0x78, + 0x43, + 0x76, + 0x73, + 0x57, + 0x76, + 0x4e, + 0x4b, + 0x4f, + 0x70, + 0x7a, + 0x57, + 0x6a, + 0x0a, + 0x31, + 0x53, + 0x7a, + 0x75, + 0x45, + 0x51, + 0x53, + 0x57, + 0x53, + 0x38, + 0x30, + 0x46, + 0x6d, + 0x54, + 0x58, + 0x35, + 0x55, + 0x39, + 0x4e, + 0x6f, + 0x2f, + 0x6e, + 0x45, + 0x52, + 0x6a, + 0x4b, + 0x6c, + 0x78, + 0x75, + 0x44, + 0x36, + 0x37, + 0x35, + 0x67, + 0x4c, + 0x4e, + 0x79, + 0x30, + 0x6c, + 0x61, + 0x63, + 0x6b, + 0x70, + 0x73, + 0x49, + 0x48, + 0x50, + 0x5a, + 0x36, + 0x7a, + 0x6f, + 0x71, + 0x53, + 0x6c, + 0x38, + 0x4c, + 0x68, + 0x70, + 0x5a, + 0x7a, + 0x78, + 0x51, + 0x67, + 0x75, + 0x0a, + 0x56, + 0x51, + 0x72, + 0x39, + 0x62, + 0x69, + 0x4e, + 0x45, + 0x57, + 0x77, + 0x30, + 0x62, + 0x64, + 0x53, + 0x45, + 0x44, + 0x57, + 0x2b, + 0x63, + 0x68, + 0x6e, + 0x41, + 0x72, + 0x7a, + 0x71, + 0x4b, + 0x71, + 0x62, + 0x65, + 0x54, + 0x34, + 0x38, + 0x52, + 0x74, + 0x71, + 0x46, + 0x77, + 0x54, + 0x45, + 0x44, + 0x69, + 0x6f, + 0x48, + 0x38, + 0x73, + 0x47, + 0x47, + 0x69, + 0x43, + 0x2b, + 0x7a, + 0x6a, + 0x50, + 0x31, + 0x76, + 0x59, + 0x78, + 0x44, + 0x66, + 0x6d, + 0x32, + 0x49, + 0x69, + 0x68, + 0x0a, + 0x41, + 0x4f, + 0x50, + 0x74, + 0x48, + 0x6e, + 0x45, + 0x33, + 0x78, + 0x7a, + 0x6b, + 0x44, + 0x4c, + 0x41, + 0x50, + 0x4a, + 0x76, + 0x7a, + 0x45, + 0x61, + 0x2f, + 0x5a, + 0x69, + 0x61, + 0x54, + 0x69, + 0x4d, + 0x61, + 0x30, + 0x4f, + 0x52, + 0x32, + 0x71, + 0x74, + 0x75, + 0x30, + 0x68, + 0x54, + 0x6a, + 0x62, + 0x34, + 0x63, + 0x7a, + 0x58, + 0x49, + 0x36, + 0x54, + 0x79, + 0x37, + 0x78, + 0x6b, + 0x53, + 0x4c, + 0x45, + 0x74, + 0x44, + 0x4f, + 0x6a, + 0x43, + 0x61, + 0x70, + 0x53, + 0x71, + 0x74, + 0x0a, + 0x34, + 0x45, + 0x4b, + 0x47, + 0x67, + 0x43, + 0x69, + 0x78, + 0x7a, + 0x63, + 0x55, + 0x35, + 0x45, + 0x36, + 0x5a, + 0x4d, + 0x49, + 0x4f, + 0x56, + 0x6a, + 0x72, + 0x66, + 0x6a, + 0x39, + 0x70, + 0x77, + 0x67, + 0x67, + 0x2f, + 0x66, + 0x56, + 0x31, + 0x35, + 0x30, + 0x63, + 0x31, + 0x68, + 0x6e, + 0x6e, + 0x56, + 0x47, + 0x75, + 0x36, + 0x51, + 0x42, + 0x30, + 0x44, + 0x72, + 0x59, + 0x4e, + 0x73, + 0x59, + 0x50, + 0x43, + 0x44, + 0x44, + 0x50, + 0x70, + 0x4d, + 0x43, + 0x41, + 0x77, + 0x45, + 0x41, + 0x0a, + 0x41, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x74, + 0x69, + 0x33, + 0x49, + 0x65, + 0x31, + 0x6a, + 0x4a, + 0x36, + 0x4a, + 0x4a, + 0x33, + 0x47, + 0x6b, + 0x71, + 0x66, + 0x51, + 0x68, + 0x49, + 0x69, + 0x75, + 0x34, + 0x78, + 0x6b, + 0x4c, + 0x2f, + 0x6d, + 0x44, + 0x47, + 0x53, + 0x32, + 0x34, + 0x72, + 0x6c, + 0x41, + 0x76, + 0x6c, + 0x50, + 0x57, + 0x4e, + 0x4e, + 0x69, + 0x6c, + 0x4b, + 0x37, + 0x50, + 0x53, + 0x6a, + 0x70, + 0x32, + 0x38, + 0x38, + 0x56, + 0x75, + 0x42, + 0x57, + 0x0a, + 0x66, + 0x53, + 0x46, + 0x4f, + 0x5a, + 0x79, + 0x42, + 0x48, + 0x55, + 0x63, + 0x50, + 0x41, + 0x56, + 0x63, + 0x6c, + 0x69, + 0x69, + 0x32, + 0x63, + 0x6b, + 0x46, + 0x51, + 0x5a, + 0x46, + 0x34, + 0x39, + 0x76, + 0x67, + 0x64, + 0x75, + 0x6f, + 0x70, + 0x50, + 0x35, + 0x6f, + 0x57, + 0x4c, + 0x54, + 0x70, + 0x5a, + 0x6c, + 0x77, + 0x67, + 0x6c, + 0x55, + 0x6a, + 0x6a, + 0x6f, + 0x76, + 0x5a, + 0x63, + 0x51, + 0x59, + 0x56, + 0x58, + 0x57, + 0x34, + 0x66, + 0x39, + 0x30, + 0x70, + 0x4d, + 0x52, + 0x5a, + 0x0a, + 0x52, + 0x2b, + 0x74, + 0x79, + 0x51, + 0x30, + 0x55, + 0x55, + 0x7a, + 0x50, + 0x43, + 0x51, + 0x39, + 0x32, + 0x50, + 0x2b, + 0x4e, + 0x6a, + 0x44, + 0x75, + 0x6d, + 0x37, + 0x75, + 0x4c, + 0x77, + 0x46, + 0x52, + 0x36, + 0x4a, + 0x4b, + 0x59, + 0x34, + 0x4c, + 0x45, + 0x53, + 0x46, + 0x6b, + 0x4b, + 0x6e, + 0x6f, + 0x6c, + 0x46, + 0x77, + 0x2b, + 0x6a, + 0x4d, + 0x4c, + 0x76, + 0x72, + 0x52, + 0x34, + 0x32, + 0x47, + 0x2b, + 0x66, + 0x54, + 0x72, + 0x4e, + 0x63, + 0x34, + 0x50, + 0x6c, + 0x48, + 0x6c, + 0x0a, + 0x4c, + 0x32, + 0x63, + 0x57, + 0x56, + 0x47, + 0x2f, + 0x63, + 0x61, + 0x41, + 0x50, + 0x63, + 0x71, + 0x55, + 0x70, + 0x69, + 0x6d, + 0x2b, + 0x31, + 0x59, + 0x72, + 0x32, + 0x72, + 0x76, + 0x55, + 0x75, + 0x51, + 0x49, + 0x51, + 0x53, + 0x33, + 0x42, + 0x6b, + 0x48, + 0x54, + 0x45, + 0x69, + 0x6f, + 0x7a, + 0x2b, + 0x33, + 0x70, + 0x74, + 0x76, + 0x65, + 0x39, + 0x68, + 0x2b, + 0x32, + 0x4c, + 0x77, + 0x70, + 0x57, + 0x49, + 0x42, + 0x66, + 0x55, + 0x5a, + 0x70, + 0x58, + 0x2b, + 0x34, + 0x56, + 0x48, + 0x0a, + 0x49, + 0x47, + 0x6b, + 0x51, + 0x69, + 0x63, + 0x4b, + 0x33, + 0x35, + 0x39, + 0x68, + 0x63, + 0x49, + 0x32, + 0x68, + 0x47, + 0x6b, + 0x71, + 0x55, + 0x62, + 0x73, + 0x36, + 0x39, + 0x49, + 0x78, + 0x4a, + 0x4e, + 0x59, + 0x66, + 0x70, + 0x65, + 0x32, + 0x47, + 0x5a, + 0x31, + 0x45, + 0x51, + 0x63, + 0x63, + 0x7a, + 0x30, + 0x53, + 0x73, + 0x48, + 0x4e, + 0x71, + 0x57, + 0x65, + 0x77, + 0x2b, + 0x6f, + 0x52, + 0x63, + 0x61, + 0x69, + 0x54, + 0x6f, + 0x77, + 0x46, + 0x76, + 0x6f, + 0x33, + 0x68, + 0x74, + 0x0a, + 0x36, + 0x47, + 0x78, + 0x53, + 0x6e, + 0x71, + 0x45, + 0x57, + 0x30, + 0x37, + 0x48, + 0x30, + 0x4b, + 0x54, + 0x4b, + 0x59, + 0x69, + 0x6c, + 0x6e, + 0x5a, + 0x69, + 0x34, + 0x58, + 0x49, + 0x57, + 0x33, + 0x48, + 0x35, + 0x30, + 0x51, + 0x61, + 0x75, + 0x61, + 0x68, + 0x62, + 0x77, + 0x48, + 0x30, + 0x75, + 0x56, + 0x73, + 0x64, + 0x32, + 0x55, + 0x39, + 0x6f, + 0x34, + 0x6c, + 0x37, + 0x54, + 0x38, + 0x5a, + 0x4d, + 0x65, + 0x34, + 0x52, + 0x50, + 0x6d, + 0x66, + 0x39, + 0x77, + 0x46, + 0x6e, + 0x2f, + 0x0a, + 0x45, + 0x7a, + 0x34, + 0x46, + 0x74, + 0x68, + 0x62, + 0x6e, + 0x51, + 0x4f, + 0x57, + 0x42, + 0x6f, + 0x31, + 0x44, + 0x78, + 0x61, + 0x42, + 0x6d, + 0x78, + 0x39, + 0x42, + 0x66, + 0x6e, + 0x35, + 0x6e, + 0x43, + 0x35, + 0x49, + 0x36, + 0x52, + 0x79, + 0x72, + 0x36, + 0x58, + 0x4d, + 0x6e, + 0x6e, + 0x4a, + 0x4a, + 0x2b, + 0x65, + 0x41, + 0x74, + 0x53, + 0x6a, + 0x58, + 0x53, + 0x41, + 0x56, + 0x37, + 0x2f, + 0x36, + 0x33, + 0x43, + 0x72, + 0x70, + 0x63, + 0x42, + 0x2b, + 0x73, + 0x42, + 0x74, + 0x46, + 0x0a, + 0x69, + 0x7a, + 0x46, + 0x33, + 0x79, + 0x45, + 0x51, + 0x38, + 0x72, + 0x74, + 0x57, + 0x41, + 0x43, + 0x71, + 0x78, + 0x6a, + 0x4d, + 0x55, + 0x50, + 0x43, + 0x33, + 0x4a, + 0x71, + 0x39, + 0x6c, + 0x39, + 0x33, + 0x39, + 0x38, + 0x2f, + 0x6a, + 0x7a, + 0x43, + 0x70, + 0x5a, + 0x5a, + 0x4c, + 0x76, + 0x7a, + 0x34, + 0x4e, + 0x55, + 0x6a, + 0x6a, + 0x74, + 0x66, + 0x4e, + 0x68, + 0x33, + 0x69, + 0x36, + 0x44, + 0x50, + 0x36, + 0x49, + 0x68, + 0x41, + 0x4b, + 0x31, + 0x33, + 0x54, + 0x56, + 0x37, + 0x49, + 0x0a, + 0x34, + 0x47, + 0x34, + 0x30, + 0x62, + 0x72, + 0x5a, + 0x68, + 0x6d, + 0x58, + 0x53, + 0x56, + 0x4b, + 0x42, + 0x6e, + 0x78, + 0x47, + 0x37, + 0x44, + 0x35, + 0x69, + 0x59, + 0x36, + 0x50, + 0x62, + 0x67, + 0x56, + 0x57, + 0x48, + 0x46, + 0x44, + 0x45, + 0x6a, + 0x50, + 0x44, + 0x77, + 0x6f, + 0x4a, + 0x6b, + 0x74, + 0x4b, + 0x31, + 0x42, + 0x50, + 0x61, + 0x64, + 0x33, + 0x71, + 0x57, + 0x66, + 0x39, + 0x34, + 0x66, + 0x6d, + 0x74, + 0x35, + 0x79, + 0x6b, + 0x5a, + 0x64, + 0x2f, + 0x74, + 0x65, + 0x71, + 0x0a, + 0x65, + 0x52, + 0x49, + 0x45, + 0x76, + 0x55, + 0x7a, + 0x4a, + 0x47, + 0x35, + 0x50, + 0x46, + 0x47, + 0x77, + 0x4f, + 0x7a, + 0x78, + 0x56, + 0x52, + 0x48, + 0x44, + 0x4e, + 0x38, + 0x51, + 0x51, + 0x6d, + 0x4e, + 0x4a, + 0x63, + 0x43, + 0x4a, + 0x75, + 0x79, + 0x74, + 0x77, + 0x34, + 0x44, + 0x6e, + 0x73, + 0x78, + 0x6f, + 0x43, + 0x38, + 0x33, + 0x69, + 0x7a, + 0x66, + 0x68, + 0x2b, + 0x6e, + 0x45, + 0x71, + 0x45, + 0x50, + 0x50, + 0x51, + 0x69, + 0x44, + 0x46, + 0x6f, + 0x54, + 0x4c, + 0x79, + 0x44, + 0x0a, + 0x2b, + 0x34, + 0x56, + 0x52, + 0x67, + 0x46, + 0x42, + 0x62, + 0x36, + 0x61, + 0x69, + 0x72, + 0x56, + 0x63, + 0x79, + 0x58, + 0x57, + 0x64, + 0x72, + 0x32, + 0x30, + 0x43, + 0x64, + 0x4f, + 0x47, + 0x67, + 0x58, + 0x6f, + 0x42, + 0x4d, + 0x72, + 0x4e, + 0x42, + 0x6c, + 0x2b, + 0x45, + 0x36, + 0x32, + 0x4d, + 0x59, + 0x41, + 0x65, + 0x32, + 0x71, + 0x63, + 0x36, + 0x37, + 0x63, + 0x51, + 0x59, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4f, + 0x39, + 0x32, + 0x58, + 0x4e, + 0x41, + 0x46, + 0x0a, + 0x52, + 0x41, + 0x77, + 0x5a, + 0x79, + 0x64, + 0x7a, + 0x59, + 0x63, + 0x68, + 0x35, + 0x45, + 0x35, + 0x65, + 0x6a, + 0x7a, + 0x39, + 0x54, + 0x5a, + 0x62, + 0x48, + 0x66, + 0x65, + 0x66, + 0x34, + 0x7a, + 0x4e, + 0x42, + 0x48, + 0x55, + 0x2f, + 0x6d, + 0x56, + 0x31, + 0x76, + 0x68, + 0x38, + 0x39, + 0x64, + 0x39, + 0x4b, + 0x6a, + 0x53, + 0x46, + 0x2f, + 0x4f, + 0x69, + 0x4c, + 0x33, + 0x45, + 0x6e, + 0x6f, + 0x59, + 0x78, + 0x50, + 0x6b, + 0x48, + 0x67, + 0x55, + 0x58, + 0x79, + 0x6e, + 0x39, + 0x41, + 0x0a, + 0x34, + 0x43, + 0x34, + 0x75, + 0x31, + 0x72, + 0x6c, + 0x64, + 0x5a, + 0x36, + 0x64, + 0x77, + 0x43, + 0x56, + 0x41, + 0x62, + 0x5a, + 0x4a, + 0x6e, + 0x54, + 0x38, + 0x55, + 0x46, + 0x32, + 0x6e, + 0x4c, + 0x52, + 0x46, + 0x4d, + 0x58, + 0x68, + 0x61, + 0x71, + 0x79, + 0x4a, + 0x45, + 0x6d, + 0x54, + 0x37, + 0x6a, + 0x43, + 0x59, + 0x52, + 0x79, + 0x37, + 0x65, + 0x72, + 0x64, + 0x37, + 0x56, + 0x4c, + 0x39, + 0x35, + 0x67, + 0x56, + 0x68, + 0x55, + 0x4f, + 0x73, + 0x4a, + 0x59, + 0x77, + 0x4c, + 0x6d, + 0x0a, + 0x74, + 0x59, + 0x66, + 0x63, + 0x69, + 0x6b, + 0x30, + 0x37, + 0x51, + 0x6d, + 0x44, + 0x53, + 0x50, + 0x56, + 0x75, + 0x47, + 0x68, + 0x56, + 0x32, + 0x4b, + 0x65, + 0x6b, + 0x6e, + 0x7a, + 0x54, + 0x61, + 0x31, + 0x4e, + 0x53, + 0x54, + 0x55, + 0x38, + 0x76, + 0x77, + 0x51, + 0x46, + 0x2b, + 0x77, + 0x4b, + 0x48, + 0x69, + 0x38, + 0x36, + 0x34, + 0x64, + 0x6d, + 0x7a, + 0x6b, + 0x73, + 0x6c, + 0x59, + 0x6e, + 0x51, + 0x71, + 0x62, + 0x46, + 0x6f, + 0x54, + 0x56, + 0x52, + 0x5a, + 0x74, + 0x4e, + 0x65, + 0x0a, + 0x72, + 0x4c, + 0x62, + 0x36, + 0x67, + 0x36, + 0x6e, + 0x63, + 0x77, + 0x36, + 0x4a, + 0x57, + 0x78, + 0x39, + 0x79, + 0x65, + 0x42, + 0x30, + 0x37, + 0x56, + 0x64, + 0x70, + 0x4a, + 0x70, + 0x36, + 0x78, + 0x39, + 0x54, + 0x59, + 0x54, + 0x2b, + 0x48, + 0x47, + 0x6a, + 0x33, + 0x6b, + 0x62, + 0x35, + 0x61, + 0x63, + 0x75, + 0x49, + 0x6e, + 0x50, + 0x2b, + 0x6a, + 0x67, + 0x37, + 0x47, + 0x5a, + 0x7a, + 0x6b, + 0x72, + 0x48, + 0x4a, + 0x4d, + 0x6e, + 0x48, + 0x4c, + 0x77, + 0x65, + 0x46, + 0x76, + 0x62, + 0x0a, + 0x4e, + 0x57, + 0x5a, + 0x66, + 0x6c, + 0x50, + 0x66, + 0x69, + 0x41, + 0x4e, + 0x6d, + 0x77, + 0x4d, + 0x38, + 0x77, + 0x4d, + 0x52, + 0x2b, + 0x6b, + 0x66, + 0x50, + 0x32, + 0x76, + 0x6f, + 0x67, + 0x47, + 0x61, + 0x6d, + 0x4b, + 0x43, + 0x69, + 0x65, + 0x6f, + 0x6a, + 0x56, + 0x61, + 0x54, + 0x69, + 0x38, + 0x76, + 0x67, + 0x71, + 0x71, + 0x6a, + 0x78, + 0x34, + 0x55, + 0x47, + 0x50, + 0x64, + 0x35, + 0x39, + 0x36, + 0x46, + 0x4e, + 0x46, + 0x65, + 0x69, + 0x5a, + 0x52, + 0x76, + 0x58, + 0x4e, + 0x6b, + 0x0a, + 0x30, + 0x64, + 0x42, + 0x30, + 0x41, + 0x4b, + 0x32, + 0x51, + 0x2f, + 0x31, + 0x32, + 0x36, + 0x53, + 0x6c, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4e, + 0x6c, + 0x69, + 0x74, + 0x64, + 0x78, + 0x48, + 0x74, + 0x74, + 0x78, + 0x68, + 0x2f, + 0x57, + 0x71, + 0x77, + 0x46, + 0x4b, + 0x33, + 0x35, + 0x62, + 0x63, + 0x4f, + 0x54, + 0x58, + 0x61, + 0x68, + 0x36, + 0x50, + 0x61, + 0x70, + 0x50, + 0x64, + 0x67, + 0x2f, + 0x37, + 0x32, + 0x50, + 0x75, + 0x4f, + 0x6c, + 0x33, + 0x57, + 0x74, + 0x0a, + 0x37, + 0x57, + 0x6e, + 0x38, + 0x41, + 0x74, + 0x36, + 0x46, + 0x49, + 0x47, + 0x51, + 0x33, + 0x58, + 0x5a, + 0x4f, + 0x4f, + 0x50, + 0x66, + 0x39, + 0x76, + 0x35, + 0x48, + 0x66, + 0x71, + 0x50, + 0x70, + 0x53, + 0x6c, + 0x4e, + 0x56, + 0x39, + 0x33, + 0x41, + 0x58, + 0x58, + 0x61, + 0x4f, + 0x36, + 0x49, + 0x54, + 0x7a, + 0x77, + 0x49, + 0x45, + 0x62, + 0x38, + 0x49, + 0x47, + 0x33, + 0x69, + 0x45, + 0x4f, + 0x74, + 0x54, + 0x4c, + 0x77, + 0x32, + 0x39, + 0x32, + 0x4b, + 0x55, + 0x38, + 0x6c, + 0x4a, + 0x0a, + 0x4c, + 0x6c, + 0x38, + 0x58, + 0x70, + 0x68, + 0x4f, + 0x6b, + 0x56, + 0x55, + 0x72, + 0x50, + 0x6b, + 0x7a, + 0x47, + 0x47, + 0x36, + 0x64, + 0x66, + 0x56, + 0x4e, + 0x70, + 0x36, + 0x49, + 0x32, + 0x68, + 0x54, + 0x70, + 0x59, + 0x4a, + 0x49, + 0x53, + 0x77, + 0x41, + 0x55, + 0x35, + 0x48, + 0x76, + 0x4b, + 0x70, + 0x63, + 0x39, + 0x6f, + 0x42, + 0x30, + 0x59, + 0x4b, + 0x68, + 0x56, + 0x4c, + 0x66, + 0x43, + 0x2b, + 0x69, + 0x64, + 0x61, + 0x6b, + 0x56, + 0x43, + 0x35, + 0x66, + 0x5a, + 0x7a, + 0x36, + 0x0a, + 0x7a, + 0x46, + 0x41, + 0x6f, + 0x32, + 0x37, + 0x46, + 0x65, + 0x6f, + 0x49, + 0x45, + 0x64, + 0x48, + 0x74, + 0x39, + 0x32, + 0x5a, + 0x65, + 0x36, + 0x51, + 0x33, + 0x34, + 0x4e, + 0x4a, + 0x67, + 0x70, + 0x56, + 0x62, + 0x67, + 0x6d, + 0x54, + 0x30, + 0x2b, + 0x77, + 0x74, + 0x75, + 0x75, + 0x72, + 0x71, + 0x6f, + 0x5a, + 0x2f, + 0x7a, + 0x52, + 0x74, + 0x6b, + 0x78, + 0x34, + 0x35, + 0x77, + 0x75, + 0x71, + 0x49, + 0x33, + 0x45, + 0x2b, + 0x54, + 0x44, + 0x61, + 0x31, + 0x53, + 0x75, + 0x71, + 0x33, + 0x0a, + 0x34, + 0x4f, + 0x51, + 0x73, + 0x65, + 0x32, + 0x57, + 0x63, + 0x67, + 0x6a, + 0x7a, + 0x53, + 0x66, + 0x64, + 0x4c, + 0x4a, + 0x74, + 0x54, + 0x66, + 0x79, + 0x65, + 0x4c, + 0x31, + 0x70, + 0x34, + 0x56, + 0x68, + 0x47, + 0x79, + 0x63, + 0x78, + 0x67, + 0x2b, + 0x4a, + 0x45, + 0x35, + 0x48, + 0x34, + 0x6a, + 0x69, + 0x33, + 0x7a, + 0x35, + 0x33, + 0x46, + 0x78, + 0x72, + 0x4d, + 0x6e, + 0x41, + 0x30, + 0x6d, + 0x53, + 0x45, + 0x6c, + 0x79, + 0x45, + 0x6a, + 0x69, + 0x38, + 0x38, + 0x78, + 0x54, + 0x47, + 0x0a, + 0x48, + 0x43, + 0x59, + 0x47, + 0x49, + 0x6e, + 0x4a, + 0x70, + 0x5a, + 0x72, + 0x2b, + 0x51, + 0x7a, + 0x67, + 0x50, + 0x54, + 0x7a, + 0x57, + 0x66, + 0x35, + 0x52, + 0x34, + 0x36, + 0x6b, + 0x30, + 0x38, + 0x47, + 0x4b, + 0x73, + 0x6d, + 0x56, + 0x67, + 0x6a, + 0x31, + 0x50, + 0x32, + 0x7a, + 0x39, + 0x4f, + 0x65, + 0x33, + 0x61, + 0x4d, + 0x43, + 0x67, + 0x67, + 0x45, + 0x41, + 0x48, + 0x42, + 0x6b, + 0x64, + 0x5a, + 0x75, + 0x4e, + 0x58, + 0x53, + 0x72, + 0x77, + 0x7a, + 0x37, + 0x5a, + 0x41, + 0x51, + 0x0a, + 0x51, + 0x2f, + 0x44, + 0x39, + 0x73, + 0x55, + 0x6e, + 0x2b, + 0x2b, + 0x66, + 0x50, + 0x54, + 0x48, + 0x6c, + 0x31, + 0x4b, + 0x67, + 0x5a, + 0x63, + 0x67, + 0x59, + 0x32, + 0x47, + 0x35, + 0x32, + 0x6e, + 0x51, + 0x32, + 0x38, + 0x70, + 0x41, + 0x6a, + 0x52, + 0x61, + 0x70, + 0x37, + 0x4e, + 0x4b, + 0x5a, + 0x45, + 0x6f, + 0x50, + 0x39, + 0x39, + 0x73, + 0x4c, + 0x58, + 0x52, + 0x74, + 0x2f, + 0x4e, + 0x45, + 0x74, + 0x59, + 0x33, + 0x64, + 0x51, + 0x45, + 0x34, + 0x4b, + 0x73, + 0x42, + 0x46, + 0x2f, + 0x0a, + 0x75, + 0x69, + 0x76, + 0x78, + 0x53, + 0x38, + 0x38, + 0x4c, + 0x44, + 0x4f, + 0x6e, + 0x4c, + 0x6f, + 0x30, + 0x7a, + 0x52, + 0x73, + 0x6d, + 0x31, + 0x30, + 0x45, + 0x70, + 0x56, + 0x42, + 0x41, + 0x33, + 0x4a, + 0x64, + 0x4d, + 0x50, + 0x33, + 0x65, + 0x2f, + 0x67, + 0x57, + 0x6d, + 0x57, + 0x53, + 0x72, + 0x56, + 0x55, + 0x43, + 0x6d, + 0x75, + 0x74, + 0x65, + 0x37, + 0x6e, + 0x57, + 0x63, + 0x7a, + 0x75, + 0x4b, + 0x30, + 0x62, + 0x37, + 0x41, + 0x67, + 0x67, + 0x6b, + 0x79, + 0x6b, + 0x38, + 0x72, + 0x0a, + 0x79, + 0x4d, + 0x53, + 0x69, + 0x6f, + 0x6e, + 0x79, + 0x30, + 0x5a, + 0x58, + 0x64, + 0x38, + 0x52, + 0x66, + 0x55, + 0x67, + 0x70, + 0x6a, + 0x63, + 0x74, + 0x59, + 0x65, + 0x76, + 0x51, + 0x31, + 0x68, + 0x34, + 0x46, + 0x69, + 0x42, + 0x52, + 0x7a, + 0x6d, + 0x54, + 0x77, + 0x58, + 0x32, + 0x55, + 0x73, + 0x30, + 0x69, + 0x64, + 0x74, + 0x74, + 0x66, + 0x4c, + 0x67, + 0x76, + 0x35, + 0x46, + 0x75, + 0x36, + 0x33, + 0x77, + 0x35, + 0x36, + 0x34, + 0x62, + 0x57, + 0x66, + 0x67, + 0x57, + 0x30, + 0x41, + 0x0a, + 0x48, + 0x36, + 0x70, + 0x6a, + 0x4a, + 0x55, + 0x58, + 0x39, + 0x77, + 0x59, + 0x68, + 0x57, + 0x73, + 0x33, + 0x4e, + 0x75, + 0x50, + 0x57, + 0x7a, + 0x42, + 0x4f, + 0x6e, + 0x30, + 0x56, + 0x33, + 0x54, + 0x46, + 0x53, + 0x7a, + 0x71, + 0x39, + 0x78, + 0x44, + 0x2b, + 0x71, + 0x78, + 0x68, + 0x36, + 0x75, + 0x58, + 0x6f, + 0x38, + 0x74, + 0x4a, + 0x57, + 0x4e, + 0x67, + 0x59, + 0x65, + 0x33, + 0x77, + 0x77, + 0x41, + 0x30, + 0x64, + 0x44, + 0x69, + 0x74, + 0x47, + 0x6f, + 0x58, + 0x7a, + 0x58, + 0x4c, + 0x0a, + 0x74, + 0x37, + 0x39, + 0x76, + 0x4e, + 0x49, + 0x41, + 0x30, + 0x5a, + 0x2b, + 0x78, + 0x63, + 0x75, + 0x73, + 0x4b, + 0x37, + 0x58, + 0x6c, + 0x41, + 0x63, + 0x34, + 0x6a, + 0x64, + 0x4b, + 0x2f, + 0x55, + 0x75, + 0x74, + 0x5a, + 0x4f, + 0x67, + 0x45, + 0x51, + 0x78, + 0x57, + 0x59, + 0x73, + 0x63, + 0x6b, + 0x37, + 0x74, + 0x55, + 0x47, + 0x38, + 0x34, + 0x78, + 0x69, + 0x42, + 0x37, + 0x73, + 0x45, + 0x6b, + 0x6f, + 0x38, + 0x65, + 0x75, + 0x76, + 0x76, + 0x44, + 0x39, + 0x71, + 0x32, + 0x41, + 0x61, + 0x0a, + 0x4e, + 0x74, + 0x74, + 0x48, + 0x49, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x42, + 0x44, + 0x77, + 0x30, + 0x5a, + 0x6e, + 0x56, + 0x52, + 0x2b, + 0x47, + 0x55, + 0x54, + 0x46, + 0x61, + 0x71, + 0x57, + 0x71, + 0x34, + 0x31, + 0x4a, + 0x44, + 0x48, + 0x72, + 0x30, + 0x38, + 0x35, + 0x72, + 0x6d, + 0x6b, + 0x38, + 0x68, + 0x71, + 0x71, + 0x4a, + 0x7a, + 0x76, + 0x72, + 0x54, + 0x5a, + 0x35, + 0x36, + 0x45, + 0x2b, + 0x32, + 0x4f, + 0x50, + 0x62, + 0x78, + 0x52, + 0x42, + 0x34, + 0x77, + 0x4f, + 0x32, + 0x0a, + 0x32, + 0x5a, + 0x39, + 0x63, + 0x32, + 0x4c, + 0x45, + 0x77, + 0x78, + 0x2b, + 0x4e, + 0x54, + 0x45, + 0x66, + 0x7a, + 0x52, + 0x2f, + 0x7a, + 0x48, + 0x46, + 0x41, + 0x57, + 0x61, + 0x2b, + 0x49, + 0x76, + 0x6f, + 0x46, + 0x79, + 0x75, + 0x44, + 0x7a, + 0x67, + 0x77, + 0x69, + 0x48, + 0x56, + 0x66, + 0x61, + 0x32, + 0x48, + 0x55, + 0x79, + 0x55, + 0x68, + 0x59, + 0x6f, + 0x7a, + 0x34, + 0x2b, + 0x4f, + 0x38, + 0x55, + 0x67, + 0x69, + 0x58, + 0x63, + 0x64, + 0x71, + 0x2b, + 0x4d, + 0x72, + 0x4b, + 0x33, + 0x0a, + 0x73, + 0x44, + 0x37, + 0x30, + 0x36, + 0x46, + 0x5a, + 0x4b, + 0x68, + 0x6c, + 0x63, + 0x52, + 0x55, + 0x68, + 0x32, + 0x51, + 0x2b, + 0x54, + 0x4b, + 0x32, + 0x4e, + 0x67, + 0x77, + 0x50, + 0x56, + 0x4c, + 0x2f, + 0x31, + 0x36, + 0x71, + 0x61, + 0x48, + 0x5a, + 0x5a, + 0x75, + 0x32, + 0x65, + 0x61, + 0x33, + 0x62, + 0x33, + 0x6e, + 0x5a, + 0x32, + 0x41, + 0x41, + 0x42, + 0x65, + 0x52, + 0x6f, + 0x51, + 0x47, + 0x68, + 0x42, + 0x4e, + 0x65, + 0x52, + 0x57, + 0x34, + 0x2b, + 0x4f, + 0x76, + 0x34, + 0x54, + 0x0a, + 0x59, + 0x55, + 0x31, + 0x33, + 0x59, + 0x51, + 0x50, + 0x6a, + 0x4f, + 0x74, + 0x76, + 0x42, + 0x70, + 0x41, + 0x2b, + 0x46, + 0x6d, + 0x56, + 0x48, + 0x72, + 0x51, + 0x49, + 0x5a, + 0x6c, + 0x43, + 0x4f, + 0x35, + 0x43, + 0x4d, + 0x79, + 0x36, + 0x2f, + 0x47, + 0x32, + 0x77, + 0x56, + 0x4b, + 0x48, + 0x62, + 0x31, + 0x4a, + 0x75, + 0x4d, + 0x48, + 0x63, + 0x4f, + 0x44, + 0x33, + 0x43, + 0x4d, + 0x70, + 0x43, + 0x6f, + 0x6e, + 0x45, + 0x44, + 0x44, + 0x32, + 0x36, + 0x6a, + 0x76, + 0x51, + 0x62, + 0x49, + 0x0a, + 0x43, + 0x4f, + 0x46, + 0x5a, + 0x37, + 0x4c, + 0x43, + 0x46, + 0x73, + 0x52, + 0x71, + 0x38, + 0x43, + 0x4a, + 0x35, + 0x4c, + 0x45, + 0x7a, + 0x33, + 0x69, + 0x62, + 0x4c, + 0x50, + 0x76, + 0x4b, + 0x50, + 0x78, + 0x51, + 0x67, + 0x6c, + 0x41, + 0x4f, + 0x75, + 0x37, + 0x62, + 0x44, + 0x44, + 0x74, + 0x62, + 0x79, + 0x4c, + 0x6d, + 0x58, + 0x49, + 0x4a, + 0x7a, + 0x76, + 0x51, + 0x33, + 0x65, + 0x31, + 0x30, + 0x49, + 0x31, + 0x45, + 0x77, + 0x39, + 0x53, + 0x61, + 0x56, + 0x76, + 0x59, + 0x39, + 0x65, + 0x0a, + 0x31, + 0x62, + 0x65, + 0x79, + 0x62, + 0x37, + 0x71, + 0x4f, + 0x31, + 0x36, + 0x44, + 0x62, + 0x73, + 0x54, + 0x75, + 0x6c, + 0x45, + 0x43, + 0x51, + 0x4e, + 0x2f, + 0x2b, + 0x79, + 0x4e, + 0x77, + 0x74, + 0x6b, + 0x44, + 0x34, + 0x58, + 0x69, + 0x37, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x41, + 0x63, + 0x73, + 0x6f, + 0x4f, + 0x7a, + 0x75, + 0x50, + 0x6d, + 0x37, + 0x35, + 0x4a, + 0x4f, + 0x32, + 0x42, + 0x33, + 0x76, + 0x32, + 0x45, + 0x64, + 0x2b, + 0x37, + 0x47, + 0x4b, + 0x31, + 0x53, + 0x63, + 0x0a, + 0x6c, + 0x76, + 0x56, + 0x4e, + 0x55, + 0x33, + 0x43, + 0x7a, + 0x30, + 0x4e, + 0x66, + 0x4c, + 0x50, + 0x4a, + 0x56, + 0x79, + 0x73, + 0x56, + 0x53, + 0x48, + 0x65, + 0x67, + 0x61, + 0x53, + 0x5a, + 0x38, + 0x33, + 0x5a, + 0x6f, + 0x4e, + 0x50, + 0x68, + 0x6d, + 0x54, + 0x42, + 0x47, + 0x62, + 0x72, + 0x56, + 0x45, + 0x72, + 0x31, + 0x62, + 0x33, + 0x42, + 0x55, + 0x59, + 0x30, + 0x6c, + 0x6b, + 0x46, + 0x49, + 0x4f, + 0x6f, + 0x33, + 0x6d, + 0x66, + 0x30, + 0x32, + 0x6c, + 0x37, + 0x57, + 0x48, + 0x59, + 0x0a, + 0x78, + 0x4d, + 0x51, + 0x38, + 0x48, + 0x63, + 0x32, + 0x61, + 0x6e, + 0x55, + 0x38, + 0x71, + 0x64, + 0x4f, + 0x58, + 0x76, + 0x6f, + 0x58, + 0x62, + 0x51, + 0x34, + 0x2f, + 0x59, + 0x59, + 0x4b, + 0x31, + 0x57, + 0x42, + 0x76, + 0x4b, + 0x75, + 0x2f, + 0x43, + 0x6c, + 0x42, + 0x53, + 0x45, + 0x63, + 0x61, + 0x4f, + 0x39, + 0x30, + 0x62, + 0x71, + 0x79, + 0x44, + 0x73, + 0x37, + 0x33, + 0x6d, + 0x69, + 0x46, + 0x57, + 0x2f, + 0x43, + 0x31, + 0x54, + 0x66, + 0x78, + 0x50, + 0x58, + 0x6e, + 0x6b, + 0x53, + 0x0a, + 0x54, + 0x2b, + 0x72, + 0x4f, + 0x4d, + 0x6d, + 0x77, + 0x52, + 0x51, + 0x67, + 0x4c, + 0x46, + 0x50, + 0x6c, + 0x35, + 0x46, + 0x41, + 0x62, + 0x48, + 0x4f, + 0x6b, + 0x31, + 0x41, + 0x58, + 0x5a, + 0x45, + 0x34, + 0x46, + 0x46, + 0x47, + 0x50, + 0x75, + 0x55, + 0x62, + 0x52, + 0x74, + 0x6b, + 0x4f, + 0x45, + 0x72, + 0x64, + 0x77, + 0x34, + 0x74, + 0x5a, + 0x68, + 0x53, + 0x77, + 0x66, + 0x58, + 0x74, + 0x2f, + 0x46, + 0x41, + 0x36, + 0x2b, + 0x32, + 0x4c, + 0x6e, + 0x48, + 0x79, + 0x37, + 0x38, + 0x38, + 0x0a, + 0x58, + 0x4f, + 0x46, + 0x62, + 0x71, + 0x30, + 0x46, + 0x4b, + 0x6e, + 0x63, + 0x6e, + 0x68, + 0x32, + 0x6a, + 0x61, + 0x2b, + 0x64, + 0x37, + 0x65, + 0x4e, + 0x6c, + 0x48, + 0x77, + 0x4c, + 0x4b, + 0x63, + 0x53, + 0x43, + 0x34, + 0x43, + 0x46, + 0x4e, + 0x46, + 0x4b, + 0x51, + 0x6d, + 0x6c, + 0x45, + 0x68, + 0x5a, + 0x5a, + 0x4a, + 0x48, + 0x63, + 0x65, + 0x6e, + 0x2f, + 0x42, + 0x30, + 0x6d, + 0x7a, + 0x72, + 0x48, + 0x6c, + 0x35, + 0x76, + 0x53, + 0x6c, + 0x59, + 0x79, + 0x37, + 0x50, + 0x37, + 0x73, + 0x0a, + 0x63, + 0x32, + 0x52, + 0x34, + 0x33, + 0x54, + 0x6c, + 0x45, + 0x41, + 0x4d, + 0x77, + 0x72, + 0x32, + 0x4a, + 0x30, + 0x4c, + 0x37, + 0x58, + 0x65, + 0x34, + 0x2b, + 0x33, + 0x45, + 0x36, + 0x31, + 0x4f, + 0x64, + 0x43, + 0x4f, + 0x30, + 0x46, + 0x37, + 0x4e, + 0x46, + 0x30, + 0x76, + 0x33, + 0x66, + 0x2f, + 0x34, + 0x5a, + 0x6f, + 0x4a, + 0x49, + 0x6d, + 0x4c, + 0x74, + 0x4e, + 0x7a, + 0x4d, + 0x4f, + 0x31, + 0x68, + 0x69, + 0x61, + 0x51, + 0x34, + 0x69, + 0x67, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char server_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x7a, + 0x43, + 0x43, + 0x41, + 0x77, + 0x4f, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x44, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x77, + 0x4d, + 0x31, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x41, + 0x7a, + 0x57, + 0x6a, + 0x42, + 0x50, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x43, + 0x57, + 0x78, + 0x76, + 0x59, + 0x32, + 0x46, + 0x73, + 0x61, + 0x47, + 0x39, + 0x7a, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x0a, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4d, + 0x74, + 0x58, + 0x71, + 0x68, + 0x69, + 0x59, + 0x68, + 0x47, + 0x6a, + 0x56, + 0x4c, + 0x77, + 0x56, + 0x75, + 0x61, + 0x79, + 0x76, + 0x4d, + 0x4d, + 0x49, + 0x72, + 0x6a, + 0x4b, + 0x37, + 0x53, + 0x34, + 0x4c, + 0x62, + 0x52, + 0x48, + 0x6c, + 0x4e, + 0x55, + 0x44, + 0x0a, + 0x74, + 0x4a, + 0x7a, + 0x46, + 0x6f, + 0x32, + 0x41, + 0x6a, + 0x75, + 0x4c, + 0x75, + 0x6d, + 0x74, + 0x65, + 0x46, + 0x2b, + 0x4b, + 0x4c, + 0x73, + 0x34, + 0x56, + 0x6a, + 0x59, + 0x7a, + 0x78, + 0x75, + 0x64, + 0x61, + 0x43, + 0x51, + 0x72, + 0x79, + 0x76, + 0x4d, + 0x61, + 0x6c, + 0x30, + 0x64, + 0x2f, + 0x38, + 0x32, + 0x38, + 0x75, + 0x6e, + 0x33, + 0x78, + 0x34, + 0x67, + 0x57, + 0x6a, + 0x59, + 0x54, + 0x4f, + 0x43, + 0x35, + 0x35, + 0x63, + 0x51, + 0x39, + 0x4d, + 0x44, + 0x4e, + 0x64, + 0x2b, + 0x0a, + 0x70, + 0x6d, + 0x4d, + 0x45, + 0x45, + 0x34, + 0x2b, + 0x64, + 0x31, + 0x75, + 0x6e, + 0x54, + 0x66, + 0x6f, + 0x39, + 0x38, + 0x38, + 0x32, + 0x6a, + 0x4d, + 0x38, + 0x65, + 0x6a, + 0x6c, + 0x4e, + 0x46, + 0x54, + 0x63, + 0x44, + 0x6f, + 0x54, + 0x50, + 0x68, + 0x6f, + 0x4e, + 0x37, + 0x77, + 0x71, + 0x66, + 0x78, + 0x4f, + 0x31, + 0x35, + 0x46, + 0x6e, + 0x69, + 0x54, + 0x77, + 0x54, + 0x43, + 0x57, + 0x39, + 0x30, + 0x62, + 0x6d, + 0x7a, + 0x37, + 0x32, + 0x74, + 0x36, + 0x7a, + 0x4c, + 0x4f, + 0x6d, + 0x0a, + 0x33, + 0x43, + 0x70, + 0x5a, + 0x75, + 0x75, + 0x64, + 0x48, + 0x53, + 0x6e, + 0x58, + 0x48, + 0x71, + 0x74, + 0x67, + 0x4f, + 0x7a, + 0x36, + 0x6e, + 0x53, + 0x53, + 0x62, + 0x54, + 0x6a, + 0x4f, + 0x31, + 0x51, + 0x52, + 0x33, + 0x4c, + 0x4e, + 0x2b, + 0x44, + 0x76, + 0x4e, + 0x30, + 0x34, + 0x62, + 0x4d, + 0x58, + 0x42, + 0x66, + 0x65, + 0x2f, + 0x30, + 0x4e, + 0x56, + 0x45, + 0x2f, + 0x51, + 0x70, + 0x65, + 0x4f, + 0x4f, + 0x47, + 0x50, + 0x5a, + 0x53, + 0x47, + 0x43, + 0x4f, + 0x74, + 0x4b, + 0x76, + 0x0a, + 0x44, + 0x5a, + 0x34, + 0x4f, + 0x75, + 0x79, + 0x4b, + 0x68, + 0x6d, + 0x32, + 0x52, + 0x6f, + 0x72, + 0x47, + 0x4a, + 0x6e, + 0x4c, + 0x6d, + 0x58, + 0x78, + 0x44, + 0x46, + 0x48, + 0x6d, + 0x42, + 0x4d, + 0x70, + 0x30, + 0x30, + 0x64, + 0x41, + 0x63, + 0x65, + 0x72, + 0x42, + 0x45, + 0x52, + 0x4c, + 0x44, + 0x6b, + 0x4c, + 0x52, + 0x70, + 0x64, + 0x57, + 0x34, + 0x38, + 0x4b, + 0x32, + 0x69, + 0x35, + 0x74, + 0x42, + 0x51, + 0x76, + 0x41, + 0x63, + 0x4a, + 0x5a, + 0x7a, + 0x6c, + 0x4c, + 0x38, + 0x51, + 0x0a, + 0x2b, + 0x48, + 0x32, + 0x66, + 0x67, + 0x75, + 0x53, + 0x62, + 0x42, + 0x32, + 0x32, + 0x65, + 0x55, + 0x79, + 0x74, + 0x42, + 0x62, + 0x62, + 0x4e, + 0x6e, + 0x67, + 0x76, + 0x36, + 0x68, + 0x79, + 0x52, + 0x79, + 0x30, + 0x62, + 0x41, + 0x49, + 0x7a, + 0x61, + 0x59, + 0x48, + 0x77, + 0x61, + 0x70, + 0x52, + 0x49, + 0x78, + 0x72, + 0x4c, + 0x54, + 0x57, + 0x6e, + 0x64, + 0x4d, + 0x69, + 0x42, + 0x37, + 0x79, + 0x4f, + 0x76, + 0x64, + 0x69, + 0x73, + 0x6c, + 0x55, + 0x2b, + 0x31, + 0x53, + 0x6f, + 0x46, + 0x0a, + 0x57, + 0x41, + 0x62, + 0x66, + 0x56, + 0x35, + 0x79, + 0x75, + 0x68, + 0x77, + 0x4f, + 0x6e, + 0x77, + 0x54, + 0x4d, + 0x53, + 0x6f, + 0x64, + 0x52, + 0x30, + 0x39, + 0x4f, + 0x78, + 0x67, + 0x55, + 0x61, + 0x75, + 0x41, + 0x32, + 0x79, + 0x72, + 0x41, + 0x4c, + 0x32, + 0x51, + 0x37, + 0x42, + 0x4d, + 0x35, + 0x47, + 0x6e, + 0x59, + 0x4c, + 0x70, + 0x55, + 0x6f, + 0x35, + 0x71, + 0x35, + 0x46, + 0x67, + 0x41, + 0x77, + 0x6b, + 0x73, + 0x4a, + 0x6c, + 0x38, + 0x77, + 0x79, + 0x53, + 0x44, + 0x70, + 0x59, + 0x0a, + 0x63, + 0x51, + 0x72, + 0x37, + 0x46, + 0x72, + 0x7a, + 0x53, + 0x6a, + 0x71, + 0x63, + 0x31, + 0x6f, + 0x39, + 0x55, + 0x73, + 0x37, + 0x68, + 0x45, + 0x45, + 0x6c, + 0x6b, + 0x76, + 0x4e, + 0x42, + 0x5a, + 0x6b, + 0x31, + 0x2b, + 0x56, + 0x50, + 0x54, + 0x61, + 0x50, + 0x35, + 0x78, + 0x45, + 0x59, + 0x79, + 0x70, + 0x63, + 0x62, + 0x67, + 0x2b, + 0x75, + 0x2b, + 0x59, + 0x43, + 0x7a, + 0x63, + 0x74, + 0x4a, + 0x57, + 0x6e, + 0x4a, + 0x4b, + 0x62, + 0x43, + 0x42, + 0x7a, + 0x32, + 0x65, + 0x73, + 0x36, + 0x0a, + 0x4b, + 0x6b, + 0x70, + 0x66, + 0x43, + 0x34, + 0x61, + 0x57, + 0x63, + 0x38, + 0x55, + 0x49, + 0x4c, + 0x6c, + 0x55, + 0x4b, + 0x2f, + 0x57, + 0x34, + 0x6a, + 0x52, + 0x46, + 0x73, + 0x4e, + 0x47, + 0x33, + 0x55, + 0x68, + 0x41, + 0x31, + 0x76, + 0x6e, + 0x49, + 0x5a, + 0x77, + 0x4b, + 0x38, + 0x36, + 0x69, + 0x71, + 0x6d, + 0x33, + 0x6b, + 0x2b, + 0x50, + 0x45, + 0x62, + 0x61, + 0x68, + 0x63, + 0x45, + 0x78, + 0x41, + 0x34, + 0x71, + 0x42, + 0x2f, + 0x4c, + 0x42, + 0x68, + 0x6f, + 0x67, + 0x76, + 0x73, + 0x0a, + 0x34, + 0x7a, + 0x39, + 0x62, + 0x32, + 0x4d, + 0x51, + 0x33, + 0x35, + 0x74, + 0x69, + 0x49, + 0x6f, + 0x51, + 0x44, + 0x6a, + 0x37, + 0x52, + 0x35, + 0x78, + 0x4e, + 0x38, + 0x63, + 0x35, + 0x41, + 0x79, + 0x77, + 0x44, + 0x79, + 0x62, + 0x38, + 0x78, + 0x47, + 0x76, + 0x32, + 0x59, + 0x6d, + 0x6b, + 0x34, + 0x6a, + 0x47, + 0x74, + 0x44, + 0x6b, + 0x64, + 0x71, + 0x72, + 0x62, + 0x74, + 0x49, + 0x55, + 0x34, + 0x32, + 0x2b, + 0x48, + 0x4d, + 0x31, + 0x79, + 0x4f, + 0x6b, + 0x38, + 0x75, + 0x38, + 0x5a, + 0x0a, + 0x45, + 0x69, + 0x78, + 0x4c, + 0x51, + 0x7a, + 0x6f, + 0x77, + 0x6d, + 0x71, + 0x55, + 0x71, + 0x72, + 0x65, + 0x42, + 0x43, + 0x68, + 0x6f, + 0x41, + 0x6f, + 0x73, + 0x63, + 0x33, + 0x46, + 0x4f, + 0x52, + 0x4f, + 0x6d, + 0x54, + 0x43, + 0x44, + 0x6c, + 0x59, + 0x36, + 0x33, + 0x34, + 0x2f, + 0x61, + 0x63, + 0x49, + 0x49, + 0x50, + 0x33, + 0x31, + 0x64, + 0x65, + 0x64, + 0x48, + 0x4e, + 0x59, + 0x5a, + 0x35, + 0x31, + 0x52, + 0x72, + 0x75, + 0x6b, + 0x41, + 0x64, + 0x41, + 0x36, + 0x32, + 0x44, + 0x62, + 0x0a, + 0x47, + 0x44, + 0x77, + 0x67, + 0x77, + 0x7a, + 0x36, + 0x54, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4b, + 0x56, + 0x76, + 0x2f, + 0x53, + 0x43, + 0x50, + 0x4f, + 0x67, + 0x77, + 0x50, + 0x41, + 0x59, + 0x30, + 0x47, + 0x34, + 0x37, + 0x79, + 0x63, + 0x76, + 0x55, + 0x48, + 0x67, + 0x0a, + 0x6b, + 0x33, + 0x44, + 0x51, + 0x67, + 0x6d, + 0x62, + 0x78, + 0x56, + 0x54, + 0x46, + 0x46, + 0x57, + 0x51, + 0x79, + 0x72, + 0x50, + 0x36, + 0x58, + 0x76, + 0x4b, + 0x48, + 0x4f, + 0x6c, + 0x33, + 0x65, + 0x5a, + 0x55, + 0x32, + 0x52, + 0x65, + 0x4a, + 0x78, + 0x65, + 0x54, + 0x78, + 0x43, + 0x4d, + 0x55, + 0x4b, + 0x63, + 0x4e, + 0x6c, + 0x57, + 0x34, + 0x2b, + 0x65, + 0x4d, + 0x32, + 0x6f, + 0x43, + 0x5a, + 0x37, + 0x57, + 0x31, + 0x76, + 0x69, + 0x74, + 0x61, + 0x75, + 0x70, + 0x6e, + 0x4f, + 0x30, + 0x0a, + 0x7a, + 0x4c, + 0x57, + 0x36, + 0x47, + 0x42, + 0x46, + 0x53, + 0x56, + 0x58, + 0x55, + 0x34, + 0x54, + 0x2b, + 0x59, + 0x5a, + 0x63, + 0x69, + 0x49, + 0x48, + 0x6e, + 0x78, + 0x6b, + 0x71, + 0x6c, + 0x48, + 0x33, + 0x58, + 0x78, + 0x63, + 0x6b, + 0x67, + 0x7a, + 0x4e, + 0x56, + 0x4c, + 0x79, + 0x57, + 0x44, + 0x41, + 0x41, + 0x4a, + 0x6f, + 0x61, + 0x77, + 0x32, + 0x63, + 0x2b, + 0x58, + 0x31, + 0x45, + 0x39, + 0x42, + 0x7a, + 0x59, + 0x62, + 0x49, + 0x54, + 0x2b, + 0x7a, + 0x4d, + 0x59, + 0x6a, + 0x5a, + 0x0a, + 0x31, + 0x62, + 0x59, + 0x41, + 0x61, + 0x76, + 0x79, + 0x51, + 0x4f, + 0x6f, + 0x4d, + 0x50, + 0x64, + 0x2f, + 0x31, + 0x7a, + 0x30, + 0x64, + 0x59, + 0x6d, + 0x49, + 0x39, + 0x46, + 0x72, + 0x77, + 0x78, + 0x5a, + 0x32, + 0x4f, + 0x42, + 0x6e, + 0x76, + 0x2b, + 0x63, + 0x58, + 0x41, + 0x72, + 0x69, + 0x30, + 0x2f, + 0x4a, + 0x4d, + 0x47, + 0x54, + 0x63, + 0x7a, + 0x58, + 0x36, + 0x66, + 0x56, + 0x67, + 0x53, + 0x45, + 0x50, + 0x47, + 0x66, + 0x48, + 0x48, + 0x63, + 0x6f, + 0x36, + 0x53, + 0x4b, + 0x4a, + 0x0a, + 0x52, + 0x5a, + 0x57, + 0x38, + 0x56, + 0x49, + 0x39, + 0x67, + 0x49, + 0x4f, + 0x52, + 0x75, + 0x57, + 0x68, + 0x79, + 0x39, + 0x76, + 0x78, + 0x55, + 0x57, + 0x41, + 0x43, + 0x4a, + 0x6f, + 0x30, + 0x4f, + 0x6b, + 0x4a, + 0x44, + 0x75, + 0x55, + 0x37, + 0x2b, + 0x57, + 0x54, + 0x61, + 0x39, + 0x65, + 0x44, + 0x66, + 0x6a, + 0x46, + 0x2b, + 0x62, + 0x62, + 0x68, + 0x2b, + 0x2f, + 0x49, + 0x73, + 0x38, + 0x36, + 0x66, + 0x72, + 0x4c, + 0x32, + 0x67, + 0x76, + 0x38, + 0x79, + 0x39, + 0x69, + 0x4d, + 0x55, + 0x0a, + 0x74, + 0x32, + 0x36, + 0x37, + 0x75, + 0x58, + 0x39, + 0x52, + 0x52, + 0x64, + 0x30, + 0x46, + 0x70, + 0x69, + 0x4b, + 0x70, + 0x2b, + 0x70, + 0x4b, + 0x4d, + 0x48, + 0x6d, + 0x50, + 0x77, + 0x47, + 0x33, + 0x70, + 0x75, + 0x6b, + 0x78, + 0x57, + 0x64, + 0x38, + 0x76, + 0x6b, + 0x4f, + 0x72, + 0x67, + 0x6a, + 0x4a, + 0x44, + 0x46, + 0x2b, + 0x54, + 0x56, + 0x48, + 0x67, + 0x67, + 0x4c, + 0x31, + 0x4a, + 0x59, + 0x58, + 0x5a, + 0x37, + 0x45, + 0x4a, + 0x39, + 0x79, + 0x6a, + 0x50, + 0x64, + 0x77, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +#ifndef BSC_NETWORK_IFACE +#define BSC_NETWORK_IFACE "127.0.0.1" +#endif + +#define BACNET_WEBSOCKET_SERVER_PORT 39000 +#define BACNET_WEBSOCKET_SERVER_PORT2 39001 +#ifdef ZEPHYR_TEST +#define BACNET_WEBSOCKET_SERVER_ADDR CONFIG_NET_CONFIG_MY_IPV4_ADDR +#else +#define BACNET_WEBSOCKET_SERVER_ADDR "127.0.0.1" +#endif +#define BACNET_TIMEOUT 3 +#define MAX_BVLC_LEN 1500 +#define MAX_NDPU_LEN 1500 +#define MAX_SERVER_SOCKETS BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM +#define MAX_CLIENT_SOCKETS BSC_CONF_CLIENT_CONNECTIONS_NUM +#define WAIT_EVENT_MS 500 + +// MbedTLS expects a key in DER format +#ifdef CONFIG_MBEDTLS +#define CLIENT_KEY client_key_der +#else +#define CLIENT_KEY client_key +#endif + +typedef struct { + BSC_HUB_CONNECTOR_EVENT ev; + BSC_HUB_CONNECTOR_HANDLE h; + void *user_arg; + uint8_t pdu[MAX_BVLC_LEN]; + size_t pdu_len; + BSC_EVENT *e; +} hubc_ev_t; + +typedef struct { + BSC_HUB_FUNCTION_EVENT ev; + BSC_HUB_FUNCTION_HANDLE h; + void *user_arg; + BSC_EVENT *e; +} hubf_ev_t; + +static void call_maintenance_timer(bool reset, int time_passed_ms) +{ + static int total_ms; + if (reset) { + total_ms = 0; + } + + total_ms += time_passed_ms; + + if (total_ms >= 1000) { + bsc_hub_connector_maintenance_timer(1); + bsc_socket_maintenance_timer(1); + total_ms = 0; + } +} + +static void wait_sec(int seconds) +{ + while (seconds >= 0) { + bsc_wait(1); + bsc_hub_connector_maintenance_timer(1); + bsc_socket_maintenance_timer(1); + seconds--; + } +} + +static hubc_ev_t hubc; +static hubf_ev_t hubf; + +static void init_hubc_ev(hubc_ev_t *ev) +{ + memset(ev, 0, sizeof(*ev)); + ev->e = bsc_event_init(); + zassert_not_equal(ev->e, NULL, 0); + ev->ev = -1; +} + +static void deinit_hubc_ev(hubc_ev_t *ev) +{ + bsc_event_deinit(ev->e); +} + +static bool wait_hubc_ev( + hubc_ev_t *ev, + BSC_HUB_CONNECTOR_EVENT wait_ev, + BSC_HUB_CONNECTOR_HANDLE wait_h) +{ + call_maintenance_timer(1, 0); + while (!bsc_event_timedwait(ev->e, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + bws_dispatch_lock(); + if (ev->ev == wait_ev && ev->h == wait_h) { + ev->ev = -1; + // reset event if it was signalled while we were blocked in call + // bws_dispatch_lock(). + // (in that case ev->ev contains code of last event.) + // that's tricky but that allows to avoid using mutexes which are + // platform specific in test code + bsc_event_timedwait(ev->e, 1); + bws_dispatch_unlock(); + return true; + } else { + bws_dispatch_unlock(); + return false; + } +} + +static void signal_hubc_ev( + hubc_ev_t *e, + BSC_HUB_CONNECTOR_EVENT ev, + BSC_HUB_CONNECTOR_HANDLE h, + void *user_arg, + uint8_t *pdu, + size_t pdu_len) +{ + e->ev = ev; + e->h = h; + e->user_arg = user_arg; + + zassert_equal(true, pdu_len <= MAX_BVLC_LEN, NULL); + + if (pdu) { + memcpy(e->pdu, pdu, pdu_len); + e->pdu_len = pdu_len; + } + bsc_event_signal(e->e); +} + +static void init_hubf_ev(hubf_ev_t *ev) +{ + memset(ev, 0, sizeof(*ev)); + ev->e = bsc_event_init(); + zassert_not_equal(ev->e, NULL, 0); + ev->ev = -1; +} + +static void deinit_hubf_ev(hubf_ev_t *ev) +{ + bsc_event_deinit(ev->e); +} + +static bool wait_hubf_ev( + hubf_ev_t *ev, + BSC_HUB_FUNCTION_EVENT wait_ev, + BSC_HUB_FUNCTION_HANDLE wait_h) +{ + call_maintenance_timer(1, 0); + while (!bsc_event_timedwait(ev->e, WAIT_EVENT_MS)) { + call_maintenance_timer(0, WAIT_EVENT_MS); + } + + bws_dispatch_lock(); + if (ev->ev == wait_ev && ev->h == wait_h) { + ev->ev = -1; + // reset event if it was signalled while we were blocked in call + // bws_dispatch_lock(). + // (in that case ev->ev contains code of last event.) + // that's tricky but that allows to avoid using mutexes which are + // platform specific in test code + bsc_event_timedwait(ev->e, 1); + bws_dispatch_unlock(); + return true; + } else { + bws_dispatch_unlock(); + return false; + } +} + +static void signal_hubf_ev( + hubf_ev_t *e, + BSC_HUB_FUNCTION_EVENT ev, + BSC_HUB_CONNECTOR_HANDLE h, + void *user_arg) +{ + e->ev = ev; + e->h = h; + e->user_arg = user_arg; + bsc_event_signal(e->e); +} + +static void hub_connector_event( + BSC_HUB_CONNECTOR_EVENT ev, + BSC_HUB_CONNECTOR_HANDLE h, + void *user_arg, + uint8_t *pdu, + size_t pdu_len, + BVLC_SC_DECODED_MESSAGE *decoded_pdu) +{ + (void)decoded_pdu; + bws_dispatch_lock(); + debug_printf( + "hub_connector_event() ev = %p, ev->e = %p, ev->ev = %d, h = " + "%p, user_arg = %p, pdu = " + "%p, pdu_len = %d\n", + &hubc, hubc.e, ev, h, user_arg, pdu, pdu_len); + signal_hubc_ev(&hubc, ev, h, user_arg, pdu, pdu_len); + bws_dispatch_unlock(); +} + +static void hub_function_event( + BSC_HUB_FUNCTION_EVENT ev, BSC_HUB_FUNCTION_HANDLE h, void *user_arg) +{ + bws_dispatch_lock(); + debug_printf( + "hub_function_event() ev = %p, ev->e = %p, ev->ev = %d, h = " + "%p, user_arg = %p\n", + &hubf, hubf.e, ev, h, user_arg); + signal_hubf_ev(&hubf, ev, h, user_arg); + bws_dispatch_unlock(); +} + +static void test_hub_connector_url(bool primary) +{ + BSC_SC_RET ret; + BACNET_SC_UUID hubf_uuid; + BACNET_SC_VMAC_ADDRESS hubf_vmac; + BSC_HUB_FUNCTION_HANDLE hubf_h; + BACNET_SC_UUID hubc_uuid; + BACNET_SC_VMAC_ADDRESS hubc_vmac; + BSC_HUB_FUNCTION_HANDLE hubc_h; + BACNET_SC_UUID hubc_uuid2; + BACNET_SC_VMAC_ADDRESS hubc_vmac2; + BSC_HUB_FUNCTION_HANDLE hubc_h2; + char primary_url[128]; + char secondary_url[128]; + uint8_t buf[256]; + size_t len; + uint8_t npdu[128]; + BVLC_SC_DECODED_MESSAGE message; + BACNET_ERROR_CODE error; + BACNET_ERROR_CLASS class; + const char *err_desc; + BACNET_SC_VMAC_ADDRESS broadcast_vmac; + BACNET_SC_VMAC_ADDRESS non_existent_vmac; + + memset(&broadcast_vmac, 0xff, sizeof(broadcast_vmac)); + memset(&hubf_uuid, 0x1, sizeof(hubf_uuid)); + memset(&hubf_vmac, 0x2, sizeof(hubf_vmac)); + memset(&hubc_uuid, 0x3, sizeof(hubc_uuid)); + memset(&hubc_vmac, 0x4, sizeof(hubc_vmac)); + memset(&hubc_uuid2, 0x5, sizeof(hubc_uuid2)); + memset(&hubc_vmac2, 0x6, sizeof(hubc_vmac2)); + memset(npdu, 0x11, sizeof(npdu)); + + if (primary) { + sprintf( + primary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT2); + } else { + sprintf( + primary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT2); + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + } + + init_hubc_ev(&hubc); + init_hubf_ev(&hubf); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + zassert_equal(bsc_hub_function_started(hubf_h), true, 0); + zassert_equal(bsc_hub_function_stopped(hubf_h), false, 0); + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + if (primary) { + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_PRIMARY, hubc_h), true, + 0); + } else { + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_FAILOVER, hubc_h), + true, 0); + } + zassert_equal(bsc_hub_connector_stopped(hubc_h), false, 0); + if (primary) { + zassert_equal( + bsc_hub_connector_state(hubc_h), + BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY, 0); + } else { + zassert_equal( + bsc_hub_connector_state(hubc_h), + BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER, 0); + } + + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid2, &hubc_vmac2, MAX_BVLC_LEN, + MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid2, &hubc_h2); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + if (primary) { + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_PRIMARY, hubc_h2), + true, 0); + } else { + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_FAILOVER, hubc_h2), + true, 0); + } + memset(&non_existent_vmac, 0x22, sizeof(non_existent_vmac)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 666, NULL, &non_existent_vmac, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_hub_connector_send(hubc_h2, buf, len); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 111, NULL, &hubc_vmac, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_hub_connector_send(hubc_h2, buf, len); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_RECEIVED, hubc_h), true, 0); + ret = bvlc_sc_decode_message( + hubc.pdu, hubc.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + // now send broadcast message + memset(npdu, 0x22, sizeof(npdu)); + len = bvlc_sc_encode_encapsulated_npdu( + buf, sizeof(buf), 222, NULL, &broadcast_vmac, npdu, sizeof(npdu)); + zassert_equal(len > 0, true, NULL); + ret = bsc_hub_connector_send(hubc_h2, buf, len); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_RECEIVED, hubc_h), true, 0); + ret = bvlc_sc_decode_message( + hubc.pdu, hubc.pdu_len, &message, &error, &class, &err_desc); + zassert_equal(ret, true, NULL); + zassert_equal( + sizeof(npdu), message.payload.encapsulated_npdu.npdu_len, NULL); + ret = memcmp(npdu, message.payload.encapsulated_npdu.npdu, sizeof(npdu)); + zassert_equal(ret, 0, NULL); + zassert_equal(bsc_hub_connector_stopped(hubc_h), false, 0); + if (primary) { + zassert_equal( + bsc_hub_connector_state(hubc_h), + BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_PRIMARY, 0); + } else { + zassert_equal( + bsc_hub_connector_state(hubc_h), + BACNET_SC_HUB_CONNECTOR_STATE_CONNECTED_TO_FAILOVER, 0); + } + bsc_hub_connector_stop(hubc_h); + zassert_equal(wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h), true, 0); + bsc_hub_connector_stop(hubc_h2); + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h2), true, 0); + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + zassert_equal(bsc_hub_function_started(hubf_h), false, 0); + zassert_equal(bsc_hub_function_stopped(hubf_h), true, 0); + zassert_equal(bsc_hub_connector_stopped(hubc_h), true, 0); + deinit_hubc_ev(&hubc); + deinit_hubf_ev(&hubf); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(hub_test_3, test_hub_connector_bad_primary_url) +#else +static void test_hub_connector_bad_primary_url(void) +#endif +{ + BSC_SC_RET ret; + BACNET_SC_UUID hubf_uuid; + BACNET_SC_VMAC_ADDRESS hubf_vmac; + BSC_HUB_FUNCTION_HANDLE hubf_h; + BACNET_SC_UUID hubc_uuid; + BACNET_SC_VMAC_ADDRESS hubc_vmac; + BSC_HUB_FUNCTION_HANDLE hubc_h; + char primary_url[128]; + char secondary_url[128]; + + memset(&hubf_uuid, 0x1, sizeof(hubf_uuid)); + memset(&hubf_vmac, 0x2, sizeof(hubf_vmac)); + memset(&hubc_uuid, 0x3, sizeof(hubc_uuid)); + memset(&hubc_vmac, 0x4, sizeof(hubc_vmac)); + + sprintf( + primary_url, "wsssdsdfsdf://sdsdf%s:sdfsdf%d", + BACNET_WEBSOCKET_SERVER_ADDR, BACNET_WEBSOCKET_SERVER_PORT); + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + init_hubc_ev(&hubc); + init_hubf_ev(&hubf); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_FAILOVER, hubc_h), true, + 0); + + bsc_hub_connector_stop(hubc_h); + zassert_equal(wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h), true, 0); + + /* now all same but without secondary url */ + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, NULL, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_BAD_PARAM, NULL); + + /* stop hub */ + + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + deinit_hubc_ev(&hubc); + deinit_hubf_ev(&hubf); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(hub_test_4, test_hub_bad_params) +#else +static void test_hub_bad_params(void) +#endif +{ + BSC_SC_RET ret; + BACNET_SC_UUID hubc_uuid; + BACNET_SC_VMAC_ADDRESS hubc_vmac; + BACNET_SC_UUID hubc_uuid2; + BACNET_SC_VMAC_ADDRESS hubc_vmac2; + BSC_HUB_FUNCTION_HANDLE hubc_h; + BSC_HUB_FUNCTION_HANDLE hubc_h2; + BSC_HUB_FUNCTION_HANDLE hubc_h3; + char primary_url[2 * BSC_WSURL_MAX_LEN]; + char secondary_url[2 * BSC_WSURL_MAX_LEN]; + + memset(&hubc_uuid, 0x3, sizeof(hubc_uuid)); + memset(&hubc_vmac, 0x4, sizeof(hubc_vmac)); + memset(&hubc_uuid2, 0x5, sizeof(hubc_uuid)); + memset(&hubc_vmac2, 0x6, sizeof(hubc_vmac)); + sprintf( + primary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT2); + + init_hubc_ev(&hubc); + + ret = bsc_hub_connector_start( + NULL, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + + ret = bsc_hub_connector_start( + NULL, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + + memset(primary_url, 0x22, sizeof(primary_url)); + primary_url[2 * BSC_WSURL_MAX_LEN - 1] = 0; + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + + sprintf( + primary_url, "wsssdsdfsdf://sdsdf%s:sdfsdf%d", + BACNET_WEBSOCKET_SERVER_ADDR, BACNET_WEBSOCKET_SERVER_PORT); + sprintf( + secondary_url, "wss23413r234://sdfsdfsdf%s:r234%2d", + BACNET_WEBSOCKET_SERVER_ADDR, BACNET_WEBSOCKET_SERVER_PORT2); + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + + sprintf( + primary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT2); + + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_vmac, &hubc_h); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac2, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_vmac2, &hubc_h2); + zassert_equal(ret, BSC_SC_SUCCESS, 0); + + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_h3, &hubc_h3); + zassert_equal(ret, BSC_SC_NO_RESOURCES, 0); + + bsc_hub_connector_stop(hubc_h); + zassert_equal(wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h), true, 0); + + bsc_hub_connector_stop(hubc_h2); + ret = bsc_hub_connector_send( + hubc_h, (uint8_t *)&hubc_uuid, sizeof(hubc_uuid)); + zassert_equal(ret, BSC_SC_INVALID_OPERATION, 0); + ret = + bsc_hub_connector_send(NULL, (uint8_t *)&hubc_uuid, sizeof(hubc_uuid)); + zassert_equal(ret, BSC_SC_BAD_PARAM, 0); + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h2), true, 0); + deinit_hubc_ev(&hubc); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(hub_test_1, test_hub_connector_primary_url) +#else +static void test_hub_connector_primary_url(void) +#endif +{ + test_hub_connector_url(true); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(hub_test_2, test_hub_connector_secondary_url) +#else +static void test_hub_connector_secondary_url(void) +#endif +{ + test_hub_connector_url(false); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(hub_test_5, test_hub_connector_reconnect) +#else +static void test_hub_connector_reconnect(void) +#endif +{ + BSC_SC_RET ret; + BACNET_SC_UUID hubf_uuid; + BACNET_SC_VMAC_ADDRESS hubf_vmac; + BSC_HUB_FUNCTION_HANDLE hubf_h; + BACNET_SC_UUID hubf_uuid2; + BACNET_SC_VMAC_ADDRESS hubf_vmac2; + BSC_HUB_FUNCTION_HANDLE hubf_h2; + BACNET_SC_UUID hubc_uuid; + BACNET_SC_VMAC_ADDRESS hubc_vmac; + BSC_HUB_FUNCTION_HANDLE hubc_h; + char primary_url[128]; + char secondary_url[128]; + + memset(&hubf_uuid, 0x1, sizeof(hubf_uuid)); + memset(&hubf_vmac, 0x2, sizeof(hubf_vmac)); + memset(&hubf_uuid2, 0x3, sizeof(hubf_uuid2)); + memset(&hubf_vmac2, 0x4, sizeof(hubf_vmac2)); + memset(&hubc_uuid, 0x5, sizeof(hubc_uuid)); + memset(&hubc_vmac, 0x6, sizeof(hubc_vmac)); + + sprintf( + primary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT2); + + init_hubc_ev(&hubc); + init_hubf_ev(&hubf); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT2, BSC_NETWORK_IFACE, + &hubf_uuid2, &hubf_vmac2, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h2); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal( + wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h2), true, 0); + + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_PRIMARY, hubc_h), true, 0); + + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_FAILOVER, hubc_h), true, + 0); + + bsc_hub_function_stop(hubf_h2); + zassert_equal( + wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h2), true, 0); + + wait_sec(BACNET_TIMEOUT); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_PRIMARY, hubc_h), true, 0); + + bsc_hub_connector_stop(hubc_h); + zassert_equal(wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h), true, 0); + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, NULL, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_PRIMARY, hubc_h), true, 0); + + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + wait_sec(BACNET_TIMEOUT); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_PRIMARY, hubc_h), true, 0); + + bsc_hub_connector_stop(hubc_h); + zassert_equal(wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h), true, 0); + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + + deinit_hubc_ev(&hubc); + deinit_hubf_ev(&hubf); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(hub_test_6, test_hub_connector_duplicated_vmac) +#else +static void test_hub_connector_duplicated_vmac(void) +#endif +{ + BSC_SC_RET ret; + BACNET_SC_UUID hubf_uuid; + BACNET_SC_VMAC_ADDRESS hubf_vmac; + BSC_HUB_FUNCTION_HANDLE hubf_h; + BACNET_SC_UUID hubc_uuid; + BACNET_SC_VMAC_ADDRESS hubc_vmac; + BSC_HUB_FUNCTION_HANDLE hubc_h; + char primary_url[128]; + char secondary_url[128]; + + memset(&hubf_uuid, 0x1, sizeof(hubf_uuid)); + memset(&hubf_vmac, 0x2, sizeof(hubf_vmac)); + memset(&hubc_uuid, 0x3, sizeof(hubc_uuid)); + memset(&hubc_vmac, 0x2, sizeof(hubc_vmac)); + + sprintf( + primary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT2); + + init_hubc_ev(&hubc); + init_hubf_ev(&hubf); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_ERROR_DUPLICATED_VMAC, hubc_h), true, + 0); + + zassert_equal( + wait_hubf_ev(&hubf, BSC_HUBF_EVENT_ERROR_DUPLICATED_VMAC, hubf_h), true, + 0); + + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + bsc_hub_connector_stop(hubc_h); + zassert_equal(wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h), true, 0); + deinit_hubc_ev(&hubc); + deinit_hubf_ev(&hubf); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(hub_test_7, test_hub_function_bad_params) +#else +static void test_hub_function_bad_params(void) +#endif +{ + BSC_SC_RET ret; + BACNET_SC_UUID hubf_uuid; + BACNET_SC_VMAC_ADDRESS hubf_vmac; + BSC_HUB_FUNCTION_HANDLE hubf_h; + BACNET_SC_UUID hubf_uuid2; + BACNET_SC_VMAC_ADDRESS hubf_vmac2; + BSC_HUB_FUNCTION_HANDLE hubf_h2; + BACNET_SC_UUID hubf_uuid3; + BACNET_SC_VMAC_ADDRESS hubf_vmac3; + BSC_HUB_FUNCTION_HANDLE hubf_h3; + + memset(&hubf_uuid, 0x1, sizeof(hubf_uuid)); + memset(&hubf_vmac, 0x2, sizeof(hubf_vmac)); + memset(&hubf_uuid2, 0x3, sizeof(hubf_uuid2)); + memset(&hubf_vmac2, 0x4, sizeof(hubf_vmac2)); + memset(&hubf_uuid3, 0x5, sizeof(hubf_uuid3)); + memset(&hubf_vmac3, 0x6, sizeof(hubf_vmac3)); + + init_hubf_ev(&hubf); + + ret = bsc_hub_function_start( + NULL, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_BAD_PARAM, NULL); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT + 1, BSC_NETWORK_IFACE, + &hubf_uuid2, &hubf_vmac2, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h2); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal( + wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h2), true, 0); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT + 2, BSC_NETWORK_IFACE, + &hubf_uuid3, &hubf_vmac3, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h3); + zassert_equal(ret, BSC_SC_NO_RESOURCES, NULL); + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + bsc_hub_function_stop(hubf_h2); + zassert_equal( + wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h2), true, 0); + deinit_hubf_ev(&hubf); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(hub_test_8, test_hub_function_duplicated_uuid) +#else +static void test_hub_function_duplicated_uuid(void) +#endif +{ + BSC_SC_RET ret; + BACNET_SC_UUID hubf_uuid; + BACNET_SC_VMAC_ADDRESS hubf_vmac; + BSC_HUB_FUNCTION_HANDLE hubf_h; + BACNET_SC_UUID hubf_uuid2; + BACNET_SC_VMAC_ADDRESS hubf_vmac2; + BSC_HUB_FUNCTION_HANDLE hubf_h2; + BACNET_SC_UUID hubc_uuid; + BACNET_SC_VMAC_ADDRESS hubc_vmac; + BSC_HUB_FUNCTION_HANDLE hubc_h; + BACNET_SC_UUID hubc_uuid2; + BACNET_SC_VMAC_ADDRESS hubc_vmac2; + BSC_HUB_FUNCTION_HANDLE hubc_h2; + char primary_url[128]; + char secondary_url[128]; + + memset(&hubf_uuid, 0x1, sizeof(hubf_uuid)); + memset(&hubf_vmac, 0x2, sizeof(hubf_vmac)); + memset(&hubf_uuid2, 0x10, sizeof(hubf_uuid2)); + memset(&hubf_vmac2, 0x20, sizeof(hubf_vmac2)); + memset(&hubc_uuid, 0x3, sizeof(hubc_uuid)); + memset(&hubc_vmac, 0x4, sizeof(hubc_vmac)); + memset(&hubc_uuid2, 0x3, sizeof(hubc_uuid2)); + memset(&hubc_vmac2, 0x6, sizeof(hubc_vmac2)); + + sprintf( + primary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + sprintf( + secondary_url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT2); + + init_hubc_ev(&hubc); + init_hubf_ev(&hubf); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT, BSC_NETWORK_IFACE, + &hubf_uuid, &hubf_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h), true, 0); + + ret = bsc_hub_function_start( + ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), BACNET_WEBSOCKET_SERVER_PORT2, BSC_NETWORK_IFACE, + &hubf_uuid2, &hubf_vmac2, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + hub_function_event, NULL, &hubf_h2); + + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal( + wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STARTED, hubf_h2), true, 0); + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid, &hubc_vmac, MAX_BVLC_LEN, MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid, &hubc_h); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_PRIMARY, hubc_h), true, 0); + + ret = bsc_hub_connector_start( + ca_cert, sizeof(ca_cert), client_cert, sizeof(client_cert), CLIENT_KEY, + sizeof(CLIENT_KEY), &hubc_uuid2, &hubc_vmac2, MAX_BVLC_LEN, + MAX_NDPU_LEN, + BACNET_TIMEOUT, // connect timeout + BACNET_TIMEOUT, // heartbeat timeout + BACNET_TIMEOUT, // disconnect timeout + primary_url, secondary_url, + BACNET_TIMEOUT, // reconnect timeout + hub_connector_event, &hubc_uuid2, &hubc_h2); + zassert_equal(ret, BSC_SC_SUCCESS, NULL); + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_CONNECTED_PRIMARY, hubc_h2), true, + 0); + bsc_hub_connector_stop(hubc_h); + zassert_equal(wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h), true, 0); + bsc_hub_connector_stop(hubc_h2); + zassert_equal( + wait_hubc_ev(&hubc, BSC_HUBC_EVENT_STOPPED, hubc_h2), true, 0); + bsc_hub_function_stop(hubf_h); + zassert_equal(wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h), true, 0); + bsc_hub_function_stop(hubf_h2); + zassert_equal( + wait_hubf_ev(&hubf, BSC_HUBF_EVENT_STOPPED, hubf_h2), true, 0); + deinit_hubc_ev(&hubc); + deinit_hubf_ev(&hubf); +} + +#if defined(CONFIG_ZTEST_NEW_API) +static void *suite_setup(void) +{ + setbuf(stdout, NULL); + return NULL; +} + +ZTEST_SUITE(hub_test_1, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(hub_test_2, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(hub_test_3, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(hub_test_4, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(hub_test_5, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(hub_test_6, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(hub_test_7, NULL, suite_setup, NULL, NULL, NULL); +ZTEST_SUITE(hub_test_8, NULL, suite_setup, NULL, NULL, NULL); +#else +void test_main(void) +{ + // setbuf(stdout, NULL); + // Tests must not be run in parallel threads! + // Thats why tests functions are in different suites. + ztest_test_suite( + hub_test_1, ztest_unit_test(test_hub_connector_primary_url)); + ztest_test_suite( + hub_test_2, ztest_unit_test(test_hub_connector_secondary_url)); + ztest_test_suite( + hub_test_3, ztest_unit_test(test_hub_connector_bad_primary_url)); + ztest_test_suite(hub_test_4, ztest_unit_test(test_hub_bad_params)); + ztest_test_suite(hub_test_5, ztest_unit_test(test_hub_connector_reconnect)); + ztest_test_suite( + hub_test_6, ztest_unit_test(test_hub_connector_duplicated_vmac)); + ztest_test_suite(hub_test_7, ztest_unit_test(test_hub_function_bad_params)); + ztest_test_suite( + hub_test_8, ztest_unit_test(test_hub_function_duplicated_uuid)); + + ztest_run_test_suite(hub_test_1); + ztest_run_test_suite(hub_test_2); + ztest_run_test_suite(hub_test_3); + ztest_run_test_suite(hub_test_4); + ztest_run_test_suite(hub_test_5); + ztest_run_test_suite(hub_test_6); + ztest_run_test_suite(hub_test_7); + ztest_run_test_suite(hub_test_8); +} +#endif diff --git a/test/bacnet/datalink/mock/src/bsc-mock.c b/test/bacnet/datalink/mock/src/bsc-mock.c new file mode 100644 index 00000000..08483b35 --- /dev/null +++ b/test/bacnet/datalink/mock/src/bsc-mock.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +#include +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include +#include +#include +#include +#include "bacnet/datalink/bsc/bsc-datalink.h" + +bool bsc_init(char *ifname) +{ + ztest_check_expected_value(ifname); + return ztest_get_return_value(); +} + +void bsc_cleanup(void) +{ +} + +void bsc_get_my_address(BACNET_ADDRESS *my_address) +{ + ztest_copy_return_data(my_address, sizeof(BACNET_ADDRESS)); +} + +int bsc_send_pdu( + BACNET_ADDRESS *dest, + BACNET_NPDU_DATA *npdu_data, + uint8_t *pdu, + unsigned pdu_len) +{ + ztest_check_expected_value(dest); + ztest_check_expected_value(npdu_data); + ztest_check_expected_data(pdu, pdu_len); + return ztest_get_return_value(); +} + +uint16_t bsc_receive( + BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout) +{ + ztest_check_expected_value(src); + ztest_check_expected_value(timeout); + ztest_copy_return_data(pdu, max_pdu); + return ztest_get_return_value(); +} + +void bsc_get_broadcast_address(BACNET_ADDRESS *dest) +{ + ztest_copy_return_data(dest, sizeof(BACNET_ADDRESS)); +} diff --git a/test/bacnet/datalink/websockets/CMakeLists.txt b/test/bacnet/datalink/websockets/CMakeLists.txt new file mode 100644 index 00000000..b284e7fa --- /dev/null +++ b/test/bacnet/datalink/websockets/CMakeLists.txt @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +include(CMakePrintHelpers) + +find_package(Threads) +find_package(libwebsockets CONFIG REQUIRED) +include_directories(${LIBWEBSOCKETS_INCLUDE_DIRS}) + +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-z0-9A-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BACDL_BSC + BSC_CONF_CLIENT_CONNECTIONS_NUM=5 + BSC_CONF_SERVER_HUB_CONNECTIONS_MAX_NUM=4 + BSC_CONF_SERVER_DIRECT_CONNECTIONS_MAX_NUM=4 + BSC_CONF_WSURL_MAX_LEN=128 + BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN=128 + BSC_CONF_WEBSOCKET_SERVERS_NUM=4 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ${LIBWEBSOCKETS_INCLUDE_DIRS} + ) + +if(ZEPHYR_BASE) + message(FATAL_ERROR "ZEPHYR_BASE env variable defined. Use zephyr/CMakeLists.txt for Zephyr build") +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(STATUS "Websockets test: building for linux") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux) + add_compile_definitions(BACNET_PORT=linux) + find_package(OpenSSL) + find_package(PkgConfig) + pkg_check_modules(LIB_WEBSOCKETS REQUIRED libwebsockets) + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/linux/websocket-cli.c + ${PORTS_DIR}/linux/websocket-srv.c + ${PORTS_DIR}/linux/websocket-global.c + ${PORTS_DIR}/linux/bsc-event.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) +elseif(WIN32) + message(STATUS "Websockets test: building for win32") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/win32) + add_compile_definitions(BACNET_PORT=win32) + find_package(OpenSSL) + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/win32/websocket-cli.c + ${PORTS_DIR}/win32/websocket-srv.c + ${PORTS_DIR}/win32/websocket-global.c + ${PORTS_DIR}/win32/bsc-event.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + + # basically if you use vcpkg you should just add ${LIBWEBSOCKETS_LIBRARIES} + # into target_link_libraries() but for some reason it does not work as expected + # so that's why libs have to be hardcoded as workaround + + target_link_libraries(${PROJECT_NAME} + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\websockets.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libssl.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\libcrypto.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\pthreadVC3.lib + ws2_32.lib + userenv.lib + psapi.lib + iphlpapi.lib + crypt32.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\zlib.lib + C:\\vcpkg\\installed\\x64-windows-custom\\lib\\uv.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + comdlg32.lib + advapi32.lib + ) +elseif(APPLE) + message(STATUS "Websockets test: building for APPLE") + set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/bsd) + add_compile_definitions(BACNET_PORT=bsd) + execute_process ( + COMMAND bash -c "brew --prefix openssl" + OUTPUT_VARIABLE OPEN_SSL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE + ) + include_directories(${OPEN_SSL_DIR}/include) + add_executable(${PROJECT_NAME} + ${PORTS_DIR}/bsd/websocket-cli.c + ${PORTS_DIR}/bsd/websocket-srv.c + ${PORTS_DIR}/bsd/websocket-global.c + ${PORTS_DIR}/bsd/bsc-event.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + target_link_libraries(${PROJECT_NAME} + ${LIBWEBSOCKETS_LIBRARIES} + ) +endif() diff --git a/test/bacnet/datalink/websockets/src/main.c b/test/bacnet/datalink/websockets/src/main.c new file mode 100644 index 00000000..0c53f43b --- /dev/null +++ b/test/bacnet/datalink/websockets/src/main.c @@ -0,0 +1,8250 @@ +/* + * Copyright (c) 2020 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +/* @file + * @brief test server/client websocket interface + */ + +#include +#include +#include +#include +#include +#include + +#define TEST_THREAD_NUM 10 + +unsigned char ca_key[] = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x52, + 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, + 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x45, + 0x6f, 0x67, 0x49, 0x42, 0x41, 0x41, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, + 0x31, 0x66, 0x7a, 0x73, 0x2b, 0x56, 0x62, 0x46, 0x39, 0x31, 0x65, 0x45, + 0x66, 0x4c, 0x32, 0x67, 0x44, 0x63, 0x6a, 0x79, 0x44, 0x47, 0x68, 0x62, + 0x4d, 0x6f, 0x44, 0x48, 0x6c, 0x71, 0x53, 0x6d, 0x70, 0x78, 0x7a, 0x45, + 0x2b, 0x4a, 0x48, 0x34, 0x6f, 0x46, 0x47, 0x46, 0x42, 0x31, 0x47, 0x48, + 0x0a, 0x69, 0x35, 0x47, 0x7a, 0x33, 0x73, 0x47, 0x51, 0x4d, 0x6e, 0x41, + 0x6e, 0x32, 0x4c, 0x41, 0x6e, 0x51, 0x53, 0x5a, 0x72, 0x46, 0x77, 0x6c, + 0x77, 0x72, 0x48, 0x34, 0x68, 0x43, 0x36, 0x62, 0x6d, 0x4a, 0x33, 0x71, + 0x55, 0x58, 0x72, 0x61, 0x32, 0x64, 0x76, 0x63, 0x45, 0x34, 0x2f, 0x32, + 0x2f, 0x70, 0x74, 0x6b, 0x34, 0x31, 0x47, 0x39, 0x68, 0x75, 0x45, 0x4f, + 0x75, 0x77, 0x69, 0x6b, 0x68, 0x0a, 0x7a, 0x5a, 0x59, 0x65, 0x67, 0x6b, + 0x4b, 0x68, 0x62, 0x78, 0x46, 0x70, 0x77, 0x56, 0x2f, 0x76, 0x48, 0x32, + 0x48, 0x6f, 0x55, 0x45, 0x39, 0x30, 0x4d, 0x43, 0x56, 0x7a, 0x66, 0x59, + 0x56, 0x51, 0x62, 0x6a, 0x38, 0x51, 0x2f, 0x73, 0x51, 0x50, 0x6d, 0x79, + 0x4f, 0x6d, 0x64, 0x76, 0x46, 0x74, 0x4a, 0x41, 0x4e, 0x47, 0x53, 0x2f, + 0x52, 0x58, 0x31, 0x67, 0x6b, 0x2b, 0x6e, 0x7a, 0x67, 0x38, 0x0a, 0x54, + 0x4f, 0x33, 0x6f, 0x4a, 0x45, 0x6b, 0x70, 0x69, 0x6e, 0x67, 0x34, 0x45, + 0x31, 0x4c, 0x4a, 0x2b, 0x63, 0x48, 0x66, 0x45, 0x66, 0x4a, 0x45, 0x49, + 0x5a, 0x4f, 0x4d, 0x69, 0x4e, 0x69, 0x6a, 0x31, 0x4d, 0x72, 0x50, 0x70, + 0x32, 0x4d, 0x63, 0x36, 0x4d, 0x49, 0x7a, 0x54, 0x6f, 0x76, 0x4c, 0x6f, + 0x51, 0x31, 0x38, 0x36, 0x2f, 0x68, 0x63, 0x59, 0x50, 0x36, 0x62, 0x4f, + 0x76, 0x46, 0x59, 0x0a, 0x6d, 0x59, 0x78, 0x68, 0x59, 0x46, 0x30, 0x68, + 0x52, 0x38, 0x4f, 0x57, 0x53, 0x4c, 0x30, 0x55, 0x45, 0x78, 0x69, 0x4e, + 0x6f, 0x64, 0x45, 0x2f, 0x61, 0x70, 0x68, 0x7a, 0x39, 0x41, 0x56, 0x52, + 0x63, 0x6d, 0x79, 0x62, 0x76, 0x62, 0x4f, 0x6c, 0x77, 0x32, 0x4c, 0x71, + 0x44, 0x50, 0x42, 0x43, 0x62, 0x46, 0x6b, 0x72, 0x50, 0x75, 0x4c, 0x7a, + 0x6d, 0x6e, 0x47, 0x6b, 0x78, 0x46, 0x57, 0x41, 0x0a, 0x45, 0x42, 0x65, + 0x52, 0x38, 0x51, 0x43, 0x33, 0x77, 0x72, 0x57, 0x6a, 0x6e, 0x6a, 0x34, + 0x30, 0x71, 0x66, 0x6e, 0x33, 0x78, 0x50, 0x6c, 0x4e, 0x41, 0x73, 0x57, + 0x42, 0x64, 0x7a, 0x52, 0x2f, 0x64, 0x32, 0x43, 0x6d, 0x63, 0x51, 0x49, + 0x44, 0x41, 0x51, 0x41, 0x42, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x43, 0x58, + 0x49, 0x78, 0x7a, 0x38, 0x76, 0x70, 0x7a, 0x30, 0x4a, 0x59, 0x77, 0x64, + 0x7a, 0x0a, 0x70, 0x44, 0x4e, 0x4b, 0x37, 0x42, 0x4a, 0x73, 0x79, 0x73, + 0x32, 0x63, 0x46, 0x36, 0x48, 0x74, 0x36, 0x4d, 0x39, 0x52, 0x4d, 0x53, + 0x61, 0x43, 0x2f, 0x39, 0x65, 0x76, 0x44, 0x55, 0x4a, 0x42, 0x6a, 0x79, + 0x47, 0x42, 0x31, 0x4c, 0x54, 0x63, 0x6b, 0x4d, 0x32, 0x58, 0x4b, 0x44, + 0x49, 0x47, 0x79, 0x4b, 0x65, 0x6b, 0x56, 0x50, 0x78, 0x34, 0x57, 0x6b, + 0x44, 0x61, 0x39, 0x4a, 0x4c, 0x4f, 0x0a, 0x49, 0x59, 0x32, 0x50, 0x41, + 0x32, 0x76, 0x46, 0x37, 0x32, 0x6f, 0x4b, 0x4b, 0x2f, 0x37, 0x6c, 0x36, + 0x31, 0x56, 0x57, 0x76, 0x63, 0x59, 0x6b, 0x39, 0x4b, 0x68, 0x49, 0x71, + 0x79, 0x37, 0x31, 0x66, 0x46, 0x61, 0x45, 0x7a, 0x31, 0x5a, 0x49, 0x31, + 0x61, 0x42, 0x36, 0x2f, 0x71, 0x56, 0x36, 0x66, 0x77, 0x71, 0x58, 0x69, + 0x79, 0x48, 0x44, 0x4a, 0x63, 0x7a, 0x71, 0x6a, 0x2f, 0x33, 0x31, 0x0a, + 0x38, 0x45, 0x48, 0x48, 0x4f, 0x51, 0x55, 0x44, 0x4d, 0x59, 0x34, 0x2f, + 0x4f, 0x55, 0x46, 0x2f, 0x56, 0x37, 0x6f, 0x6f, 0x4b, 0x64, 0x31, 0x33, + 0x67, 0x35, 0x72, 0x7a, 0x4c, 0x41, 0x50, 0x47, 0x4e, 0x74, 0x43, 0x37, + 0x6e, 0x2b, 0x56, 0x6b, 0x6a, 0x41, 0x31, 0x31, 0x78, 0x38, 0x4f, 0x6c, + 0x32, 0x57, 0x62, 0x48, 0x33, 0x64, 0x6a, 0x31, 0x78, 0x4f, 0x47, 0x2f, + 0x66, 0x50, 0x52, 0x57, 0x0a, 0x72, 0x63, 0x69, 0x57, 0x46, 0x61, 0x79, + 0x4a, 0x72, 0x33, 0x71, 0x56, 0x6c, 0x5a, 0x2b, 0x42, 0x79, 0x31, 0x66, + 0x44, 0x43, 0x59, 0x38, 0x31, 0x4d, 0x73, 0x55, 0x56, 0x41, 0x65, 0x55, + 0x57, 0x2f, 0x54, 0x33, 0x6b, 0x39, 0x42, 0x6e, 0x4b, 0x67, 0x36, 0x68, + 0x71, 0x61, 0x33, 0x4a, 0x47, 0x36, 0x37, 0x4e, 0x41, 0x36, 0x42, 0x72, + 0x63, 0x7a, 0x4e, 0x4b, 0x53, 0x4b, 0x47, 0x79, 0x37, 0x0a, 0x70, 0x70, + 0x56, 0x56, 0x41, 0x65, 0x61, 0x47, 0x44, 0x49, 0x73, 0x37, 0x62, 0x73, + 0x43, 0x7a, 0x52, 0x70, 0x45, 0x42, 0x43, 0x67, 0x4e, 0x68, 0x52, 0x42, + 0x5a, 0x45, 0x2f, 0x41, 0x79, 0x65, 0x77, 0x4d, 0x56, 0x6e, 0x42, 0x4a, + 0x62, 0x4b, 0x41, 0x72, 0x6e, 0x72, 0x79, 0x69, 0x69, 0x38, 0x2f, 0x5a, + 0x62, 0x56, 0x51, 0x55, 0x4d, 0x4c, 0x56, 0x48, 0x32, 0x70, 0x68, 0x34, + 0x46, 0x69, 0x0a, 0x6b, 0x2b, 0x30, 0x56, 0x66, 0x56, 0x45, 0x43, 0x67, + 0x59, 0x45, 0x41, 0x2f, 0x5a, 0x45, 0x39, 0x35, 0x71, 0x4b, 0x46, 0x6d, + 0x71, 0x51, 0x65, 0x4f, 0x36, 0x6d, 0x76, 0x42, 0x37, 0x6b, 0x56, 0x6b, + 0x70, 0x56, 0x4b, 0x53, 0x58, 0x47, 0x75, 0x2f, 0x59, 0x35, 0x6b, 0x46, + 0x63, 0x70, 0x4c, 0x57, 0x4f, 0x63, 0x35, 0x45, 0x56, 0x48, 0x4c, 0x36, + 0x46, 0x6d, 0x66, 0x7a, 0x62, 0x71, 0x42, 0x0a, 0x41, 0x78, 0x6f, 0x71, + 0x39, 0x48, 0x68, 0x66, 0x4c, 0x79, 0x39, 0x61, 0x37, 0x4e, 0x7a, 0x73, + 0x61, 0x43, 0x77, 0x30, 0x66, 0x34, 0x56, 0x79, 0x5a, 0x72, 0x73, 0x4f, + 0x61, 0x73, 0x67, 0x42, 0x31, 0x43, 0x42, 0x5a, 0x50, 0x36, 0x30, 0x32, + 0x43, 0x47, 0x69, 0x38, 0x68, 0x62, 0x48, 0x61, 0x6a, 0x4a, 0x4d, 0x77, + 0x4a, 0x67, 0x37, 0x42, 0x39, 0x64, 0x38, 0x52, 0x71, 0x62, 0x67, 0x35, + 0x0a, 0x76, 0x61, 0x6b, 0x45, 0x75, 0x64, 0x70, 0x42, 0x31, 0x68, 0x39, + 0x6a, 0x6d, 0x39, 0x4e, 0x4a, 0x35, 0x61, 0x63, 0x52, 0x37, 0x2f, 0x76, + 0x63, 0x64, 0x56, 0x70, 0x45, 0x67, 0x70, 0x6e, 0x32, 0x6e, 0x50, 0x38, + 0x34, 0x52, 0x6e, 0x2f, 0x4e, 0x36, 0x72, 0x65, 0x75, 0x77, 0x52, 0x56, + 0x61, 0x38, 0x2f, 0x75, 0x2b, 0x59, 0x65, 0x63, 0x43, 0x67, 0x59, 0x45, + 0x41, 0x32, 0x41, 0x70, 0x36, 0x0a, 0x4f, 0x36, 0x78, 0x4d, 0x32, 0x32, + 0x70, 0x4e, 0x44, 0x64, 0x33, 0x70, 0x35, 0x79, 0x50, 0x65, 0x38, 0x36, + 0x38, 0x66, 0x79, 0x66, 0x35, 0x56, 0x35, 0x2f, 0x32, 0x4b, 0x6f, 0x76, + 0x34, 0x76, 0x46, 0x38, 0x77, 0x66, 0x69, 0x73, 0x61, 0x45, 0x63, 0x66, + 0x62, 0x2f, 0x57, 0x62, 0x4a, 0x52, 0x67, 0x77, 0x63, 0x36, 0x2f, 0x6b, + 0x55, 0x55, 0x6b, 0x31, 0x6c, 0x4a, 0x6e, 0x78, 0x6e, 0x47, 0x0a, 0x50, + 0x4b, 0x39, 0x38, 0x48, 0x6d, 0x6a, 0x69, 0x64, 0x35, 0x59, 0x33, 0x39, + 0x64, 0x7a, 0x69, 0x6c, 0x58, 0x67, 0x74, 0x43, 0x36, 0x36, 0x58, 0x56, + 0x41, 0x75, 0x30, 0x4f, 0x4a, 0x30, 0x34, 0x6c, 0x71, 0x69, 0x6c, 0x73, + 0x4d, 0x34, 0x42, 0x53, 0x43, 0x64, 0x6b, 0x6c, 0x69, 0x6d, 0x70, 0x68, + 0x75, 0x46, 0x73, 0x7a, 0x4a, 0x33, 0x50, 0x4f, 0x74, 0x36, 0x55, 0x37, + 0x68, 0x67, 0x42, 0x0a, 0x31, 0x71, 0x73, 0x4b, 0x4d, 0x4b, 0x61, 0x75, + 0x74, 0x6a, 0x33, 0x2f, 0x38, 0x76, 0x32, 0x53, 0x77, 0x63, 0x71, 0x36, + 0x46, 0x49, 0x78, 0x36, 0x6c, 0x36, 0x46, 0x6f, 0x37, 0x71, 0x2b, 0x73, + 0x70, 0x74, 0x56, 0x72, 0x57, 0x65, 0x63, 0x43, 0x67, 0x59, 0x42, 0x2b, + 0x36, 0x63, 0x31, 0x54, 0x76, 0x4a, 0x43, 0x6d, 0x66, 0x2f, 0x4a, 0x70, + 0x35, 0x6c, 0x6f, 0x6d, 0x77, 0x57, 0x71, 0x63, 0x0a, 0x76, 0x59, 0x41, + 0x37, 0x46, 0x6c, 0x32, 0x42, 0x70, 0x31, 0x31, 0x4d, 0x30, 0x72, 0x32, + 0x33, 0x74, 0x37, 0x4f, 0x47, 0x69, 0x61, 0x78, 0x48, 0x6c, 0x57, 0x51, + 0x34, 0x73, 0x6c, 0x71, 0x55, 0x56, 0x4f, 0x71, 0x66, 0x42, 0x67, 0x69, + 0x4f, 0x4d, 0x32, 0x4f, 0x4e, 0x48, 0x6c, 0x35, 0x74, 0x48, 0x59, 0x4d, + 0x42, 0x4f, 0x4b, 0x65, 0x7a, 0x35, 0x33, 0x67, 0x6c, 0x31, 0x67, 0x6d, + 0x6b, 0x0a, 0x52, 0x4c, 0x53, 0x6d, 0x2f, 0x47, 0x6b, 0x49, 0x2b, 0x48, + 0x4d, 0x7a, 0x62, 0x33, 0x74, 0x31, 0x31, 0x4d, 0x33, 0x4b, 0x6e, 0x71, + 0x52, 0x53, 0x44, 0x64, 0x35, 0x6e, 0x56, 0x6b, 0x41, 0x41, 0x50, 0x37, + 0x4b, 0x50, 0x32, 0x30, 0x41, 0x4d, 0x6a, 0x68, 0x56, 0x72, 0x44, 0x75, + 0x76, 0x7a, 0x75, 0x42, 0x56, 0x77, 0x53, 0x6c, 0x31, 0x6a, 0x6c, 0x31, + 0x53, 0x6e, 0x45, 0x61, 0x79, 0x76, 0x0a, 0x6b, 0x38, 0x5a, 0x30, 0x38, + 0x73, 0x37, 0x37, 0x35, 0x67, 0x66, 0x66, 0x75, 0x48, 0x4b, 0x58, 0x6e, + 0x36, 0x38, 0x41, 0x6a, 0x51, 0x4b, 0x42, 0x67, 0x46, 0x58, 0x73, 0x4c, + 0x4e, 0x73, 0x6f, 0x31, 0x74, 0x52, 0x35, 0x50, 0x62, 0x59, 0x6a, 0x4b, + 0x56, 0x44, 0x39, 0x69, 0x6b, 0x47, 0x65, 0x78, 0x2b, 0x54, 0x64, 0x57, + 0x36, 0x74, 0x4e, 0x77, 0x6d, 0x4b, 0x36, 0x39, 0x31, 0x33, 0x65, 0x0a, + 0x6d, 0x44, 0x6a, 0x6f, 0x5a, 0x57, 0x71, 0x79, 0x45, 0x72, 0x4c, 0x49, + 0x34, 0x66, 0x52, 0x62, 0x33, 0x74, 0x47, 0x63, 0x42, 0x65, 0x66, 0x6f, + 0x6e, 0x67, 0x68, 0x43, 0x42, 0x76, 0x37, 0x42, 0x79, 0x48, 0x71, 0x4c, + 0x75, 0x6d, 0x35, 0x58, 0x64, 0x32, 0x41, 0x34, 0x66, 0x6f, 0x46, 0x31, + 0x37, 0x32, 0x78, 0x79, 0x2f, 0x73, 0x71, 0x31, 0x63, 0x50, 0x4d, 0x48, + 0x54, 0x4b, 0x64, 0x57, 0x0a, 0x34, 0x62, 0x63, 0x6b, 0x35, 0x34, 0x75, + 0x62, 0x35, 0x7a, 0x78, 0x31, 0x79, 0x32, 0x2f, 0x53, 0x6e, 0x69, 0x50, + 0x76, 0x4b, 0x36, 0x6b, 0x39, 0x4e, 0x7a, 0x78, 0x4f, 0x6e, 0x67, 0x53, + 0x54, 0x75, 0x42, 0x54, 0x4c, 0x5a, 0x6a, 0x63, 0x6a, 0x42, 0x33, 0x58, + 0x4c, 0x39, 0x68, 0x39, 0x50, 0x45, 0x70, 0x7a, 0x7a, 0x6c, 0x68, 0x70, + 0x53, 0x58, 0x74, 0x70, 0x33, 0x55, 0x68, 0x4a, 0x30, 0x0a, 0x56, 0x53, + 0x4c, 0x48, 0x41, 0x6f, 0x47, 0x41, 0x44, 0x4e, 0x79, 0x6b, 0x52, 0x50, + 0x32, 0x55, 0x38, 0x34, 0x78, 0x70, 0x50, 0x6d, 0x36, 0x77, 0x31, 0x69, + 0x36, 0x72, 0x47, 0x48, 0x71, 0x50, 0x38, 0x67, 0x65, 0x2b, 0x4f, 0x6f, + 0x6f, 0x77, 0x69, 0x47, 0x47, 0x4b, 0x48, 0x54, 0x45, 0x48, 0x72, 0x56, + 0x39, 0x53, 0x79, 0x66, 0x6c, 0x2f, 0x63, 0x42, 0x39, 0x55, 0x4d, 0x65, + 0x79, 0x43, 0x0a, 0x45, 0x49, 0x68, 0x46, 0x37, 0x4e, 0x71, 0x6b, 0x35, + 0x76, 0x2b, 0x4d, 0x68, 0x2b, 0x50, 0x31, 0x52, 0x49, 0x52, 0x71, 0x35, + 0x39, 0x66, 0x4d, 0x41, 0x34, 0x72, 0x4e, 0x7a, 0x6d, 0x61, 0x78, 0x65, + 0x6b, 0x35, 0x72, 0x4b, 0x36, 0x45, 0x50, 0x67, 0x31, 0x62, 0x2f, 0x67, + 0x78, 0x7a, 0x45, 0x73, 0x4c, 0x56, 0x45, 0x45, 0x42, 0x58, 0x49, 0x51, + 0x35, 0x57, 0x57, 0x6e, 0x58, 0x6a, 0x52, 0x0a, 0x75, 0x2b, 0x5a, 0x56, + 0x49, 0x37, 0x33, 0x68, 0x62, 0x36, 0x79, 0x56, 0x56, 0x70, 0x75, 0x6f, + 0x4e, 0x68, 0x52, 0x43, 0x67, 0x59, 0x67, 0x45, 0x70, 0x48, 0x6b, 0x6e, + 0x47, 0x43, 0x45, 0x76, 0x62, 0x55, 0x52, 0x4b, 0x4f, 0x68, 0x79, 0x44, + 0x4d, 0x4e, 0x69, 0x6c, 0x49, 0x6a, 0x31, 0x6d, 0x71, 0x44, 0x4d, 0x3d, + 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x52, 0x53, + 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, + 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a +}; + +unsigned char ca_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x44, + 0x48, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x51, + 0x43, + 0x43, + 0x51, + 0x44, + 0x43, + 0x44, + 0x35, + 0x33, + 0x59, + 0x5a, + 0x4a, + 0x4a, + 0x37, + 0x6c, + 0x6a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x6b, + 0x71, + 0x68, + 0x6b, + 0x69, + 0x47, + 0x39, + 0x77, + 0x30, + 0x42, + 0x41, + 0x51, + 0x73, + 0x46, + 0x41, + 0x44, + 0x42, + 0x50, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x63, + 0x54, + 0x43, + 0x56, + 0x68, + 0x70, + 0x59, + 0x57, + 0x39, + 0x69, + 0x61, + 0x58, + 0x52, + 0x68, + 0x62, + 0x6a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x0a, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x41, + 0x67, + 0x46, + 0x77, + 0x30, + 0x79, + 0x4d, + 0x6a, + 0x41, + 0x33, + 0x4d, + 0x44, + 0x59, + 0x78, + 0x4d, + 0x54, + 0x49, + 0x30, + 0x4d, + 0x6a, + 0x42, + 0x61, + 0x47, + 0x41, + 0x38, + 0x79, + 0x4d, + 0x44, + 0x55, + 0x77, + 0x4d, + 0x44, + 0x63, + 0x78, + 0x4f, + 0x54, + 0x45, + 0x78, + 0x0a, + 0x4d, + 0x6a, + 0x51, + 0x79, + 0x4d, + 0x46, + 0x6f, + 0x77, + 0x54, + 0x7a, + 0x45, + 0x62, + 0x4d, + 0x42, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x68, + 0x4d, + 0x53, + 0x62, + 0x47, + 0x6c, + 0x69, + 0x64, + 0x32, + 0x56, + 0x69, + 0x63, + 0x32, + 0x39, + 0x6a, + 0x61, + 0x32, + 0x56, + 0x30, + 0x63, + 0x79, + 0x31, + 0x30, + 0x5a, + 0x58, + 0x4e, + 0x30, + 0x4d, + 0x52, + 0x49, + 0x77, + 0x45, + 0x41, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x48, + 0x45, + 0x77, + 0x6c, + 0x59, + 0x0a, + 0x61, + 0x57, + 0x46, + 0x76, + 0x59, + 0x6d, + 0x6c, + 0x30, + 0x59, + 0x57, + 0x34, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x67, + 0x54, + 0x42, + 0x6c, + 0x52, + 0x68, + 0x61, + 0x58, + 0x42, + 0x6c, + 0x61, + 0x54, + 0x45, + 0x4c, + 0x4d, + 0x41, + 0x6b, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x68, + 0x4d, + 0x43, + 0x56, + 0x46, + 0x63, + 0x77, + 0x67, + 0x67, + 0x45, + 0x69, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x0a, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x41, + 0x51, + 0x55, + 0x41, + 0x41, + 0x34, + 0x49, + 0x42, + 0x44, + 0x77, + 0x41, + 0x77, + 0x67, + 0x67, + 0x45, + 0x4b, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x51, + 0x44, + 0x56, + 0x2f, + 0x4f, + 0x7a, + 0x35, + 0x56, + 0x73, + 0x58, + 0x33, + 0x56, + 0x34, + 0x52, + 0x38, + 0x76, + 0x61, + 0x41, + 0x4e, + 0x79, + 0x50, + 0x49, + 0x4d, + 0x61, + 0x46, + 0x73, + 0x79, + 0x67, + 0x4d, + 0x65, + 0x57, + 0x70, + 0x4b, + 0x61, + 0x6e, + 0x0a, + 0x48, + 0x4d, + 0x54, + 0x34, + 0x6b, + 0x66, + 0x69, + 0x67, + 0x55, + 0x59, + 0x55, + 0x48, + 0x55, + 0x59, + 0x65, + 0x4c, + 0x6b, + 0x62, + 0x50, + 0x65, + 0x77, + 0x5a, + 0x41, + 0x79, + 0x63, + 0x43, + 0x66, + 0x59, + 0x73, + 0x43, + 0x64, + 0x42, + 0x4a, + 0x6d, + 0x73, + 0x58, + 0x43, + 0x58, + 0x43, + 0x73, + 0x66, + 0x69, + 0x45, + 0x4c, + 0x70, + 0x75, + 0x59, + 0x6e, + 0x65, + 0x70, + 0x52, + 0x65, + 0x74, + 0x72, + 0x5a, + 0x32, + 0x39, + 0x77, + 0x54, + 0x6a, + 0x2f, + 0x62, + 0x2b, + 0x6d, + 0x0a, + 0x32, + 0x54, + 0x6a, + 0x55, + 0x62, + 0x32, + 0x47, + 0x34, + 0x51, + 0x36, + 0x37, + 0x43, + 0x4b, + 0x53, + 0x48, + 0x4e, + 0x6c, + 0x68, + 0x36, + 0x43, + 0x51, + 0x71, + 0x46, + 0x76, + 0x45, + 0x57, + 0x6e, + 0x42, + 0x58, + 0x2b, + 0x38, + 0x66, + 0x59, + 0x65, + 0x68, + 0x51, + 0x54, + 0x33, + 0x51, + 0x77, + 0x4a, + 0x58, + 0x4e, + 0x39, + 0x68, + 0x56, + 0x42, + 0x75, + 0x50, + 0x78, + 0x44, + 0x2b, + 0x78, + 0x41, + 0x2b, + 0x62, + 0x49, + 0x36, + 0x5a, + 0x32, + 0x38, + 0x57, + 0x30, + 0x6b, + 0x0a, + 0x41, + 0x30, + 0x5a, + 0x4c, + 0x39, + 0x46, + 0x66, + 0x57, + 0x43, + 0x54, + 0x36, + 0x66, + 0x4f, + 0x44, + 0x78, + 0x4d, + 0x37, + 0x65, + 0x67, + 0x6b, + 0x53, + 0x53, + 0x6d, + 0x4b, + 0x65, + 0x44, + 0x67, + 0x54, + 0x55, + 0x73, + 0x6e, + 0x35, + 0x77, + 0x64, + 0x38, + 0x52, + 0x38, + 0x6b, + 0x51, + 0x68, + 0x6b, + 0x34, + 0x79, + 0x49, + 0x32, + 0x4b, + 0x50, + 0x55, + 0x79, + 0x73, + 0x2b, + 0x6e, + 0x59, + 0x78, + 0x7a, + 0x6f, + 0x77, + 0x6a, + 0x4e, + 0x4f, + 0x69, + 0x38, + 0x75, + 0x68, + 0x0a, + 0x44, + 0x58, + 0x7a, + 0x72, + 0x2b, + 0x46, + 0x78, + 0x67, + 0x2f, + 0x70, + 0x73, + 0x36, + 0x38, + 0x56, + 0x69, + 0x5a, + 0x6a, + 0x47, + 0x46, + 0x67, + 0x58, + 0x53, + 0x46, + 0x48, + 0x77, + 0x35, + 0x5a, + 0x49, + 0x76, + 0x52, + 0x51, + 0x54, + 0x47, + 0x49, + 0x32, + 0x68, + 0x30, + 0x54, + 0x39, + 0x71, + 0x6d, + 0x48, + 0x50, + 0x30, + 0x42, + 0x56, + 0x46, + 0x79, + 0x62, + 0x4a, + 0x75, + 0x39, + 0x73, + 0x36, + 0x58, + 0x44, + 0x59, + 0x75, + 0x6f, + 0x4d, + 0x38, + 0x45, + 0x4a, + 0x73, + 0x0a, + 0x57, + 0x53, + 0x73, + 0x2b, + 0x34, + 0x76, + 0x4f, + 0x61, + 0x63, + 0x61, + 0x54, + 0x45, + 0x56, + 0x59, + 0x41, + 0x51, + 0x46, + 0x35, + 0x48, + 0x78, + 0x41, + 0x4c, + 0x66, + 0x43, + 0x74, + 0x61, + 0x4f, + 0x65, + 0x50, + 0x6a, + 0x53, + 0x70, + 0x2b, + 0x66, + 0x66, + 0x45, + 0x2b, + 0x55, + 0x30, + 0x43, + 0x78, + 0x59, + 0x46, + 0x33, + 0x4e, + 0x48, + 0x39, + 0x33, + 0x59, + 0x4b, + 0x5a, + 0x78, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x0a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x49, + 0x6d, + 0x4a, + 0x76, + 0x71, + 0x6e, + 0x70, + 0x50, + 0x64, + 0x53, + 0x75, + 0x46, + 0x4f, + 0x5a, + 0x54, + 0x36, + 0x76, + 0x74, + 0x48, + 0x31, + 0x70, + 0x4a, + 0x43, + 0x45, + 0x76, + 0x4a, + 0x39, + 0x62, + 0x53, + 0x78, + 0x31, + 0x43, + 0x41, + 0x76, + 0x36, + 0x46, + 0x34, + 0x46, + 0x44, + 0x6f, + 0x77, + 0x4b, + 0x77, + 0x0a, + 0x43, + 0x71, + 0x4b, + 0x53, + 0x6c, + 0x59, + 0x45, + 0x6a, + 0x72, + 0x49, + 0x4c, + 0x64, + 0x6c, + 0x42, + 0x30, + 0x39, + 0x32, + 0x31, + 0x4f, + 0x54, + 0x30, + 0x76, + 0x61, + 0x68, + 0x33, + 0x6c, + 0x55, + 0x76, + 0x2f, + 0x6b, + 0x47, + 0x4e, + 0x4c, + 0x76, + 0x58, + 0x55, + 0x71, + 0x54, + 0x69, + 0x42, + 0x61, + 0x6b, + 0x77, + 0x66, + 0x52, + 0x47, + 0x30, + 0x39, + 0x61, + 0x49, + 0x45, + 0x6e, + 0x53, + 0x68, + 0x6d, + 0x79, + 0x6f, + 0x30, + 0x68, + 0x63, + 0x65, + 0x4f, + 0x68, + 0x33, + 0x0a, + 0x4f, + 0x31, + 0x4b, + 0x4b, + 0x59, + 0x76, + 0x4a, + 0x32, + 0x6a, + 0x4a, + 0x47, + 0x6b, + 0x36, + 0x50, + 0x6c, + 0x52, + 0x78, + 0x65, + 0x53, + 0x67, + 0x37, + 0x64, + 0x35, + 0x4d, + 0x69, + 0x37, + 0x58, + 0x67, + 0x6e, + 0x41, + 0x64, + 0x65, + 0x61, + 0x78, + 0x77, + 0x68, + 0x75, + 0x76, + 0x5a, + 0x5a, + 0x6d, + 0x61, + 0x49, + 0x7a, + 0x62, + 0x68, + 0x41, + 0x57, + 0x50, + 0x38, + 0x71, + 0x67, + 0x49, + 0x30, + 0x36, + 0x50, + 0x32, + 0x52, + 0x42, + 0x53, + 0x35, + 0x42, + 0x4a, + 0x76, + 0x0a, + 0x72, + 0x44, + 0x44, + 0x33, + 0x44, + 0x68, + 0x77, + 0x38, + 0x4e, + 0x38, + 0x47, + 0x77, + 0x42, + 0x44, + 0x31, + 0x52, + 0x59, + 0x32, + 0x79, + 0x4b, + 0x72, + 0x79, + 0x46, + 0x51, + 0x2b, + 0x34, + 0x55, + 0x32, + 0x31, + 0x45, + 0x72, + 0x73, + 0x77, + 0x2f, + 0x33, + 0x38, + 0x63, + 0x59, + 0x38, + 0x55, + 0x41, + 0x46, + 0x54, + 0x6b, + 0x67, + 0x33, + 0x72, + 0x57, + 0x72, + 0x34, + 0x44, + 0x57, + 0x78, + 0x36, + 0x74, + 0x6e, + 0x49, + 0x66, + 0x64, + 0x72, + 0x4e, + 0x31, + 0x49, + 0x49, + 0x0a, + 0x70, + 0x52, + 0x71, + 0x53, + 0x78, + 0x48, + 0x51, + 0x57, + 0x34, + 0x6b, + 0x5a, + 0x61, + 0x67, + 0x31, + 0x75, + 0x5a, + 0x64, + 0x46, + 0x63, + 0x53, + 0x69, + 0x61, + 0x59, + 0x54, + 0x2b, + 0x65, + 0x62, + 0x4c, + 0x57, + 0x56, + 0x58, + 0x41, + 0x7a, + 0x62, + 0x66, + 0x4a, + 0x4c, + 0x4f, + 0x56, + 0x38, + 0x45, + 0x46, + 0x46, + 0x33, + 0x52, + 0x44, + 0x43, + 0x2f, + 0x41, + 0x33, + 0x55, + 0x33, + 0x56, + 0x63, + 0x4f, + 0x52, + 0x44, + 0x42, + 0x44, + 0x57, + 0x44, + 0x68, + 0x6d, + 0x68, + 0x0a, + 0x38, + 0x72, + 0x34, + 0x54, + 0x2b, + 0x36, + 0x46, + 0x67, + 0x67, + 0x59, + 0x4c, + 0x32, + 0x65, + 0x6a, + 0x4d, + 0x6d, + 0x66, + 0x77, + 0x62, + 0x65, + 0x77, + 0x7a, + 0x49, + 0x2b, + 0x48, + 0x6d, + 0x34, + 0x53, + 0x69, + 0x64, + 0x67, + 0x4f, + 0x78, + 0x4c, + 0x66, + 0x31, + 0x46, + 0x78, + 0x31, + 0x64, + 0x32, + 0x76, + 0x34, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char client_key[] = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x52, + 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, + 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x4a, + 0x4b, 0x51, 0x49, 0x42, 0x41, 0x41, 0x4b, 0x43, 0x41, 0x67, 0x45, 0x41, + 0x72, 0x77, 0x53, 0x79, 0x4e, 0x6e, 0x68, 0x66, 0x76, 0x44, 0x69, 0x63, + 0x47, 0x4a, 0x4f, 0x42, 0x66, 0x55, 0x51, 0x4c, 0x47, 0x45, 0x70, 0x4b, + 0x43, 0x32, 0x2f, 0x4e, 0x32, 0x35, 0x54, 0x6f, 0x32, 0x4b, 0x66, 0x36, + 0x36, 0x7a, 0x6b, 0x44, 0x62, 0x33, 0x6d, 0x34, 0x49, 0x50, 0x77, 0x4c, + 0x0a, 0x63, 0x63, 0x78, 0x47, 0x57, 0x48, 0x57, 0x75, 0x65, 0x78, 0x69, + 0x70, 0x6d, 0x76, 0x41, 0x77, 0x46, 0x2b, 0x58, 0x32, 0x36, 0x46, 0x47, + 0x7a, 0x35, 0x66, 0x4a, 0x64, 0x71, 0x38, 0x46, 0x68, 0x77, 0x6c, 0x71, + 0x2b, 0x6e, 0x71, 0x6a, 0x34, 0x50, 0x68, 0x6c, 0x2f, 0x51, 0x2b, 0x77, + 0x55, 0x42, 0x6c, 0x51, 0x2b, 0x58, 0x4d, 0x6a, 0x71, 0x49, 0x70, 0x77, + 0x79, 0x7a, 0x70, 0x5a, 0x6d, 0x0a, 0x34, 0x48, 0x6d, 0x2b, 0x4f, 0x4f, + 0x43, 0x4b, 0x66, 0x73, 0x79, 0x58, 0x64, 0x6d, 0x4c, 0x4c, 0x70, 0x46, + 0x79, 0x56, 0x6b, 0x34, 0x72, 0x72, 0x32, 0x47, 0x51, 0x34, 0x4b, 0x46, + 0x53, 0x51, 0x58, 0x51, 0x58, 0x79, 0x5a, 0x78, 0x78, 0x62, 0x4e, 0x46, + 0x64, 0x67, 0x6e, 0x43, 0x72, 0x76, 0x6d, 0x74, 0x6b, 0x49, 0x42, 0x51, + 0x62, 0x76, 0x6d, 0x6e, 0x42, 0x79, 0x50, 0x2b, 0x77, 0x6e, 0x0a, 0x65, + 0x7a, 0x68, 0x55, 0x51, 0x4b, 0x4c, 0x6b, 0x6d, 0x51, 0x50, 0x32, 0x53, + 0x6a, 0x37, 0x65, 0x52, 0x7a, 0x4c, 0x6f, 0x79, 0x58, 0x32, 0x71, 0x41, + 0x49, 0x75, 0x43, 0x46, 0x59, 0x62, 0x57, 0x41, 0x36, 0x44, 0x57, 0x53, + 0x39, 0x50, 0x4c, 0x56, 0x75, 0x67, 0x71, 0x53, 0x76, 0x4c, 0x65, 0x51, + 0x71, 0x7a, 0x31, 0x74, 0x70, 0x32, 0x66, 0x5a, 0x70, 0x68, 0x42, 0x64, + 0x6a, 0x66, 0x49, 0x0a, 0x6c, 0x65, 0x44, 0x42, 0x75, 0x7a, 0x59, 0x67, + 0x42, 0x30, 0x61, 0x63, 0x39, 0x79, 0x44, 0x48, 0x31, 0x6d, 0x65, 0x65, + 0x51, 0x4f, 0x43, 0x4e, 0x50, 0x61, 0x7a, 0x52, 0x41, 0x63, 0x57, 0x57, + 0x77, 0x75, 0x2b, 0x6f, 0x56, 0x57, 0x6d, 0x45, 0x42, 0x45, 0x6b, 0x50, + 0x44, 0x49, 0x31, 0x70, 0x68, 0x75, 0x6e, 0x44, 0x39, 0x33, 0x33, 0x47, + 0x31, 0x52, 0x63, 0x57, 0x64, 0x6d, 0x51, 0x55, 0x0a, 0x72, 0x37, 0x6b, + 0x48, 0x65, 0x50, 0x4d, 0x50, 0x77, 0x35, 0x70, 0x33, 0x42, 0x7a, 0x2b, + 0x71, 0x6a, 0x71, 0x61, 0x48, 0x2b, 0x36, 0x6f, 0x43, 0x54, 0x68, 0x5a, + 0x43, 0x6f, 0x2b, 0x59, 0x55, 0x39, 0x68, 0x51, 0x2f, 0x34, 0x2b, 0x48, + 0x77, 0x4d, 0x4e, 0x79, 0x38, 0x51, 0x73, 0x79, 0x6a, 0x61, 0x6d, 0x66, + 0x37, 0x31, 0x7a, 0x35, 0x67, 0x34, 0x43, 0x68, 0x65, 0x32, 0x34, 0x51, + 0x50, 0x0a, 0x66, 0x63, 0x6e, 0x30, 0x33, 0x57, 0x79, 0x2f, 0x5a, 0x52, + 0x61, 0x75, 0x53, 0x67, 0x73, 0x51, 0x6e, 0x79, 0x7a, 0x68, 0x72, 0x5a, + 0x7a, 0x39, 0x57, 0x4a, 0x35, 0x2b, 0x38, 0x77, 0x55, 0x71, 0x6a, 0x34, + 0x34, 0x51, 0x38, 0x45, 0x36, 0x70, 0x59, 0x52, 0x4c, 0x30, 0x68, 0x34, + 0x78, 0x65, 0x49, 0x73, 0x45, 0x4f, 0x76, 0x2b, 0x49, 0x77, 0x4e, 0x62, + 0x56, 0x59, 0x6d, 0x38, 0x57, 0x31, 0x0a, 0x62, 0x48, 0x5a, 0x62, 0x64, + 0x57, 0x31, 0x53, 0x65, 0x4d, 0x77, 0x70, 0x74, 0x45, 0x62, 0x37, 0x47, + 0x50, 0x67, 0x55, 0x61, 0x4f, 0x2f, 0x79, 0x4a, 0x7a, 0x2f, 0x68, 0x4b, + 0x71, 0x63, 0x72, 0x70, 0x49, 0x4a, 0x37, 0x2f, 0x49, 0x6f, 0x61, 0x41, + 0x54, 0x4b, 0x2b, 0x6a, 0x39, 0x70, 0x38, 0x32, 0x62, 0x33, 0x51, 0x69, + 0x32, 0x2f, 0x55, 0x54, 0x70, 0x71, 0x49, 0x44, 0x49, 0x67, 0x79, 0x0a, + 0x79, 0x5a, 0x41, 0x4c, 0x55, 0x6b, 0x36, 0x68, 0x57, 0x48, 0x39, 0x37, + 0x53, 0x6f, 0x4d, 0x61, 0x71, 0x6b, 0x68, 0x55, 0x6a, 0x73, 0x44, 0x38, + 0x36, 0x7a, 0x50, 0x32, 0x31, 0x4b, 0x48, 0x45, 0x4e, 0x35, 0x47, 0x35, + 0x6b, 0x58, 0x33, 0x36, 0x41, 0x77, 0x79, 0x66, 0x53, 0x45, 0x41, 0x6d, + 0x52, 0x76, 0x6c, 0x48, 0x63, 0x7a, 0x47, 0x6a, 0x78, 0x66, 0x71, 0x51, + 0x50, 0x42, 0x71, 0x7a, 0x0a, 0x4d, 0x63, 0x65, 0x39, 0x46, 0x53, 0x78, + 0x39, 0x4e, 0x2b, 0x52, 0x39, 0x62, 0x77, 0x2f, 0x56, 0x42, 0x73, 0x66, + 0x43, 0x63, 0x7a, 0x46, 0x35, 0x4c, 0x4b, 0x6d, 0x37, 0x52, 0x6f, 0x6a, + 0x48, 0x44, 0x35, 0x7a, 0x77, 0x4f, 0x62, 0x4f, 0x33, 0x36, 0x57, 0x7a, + 0x74, 0x75, 0x50, 0x61, 0x62, 0x62, 0x6f, 0x66, 0x7a, 0x30, 0x6d, 0x46, + 0x69, 0x56, 0x79, 0x75, 0x46, 0x33, 0x63, 0x4f, 0x62, 0x0a, 0x2f, 0x74, + 0x71, 0x47, 0x56, 0x56, 0x39, 0x33, 0x6e, 0x49, 0x72, 0x41, 0x42, 0x62, + 0x79, 0x2f, 0x44, 0x62, 0x42, 0x62, 0x49, 0x51, 0x4a, 0x58, 0x4c, 0x73, + 0x73, 0x74, 0x6e, 0x70, 0x77, 0x70, 0x6c, 0x5a, 0x62, 0x61, 0x5a, 0x6b, + 0x59, 0x71, 0x50, 0x76, 0x4f, 0x37, 0x47, 0x52, 0x56, 0x37, 0x30, 0x48, + 0x38, 0x77, 0x4e, 0x4f, 0x35, 0x4f, 0x66, 0x33, 0x4d, 0x43, 0x41, 0x77, + 0x45, 0x41, 0x0a, 0x41, 0x51, 0x4b, 0x43, 0x41, 0x67, 0x42, 0x6c, 0x78, + 0x33, 0x56, 0x62, 0x39, 0x2b, 0x53, 0x30, 0x73, 0x4c, 0x63, 0x57, 0x45, + 0x37, 0x48, 0x61, 0x42, 0x78, 0x66, 0x73, 0x71, 0x45, 0x63, 0x6e, 0x48, + 0x33, 0x32, 0x33, 0x6c, 0x49, 0x46, 0x55, 0x66, 0x56, 0x75, 0x4f, 0x4c, + 0x7a, 0x6d, 0x77, 0x4f, 0x6a, 0x69, 0x35, 0x39, 0x64, 0x6b, 0x78, 0x39, + 0x48, 0x6c, 0x30, 0x4e, 0x2f, 0x75, 0x66, 0x0a, 0x32, 0x6c, 0x66, 0x48, + 0x6f, 0x71, 0x5a, 0x56, 0x50, 0x34, 0x61, 0x32, 0x30, 0x38, 0x79, 0x71, + 0x6a, 0x4b, 0x65, 0x73, 0x6d, 0x6d, 0x6b, 0x66, 0x66, 0x57, 0x59, 0x64, + 0x48, 0x6d, 0x59, 0x2b, 0x74, 0x74, 0x55, 0x72, 0x79, 0x72, 0x35, 0x61, + 0x62, 0x2b, 0x4e, 0x55, 0x55, 0x67, 0x4c, 0x57, 0x33, 0x62, 0x38, 0x75, + 0x4a, 0x49, 0x6a, 0x76, 0x51, 0x64, 0x30, 0x39, 0x64, 0x6c, 0x63, 0x55, + 0x0a, 0x4a, 0x69, 0x5a, 0x75, 0x30, 0x6b, 0x56, 0x61, 0x37, 0x2f, 0x79, + 0x4d, 0x4d, 0x4e, 0x32, 0x32, 0x6d, 0x5a, 0x47, 0x4f, 0x34, 0x70, 0x36, + 0x52, 0x65, 0x6b, 0x50, 0x64, 0x63, 0x73, 0x41, 0x58, 0x55, 0x44, 0x6a, + 0x2b, 0x6d, 0x48, 0x6c, 0x2b, 0x73, 0x33, 0x66, 0x57, 0x64, 0x4a, 0x49, + 0x69, 0x58, 0x67, 0x49, 0x53, 0x36, 0x6d, 0x4b, 0x4c, 0x5a, 0x64, 0x61, + 0x5a, 0x51, 0x43, 0x46, 0x77, 0x0a, 0x57, 0x72, 0x31, 0x2f, 0x72, 0x38, + 0x2f, 0x54, 0x31, 0x2b, 0x64, 0x49, 0x52, 0x61, 0x76, 0x33, 0x5a, 0x53, + 0x6e, 0x68, 0x47, 0x75, 0x69, 0x61, 0x63, 0x34, 0x34, 0x72, 0x79, 0x70, + 0x38, 0x56, 0x69, 0x79, 0x34, 0x4e, 0x4a, 0x2b, 0x2f, 0x5a, 0x46, 0x6e, + 0x78, 0x4f, 0x46, 0x70, 0x76, 0x38, 0x4c, 0x63, 0x37, 0x6a, 0x30, 0x4d, + 0x7a, 0x31, 0x58, 0x42, 0x39, 0x4e, 0x6e, 0x38, 0x78, 0x41, 0x0a, 0x62, + 0x76, 0x41, 0x5a, 0x52, 0x78, 0x62, 0x76, 0x75, 0x4a, 0x4a, 0x76, 0x61, + 0x43, 0x61, 0x37, 0x46, 0x79, 0x54, 0x30, 0x77, 0x74, 0x4e, 0x4a, 0x79, + 0x64, 0x55, 0x36, 0x31, 0x6f, 0x48, 0x50, 0x66, 0x43, 0x30, 0x6b, 0x50, + 0x35, 0x68, 0x2b, 0x76, 0x4c, 0x4d, 0x5a, 0x32, 0x69, 0x73, 0x6e, 0x41, + 0x4b, 0x59, 0x76, 0x63, 0x30, 0x51, 0x55, 0x62, 0x4b, 0x58, 0x4c, 0x30, + 0x49, 0x33, 0x36, 0x0a, 0x55, 0x6e, 0x6d, 0x6c, 0x33, 0x59, 0x42, 0x79, + 0x4e, 0x4b, 0x59, 0x66, 0x31, 0x46, 0x35, 0x43, 0x79, 0x75, 0x38, 0x32, + 0x49, 0x54, 0x64, 0x50, 0x59, 0x37, 0x64, 0x43, 0x39, 0x39, 0x55, 0x6f, + 0x6e, 0x2f, 0x47, 0x35, 0x66, 0x52, 0x55, 0x56, 0x6e, 0x61, 0x6d, 0x58, + 0x34, 0x41, 0x2b, 0x4e, 0x62, 0x66, 0x47, 0x45, 0x68, 0x6c, 0x4a, 0x32, + 0x58, 0x58, 0x55, 0x51, 0x58, 0x54, 0x69, 0x68, 0x0a, 0x41, 0x77, 0x46, + 0x75, 0x5a, 0x48, 0x72, 0x54, 0x48, 0x41, 0x4a, 0x58, 0x39, 0x64, 0x6f, + 0x77, 0x34, 0x4c, 0x51, 0x51, 0x75, 0x49, 0x68, 0x44, 0x50, 0x4f, 0x59, + 0x55, 0x4e, 0x7a, 0x5a, 0x6f, 0x34, 0x57, 0x30, 0x34, 0x44, 0x44, 0x4a, + 0x2b, 0x62, 0x75, 0x66, 0x34, 0x67, 0x7a, 0x70, 0x4e, 0x68, 0x31, 0x39, + 0x59, 0x52, 0x65, 0x42, 0x44, 0x75, 0x7a, 0x67, 0x33, 0x43, 0x54, 0x62, + 0x59, 0x0a, 0x4b, 0x38, 0x52, 0x45, 0x6e, 0x58, 0x76, 0x4d, 0x59, 0x34, + 0x4b, 0x44, 0x36, 0x63, 0x69, 0x4d, 0x4f, 0x72, 0x61, 0x78, 0x4a, 0x53, + 0x31, 0x43, 0x49, 0x6d, 0x67, 0x53, 0x70, 0x68, 0x56, 0x70, 0x50, 0x37, + 0x54, 0x77, 0x4f, 0x4f, 0x6c, 0x62, 0x75, 0x78, 0x6a, 0x39, 0x76, 0x31, + 0x45, 0x63, 0x54, 0x41, 0x78, 0x34, 0x49, 0x68, 0x31, 0x38, 0x71, 0x72, + 0x71, 0x6c, 0x36, 0x32, 0x32, 0x56, 0x0a, 0x34, 0x45, 0x70, 0x34, 0x6c, + 0x62, 0x2f, 0x4a, 0x78, 0x57, 0x6e, 0x58, 0x4e, 0x48, 0x77, 0x2f, 0x7a, + 0x32, 0x64, 0x70, 0x64, 0x4d, 0x55, 0x6b, 0x4a, 0x4a, 0x66, 0x33, 0x6c, + 0x6d, 0x6e, 0x2b, 0x2f, 0x34, 0x5a, 0x56, 0x2b, 0x67, 0x4e, 0x42, 0x74, + 0x42, 0x79, 0x71, 0x4c, 0x52, 0x49, 0x50, 0x54, 0x47, 0x32, 0x6c, 0x37, + 0x4f, 0x34, 0x55, 0x6e, 0x52, 0x2f, 0x32, 0x69, 0x75, 0x71, 0x78, 0x0a, + 0x30, 0x77, 0x4b, 0x70, 0x59, 0x37, 0x54, 0x68, 0x34, 0x49, 0x66, 0x58, + 0x6a, 0x53, 0x56, 0x57, 0x36, 0x73, 0x4f, 0x63, 0x2b, 0x4a, 0x77, 0x37, + 0x64, 0x35, 0x68, 0x73, 0x56, 0x2f, 0x7a, 0x37, 0x41, 0x55, 0x52, 0x45, + 0x45, 0x4a, 0x34, 0x57, 0x58, 0x36, 0x4e, 0x4e, 0x70, 0x42, 0x77, 0x51, + 0x5a, 0x4e, 0x32, 0x56, 0x78, 0x48, 0x30, 0x37, 0x49, 0x78, 0x79, 0x76, + 0x6b, 0x33, 0x5a, 0x51, 0x0a, 0x58, 0x67, 0x56, 0x51, 0x58, 0x4e, 0x68, + 0x72, 0x79, 0x4e, 0x43, 0x49, 0x57, 0x70, 0x74, 0x2f, 0x66, 0x57, 0x69, + 0x76, 0x75, 0x50, 0x43, 0x7a, 0x77, 0x74, 0x32, 0x55, 0x74, 0x4f, 0x72, + 0x70, 0x2f, 0x35, 0x57, 0x43, 0x35, 0x37, 0x77, 0x31, 0x65, 0x65, 0x4d, + 0x73, 0x58, 0x63, 0x68, 0x72, 0x41, 0x51, 0x4b, 0x43, 0x41, 0x51, 0x45, + 0x41, 0x35, 0x4b, 0x48, 0x7a, 0x4c, 0x34, 0x76, 0x57, 0x0a, 0x42, 0x58, + 0x65, 0x66, 0x6f, 0x36, 0x2f, 0x4b, 0x68, 0x77, 0x49, 0x4a, 0x41, 0x37, + 0x79, 0x35, 0x54, 0x53, 0x67, 0x41, 0x62, 0x44, 0x68, 0x59, 0x70, 0x51, + 0x69, 0x34, 0x32, 0x4c, 0x6c, 0x74, 0x33, 0x37, 0x4e, 0x4d, 0x6e, 0x4a, + 0x63, 0x79, 0x4b, 0x54, 0x56, 0x6d, 0x79, 0x78, 0x70, 0x66, 0x59, 0x47, + 0x38, 0x65, 0x6e, 0x70, 0x31, 0x46, 0x71, 0x7a, 0x54, 0x56, 0x59, 0x74, + 0x63, 0x65, 0x0a, 0x5a, 0x49, 0x77, 0x33, 0x65, 0x39, 0x54, 0x35, 0x48, + 0x47, 0x4e, 0x71, 0x55, 0x6a, 0x70, 0x6f, 0x78, 0x43, 0x70, 0x35, 0x33, + 0x4b, 0x63, 0x44, 0x75, 0x38, 0x69, 0x47, 0x46, 0x36, 0x43, 0x51, 0x71, + 0x4a, 0x4d, 0x46, 0x72, 0x6b, 0x38, 0x47, 0x6b, 0x56, 0x51, 0x55, 0x38, + 0x31, 0x71, 0x58, 0x75, 0x57, 0x6f, 0x41, 0x50, 0x32, 0x79, 0x4a, 0x66, + 0x51, 0x53, 0x6d, 0x42, 0x75, 0x68, 0x67, 0x0a, 0x50, 0x64, 0x48, 0x4b, + 0x55, 0x4b, 0x31, 0x68, 0x47, 0x31, 0x6f, 0x36, 0x32, 0x44, 0x48, 0x32, + 0x67, 0x57, 0x33, 0x65, 0x6e, 0x37, 0x6a, 0x33, 0x70, 0x78, 0x43, 0x70, + 0x34, 0x4e, 0x68, 0x70, 0x39, 0x49, 0x70, 0x2f, 0x35, 0x39, 0x67, 0x30, + 0x4c, 0x73, 0x50, 0x44, 0x42, 0x67, 0x36, 0x6e, 0x59, 0x69, 0x38, 0x78, + 0x35, 0x61, 0x44, 0x4e, 0x6f, 0x59, 0x6d, 0x76, 0x45, 0x56, 0x45, 0x41, + 0x0a, 0x35, 0x46, 0x52, 0x7a, 0x37, 0x75, 0x2f, 0x77, 0x77, 0x67, 0x79, + 0x6d, 0x47, 0x49, 0x50, 0x67, 0x57, 0x70, 0x59, 0x47, 0x33, 0x63, 0x35, + 0x6b, 0x73, 0x31, 0x6e, 0x51, 0x41, 0x5a, 0x47, 0x53, 0x61, 0x37, 0x64, + 0x4c, 0x46, 0x74, 0x44, 0x4d, 0x45, 0x59, 0x4f, 0x78, 0x34, 0x37, 0x36, + 0x77, 0x42, 0x75, 0x48, 0x41, 0x56, 0x53, 0x57, 0x53, 0x41, 0x70, 0x64, + 0x55, 0x4d, 0x38, 0x56, 0x77, 0x0a, 0x6b, 0x2f, 0x6b, 0x63, 0x66, 0x2f, + 0x76, 0x61, 0x6f, 0x33, 0x4f, 0x7a, 0x71, 0x65, 0x33, 0x6f, 0x73, 0x37, + 0x37, 0x43, 0x6f, 0x46, 0x52, 0x77, 0x5a, 0x4d, 0x4e, 0x61, 0x7a, 0x2f, + 0x46, 0x2f, 0x7a, 0x35, 0x53, 0x77, 0x63, 0x47, 0x77, 0x42, 0x6f, 0x65, + 0x34, 0x51, 0x67, 0x69, 0x42, 0x6f, 0x33, 0x39, 0x4b, 0x58, 0x52, 0x33, + 0x5a, 0x63, 0x57, 0x33, 0x48, 0x63, 0x68, 0x76, 0x7a, 0x7a, 0x0a, 0x46, + 0x38, 0x54, 0x36, 0x6d, 0x38, 0x30, 0x78, 0x61, 0x7a, 0x67, 0x54, 0x6a, + 0x51, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, 0x77, 0x2f, 0x66, 0x55, 0x62, + 0x53, 0x78, 0x69, 0x6b, 0x33, 0x78, 0x38, 0x5a, 0x70, 0x59, 0x50, 0x71, + 0x53, 0x4a, 0x5a, 0x6f, 0x62, 0x30, 0x58, 0x76, 0x4b, 0x6b, 0x74, 0x67, + 0x37, 0x52, 0x35, 0x44, 0x4e, 0x32, 0x57, 0x2f, 0x65, 0x4c, 0x6e, 0x46, + 0x34, 0x63, 0x55, 0x0a, 0x45, 0x33, 0x70, 0x2b, 0x38, 0x5a, 0x33, 0x38, + 0x4c, 0x57, 0x76, 0x37, 0x6c, 0x36, 0x72, 0x72, 0x4e, 0x47, 0x4d, 0x41, + 0x4e, 0x48, 0x6a, 0x59, 0x44, 0x47, 0x53, 0x5a, 0x2f, 0x67, 0x79, 0x2b, + 0x48, 0x31, 0x6f, 0x75, 0x38, 0x69, 0x72, 0x54, 0x2b, 0x55, 0x64, 0x42, + 0x6c, 0x6c, 0x58, 0x4b, 0x70, 0x71, 0x44, 0x35, 0x39, 0x5a, 0x56, 0x55, + 0x73, 0x2b, 0x6f, 0x76, 0x4e, 0x73, 0x7a, 0x52, 0x0a, 0x68, 0x50, 0x37, + 0x77, 0x6c, 0x4c, 0x4d, 0x30, 0x42, 0x67, 0x45, 0x44, 0x58, 0x33, 0x6b, + 0x6e, 0x36, 0x78, 0x52, 0x41, 0x63, 0x5a, 0x2b, 0x74, 0x6b, 0x59, 0x53, + 0x64, 0x57, 0x79, 0x63, 0x6f, 0x2b, 0x5a, 0x4b, 0x4f, 0x48, 0x63, 0x66, + 0x71, 0x75, 0x54, 0x2f, 0x72, 0x4a, 0x34, 0x4b, 0x35, 0x42, 0x43, 0x65, + 0x75, 0x33, 0x31, 0x48, 0x6f, 0x78, 0x74, 0x51, 0x6c, 0x78, 0x4a, 0x44, + 0x51, 0x0a, 0x52, 0x36, 0x4b, 0x6a, 0x42, 0x4e, 0x2b, 0x61, 0x63, 0x37, + 0x63, 0x33, 0x6c, 0x32, 0x57, 0x52, 0x47, 0x7a, 0x2f, 0x70, 0x4b, 0x6a, + 0x30, 0x47, 0x39, 0x35, 0x32, 0x71, 0x42, 0x43, 0x72, 0x58, 0x4c, 0x71, + 0x32, 0x32, 0x30, 0x54, 0x4e, 0x71, 0x44, 0x4b, 0x44, 0x44, 0x4c, 0x50, + 0x54, 0x32, 0x5a, 0x46, 0x41, 0x6b, 0x65, 0x48, 0x77, 0x7a, 0x50, 0x58, + 0x32, 0x65, 0x65, 0x71, 0x53, 0x75, 0x0a, 0x62, 0x34, 0x6b, 0x48, 0x45, + 0x49, 0x44, 0x7a, 0x72, 0x66, 0x4d, 0x55, 0x44, 0x35, 0x63, 0x6d, 0x54, + 0x73, 0x42, 0x50, 0x71, 0x30, 0x4c, 0x59, 0x75, 0x6c, 0x67, 0x75, 0x42, + 0x59, 0x78, 0x5a, 0x57, 0x73, 0x50, 0x63, 0x6d, 0x2b, 0x66, 0x57, 0x63, + 0x53, 0x6e, 0x77, 0x39, 0x73, 0x32, 0x4c, 0x2b, 0x45, 0x4c, 0x32, 0x37, + 0x52, 0x39, 0x39, 0x49, 0x67, 0x7a, 0x43, 0x76, 0x64, 0x47, 0x49, 0x0a, + 0x71, 0x52, 0x45, 0x75, 0x2f, 0x69, 0x65, 0x5a, 0x66, 0x79, 0x6d, 0x31, + 0x6e, 0x67, 0x65, 0x58, 0x33, 0x56, 0x70, 0x73, 0x50, 0x4e, 0x54, 0x36, + 0x77, 0x43, 0x55, 0x58, 0x4c, 0x61, 0x63, 0x70, 0x79, 0x4a, 0x46, 0x6d, + 0x59, 0x57, 0x71, 0x65, 0x2f, 0x77, 0x4b, 0x43, 0x41, 0x51, 0x42, 0x44, + 0x75, 0x49, 0x53, 0x6a, 0x7a, 0x4c, 0x4f, 0x30, 0x49, 0x74, 0x36, 0x79, + 0x53, 0x56, 0x75, 0x66, 0x0a, 0x36, 0x63, 0x5a, 0x70, 0x79, 0x50, 0x6a, + 0x4b, 0x46, 0x64, 0x4d, 0x71, 0x4f, 0x76, 0x5a, 0x6d, 0x79, 0x39, 0x4b, + 0x55, 0x76, 0x7a, 0x67, 0x41, 0x54, 0x73, 0x65, 0x65, 0x69, 0x6c, 0x70, + 0x64, 0x51, 0x6d, 0x67, 0x55, 0x4f, 0x4e, 0x65, 0x50, 0x5a, 0x4e, 0x71, + 0x59, 0x2b, 0x4e, 0x53, 0x75, 0x42, 0x5a, 0x51, 0x2f, 0x46, 0x71, 0x44, + 0x31, 0x2f, 0x32, 0x4a, 0x66, 0x31, 0x35, 0x47, 0x43, 0x0a, 0x43, 0x79, + 0x42, 0x76, 0x41, 0x73, 0x59, 0x4e, 0x64, 0x4e, 0x64, 0x72, 0x75, 0x44, + 0x79, 0x75, 0x33, 0x70, 0x4a, 0x35, 0x5a, 0x53, 0x48, 0x30, 0x44, 0x4c, + 0x68, 0x65, 0x44, 0x53, 0x4a, 0x51, 0x34, 0x61, 0x72, 0x69, 0x2b, 0x69, + 0x35, 0x2b, 0x79, 0x52, 0x73, 0x52, 0x72, 0x6b, 0x42, 0x37, 0x4f, 0x32, + 0x6c, 0x43, 0x47, 0x6f, 0x71, 0x48, 0x52, 0x53, 0x43, 0x38, 0x44, 0x4e, + 0x37, 0x36, 0x0a, 0x6a, 0x78, 0x74, 0x6d, 0x39, 0x6b, 0x57, 0x68, 0x79, + 0x4c, 0x31, 0x73, 0x61, 0x67, 0x6c, 0x51, 0x2f, 0x75, 0x71, 0x53, 0x6f, + 0x77, 0x65, 0x66, 0x57, 0x33, 0x62, 0x50, 0x59, 0x59, 0x62, 0x7a, 0x59, + 0x79, 0x44, 0x64, 0x4a, 0x6e, 0x49, 0x46, 0x37, 0x78, 0x7a, 0x45, 0x65, + 0x34, 0x44, 0x74, 0x48, 0x43, 0x38, 0x79, 0x33, 0x64, 0x39, 0x35, 0x77, + 0x6c, 0x6f, 0x6b, 0x71, 0x41, 0x57, 0x55, 0x0a, 0x4e, 0x4e, 0x4c, 0x74, + 0x36, 0x41, 0x49, 0x49, 0x55, 0x75, 0x2f, 0x74, 0x75, 0x7a, 0x69, 0x77, + 0x74, 0x79, 0x57, 0x5a, 0x6b, 0x56, 0x6a, 0x68, 0x64, 0x77, 0x56, 0x53, + 0x32, 0x4c, 0x33, 0x5a, 0x59, 0x6a, 0x7a, 0x4b, 0x7a, 0x4b, 0x79, 0x76, + 0x48, 0x53, 0x63, 0x76, 0x45, 0x56, 0x58, 0x53, 0x56, 0x71, 0x69, 0x6d, + 0x50, 0x52, 0x45, 0x2f, 0x67, 0x30, 0x59, 0x68, 0x30, 0x71, 0x50, 0x4d, + 0x0a, 0x54, 0x6e, 0x55, 0x6c, 0x48, 0x45, 0x63, 0x56, 0x46, 0x71, 0x66, + 0x6e, 0x66, 0x5a, 0x74, 0x63, 0x2f, 0x56, 0x42, 0x6b, 0x7a, 0x34, 0x2b, + 0x67, 0x47, 0x4e, 0x61, 0x36, 0x4e, 0x67, 0x4f, 0x31, 0x78, 0x79, 0x49, + 0x30, 0x71, 0x51, 0x7a, 0x6e, 0x58, 0x79, 0x56, 0x59, 0x45, 0x59, 0x4d, + 0x42, 0x77, 0x78, 0x51, 0x6a, 0x46, 0x38, 0x47, 0x5a, 0x68, 0x4d, 0x73, + 0x47, 0x59, 0x78, 0x30, 0x45, 0x0a, 0x4f, 0x54, 0x50, 0x78, 0x41, 0x6f, + 0x49, 0x42, 0x41, 0x51, 0x43, 0x36, 0x67, 0x31, 0x72, 0x4c, 0x32, 0x59, + 0x32, 0x73, 0x76, 0x37, 0x4f, 0x4b, 0x30, 0x39, 0x48, 0x74, 0x38, 0x51, + 0x4b, 0x4c, 0x2f, 0x48, 0x50, 0x6d, 0x48, 0x4d, 0x4a, 0x7a, 0x38, 0x73, + 0x57, 0x76, 0x61, 0x41, 0x74, 0x34, 0x63, 0x6f, 0x66, 0x74, 0x6a, 0x61, + 0x4f, 0x65, 0x38, 0x6c, 0x78, 0x31, 0x7a, 0x42, 0x36, 0x69, 0x0a, 0x67, + 0x48, 0x37, 0x42, 0x6d, 0x47, 0x77, 0x70, 0x76, 0x50, 0x77, 0x4a, 0x39, + 0x4f, 0x58, 0x7a, 0x7a, 0x5a, 0x2f, 0x2b, 0x2b, 0x74, 0x50, 0x4d, 0x39, + 0x55, 0x46, 0x76, 0x50, 0x54, 0x48, 0x6f, 0x74, 0x46, 0x67, 0x4a, 0x4a, + 0x48, 0x67, 0x75, 0x35, 0x56, 0x46, 0x68, 0x32, 0x70, 0x48, 0x32, 0x64, + 0x72, 0x49, 0x66, 0x48, 0x77, 0x74, 0x65, 0x34, 0x47, 0x48, 0x4c, 0x74, + 0x55, 0x2b, 0x54, 0x0a, 0x55, 0x30, 0x32, 0x4a, 0x68, 0x62, 0x39, 0x6e, + 0x62, 0x79, 0x76, 0x79, 0x75, 0x4c, 0x34, 0x79, 0x6e, 0x4a, 0x78, 0x44, + 0x6b, 0x46, 0x37, 0x67, 0x51, 0x67, 0x73, 0x2b, 0x37, 0x76, 0x4a, 0x4a, + 0x42, 0x44, 0x76, 0x6b, 0x71, 0x2f, 0x63, 0x35, 0x72, 0x43, 0x63, 0x30, + 0x35, 0x7a, 0x6c, 0x38, 0x57, 0x35, 0x66, 0x69, 0x6f, 0x4d, 0x32, 0x4c, + 0x45, 0x4a, 0x44, 0x46, 0x5a, 0x36, 0x67, 0x52, 0x0a, 0x38, 0x46, 0x52, + 0x5a, 0x70, 0x4e, 0x4a, 0x5a, 0x74, 0x42, 0x64, 0x51, 0x47, 0x79, 0x74, + 0x52, 0x61, 0x74, 0x37, 0x51, 0x2f, 0x70, 0x45, 0x31, 0x48, 0x53, 0x4b, + 0x39, 0x73, 0x34, 0x69, 0x41, 0x6f, 0x6c, 0x57, 0x41, 0x71, 0x56, 0x6b, + 0x45, 0x6d, 0x6e, 0x35, 0x4c, 0x71, 0x6a, 0x48, 0x2b, 0x6d, 0x56, 0x76, + 0x48, 0x63, 0x49, 0x75, 0x52, 0x48, 0x6b, 0x4c, 0x36, 0x41, 0x7a, 0x46, + 0x70, 0x0a, 0x34, 0x73, 0x75, 0x36, 0x46, 0x75, 0x72, 0x66, 0x30, 0x77, + 0x74, 0x41, 0x66, 0x65, 0x76, 0x57, 0x56, 0x32, 0x7a, 0x64, 0x33, 0x50, + 0x6d, 0x43, 0x65, 0x4a, 0x32, 0x30, 0x53, 0x52, 0x43, 0x42, 0x50, 0x69, + 0x44, 0x59, 0x56, 0x55, 0x4f, 0x64, 0x73, 0x75, 0x5a, 0x66, 0x51, 0x7a, + 0x51, 0x4f, 0x76, 0x30, 0x52, 0x74, 0x38, 0x30, 0x70, 0x31, 0x56, 0x79, + 0x52, 0x35, 0x77, 0x75, 0x6a, 0x46, 0x0a, 0x37, 0x63, 0x57, 0x73, 0x74, + 0x5a, 0x39, 0x6c, 0x6a, 0x74, 0x55, 0x72, 0x68, 0x4b, 0x31, 0x76, 0x53, + 0x52, 0x33, 0x70, 0x56, 0x6f, 0x74, 0x66, 0x47, 0x48, 0x76, 0x76, 0x78, + 0x64, 0x47, 0x7a, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x51, 0x43, 0x36, 0x59, + 0x70, 0x5a, 0x64, 0x71, 0x4d, 0x38, 0x7a, 0x68, 0x32, 0x45, 0x41, 0x30, + 0x75, 0x69, 0x4e, 0x55, 0x4f, 0x55, 0x2f, 0x5a, 0x63, 0x6d, 0x54, 0x0a, + 0x31, 0x51, 0x6c, 0x4b, 0x67, 0x2b, 0x69, 0x58, 0x4b, 0x66, 0x57, 0x5a, + 0x72, 0x2b, 0x61, 0x64, 0x50, 0x56, 0x50, 0x6b, 0x31, 0x64, 0x54, 0x39, + 0x79, 0x55, 0x78, 0x2f, 0x6e, 0x4e, 0x77, 0x6e, 0x66, 0x43, 0x62, 0x4b, + 0x63, 0x54, 0x45, 0x79, 0x56, 0x74, 0x37, 0x65, 0x41, 0x7a, 0x69, 0x69, + 0x4c, 0x73, 0x5a, 0x77, 0x36, 0x65, 0x58, 0x4f, 0x50, 0x4b, 0x39, 0x55, + 0x38, 0x7a, 0x76, 0x43, 0x0a, 0x4a, 0x50, 0x55, 0x71, 0x67, 0x67, 0x44, + 0x5a, 0x33, 0x2b, 0x6b, 0x62, 0x6b, 0x34, 0x6a, 0x50, 0x44, 0x52, 0x41, + 0x73, 0x30, 0x4a, 0x74, 0x49, 0x6b, 0x43, 0x30, 0x62, 0x75, 0x48, 0x7a, + 0x6b, 0x58, 0x46, 0x30, 0x72, 0x67, 0x41, 0x34, 0x62, 0x4a, 0x7a, 0x75, + 0x55, 0x47, 0x30, 0x44, 0x45, 0x73, 0x58, 0x4a, 0x58, 0x59, 0x71, 0x48, + 0x6f, 0x4f, 0x6d, 0x31, 0x4f, 0x56, 0x4a, 0x66, 0x73, 0x0a, 0x78, 0x6f, + 0x4c, 0x50, 0x74, 0x68, 0x71, 0x55, 0x44, 0x30, 0x44, 0x32, 0x73, 0x43, + 0x59, 0x37, 0x46, 0x62, 0x4a, 0x58, 0x67, 0x4e, 0x4d, 0x61, 0x33, 0x76, + 0x59, 0x62, 0x31, 0x39, 0x6b, 0x78, 0x65, 0x4a, 0x37, 0x76, 0x37, 0x70, + 0x69, 0x42, 0x72, 0x69, 0x68, 0x4c, 0x6b, 0x35, 0x4c, 0x57, 0x64, 0x41, + 0x78, 0x32, 0x6c, 0x53, 0x56, 0x30, 0x6c, 0x61, 0x46, 0x45, 0x39, 0x4f, + 0x38, 0x43, 0x0a, 0x68, 0x36, 0x48, 0x61, 0x5a, 0x65, 0x52, 0x39, 0x4a, + 0x49, 0x69, 0x42, 0x79, 0x63, 0x5a, 0x39, 0x71, 0x48, 0x49, 0x58, 0x50, + 0x50, 0x43, 0x79, 0x5a, 0x57, 0x51, 0x4f, 0x6c, 0x6c, 0x43, 0x49, 0x56, + 0x69, 0x31, 0x71, 0x53, 0x51, 0x6b, 0x70, 0x51, 0x4e, 0x34, 0x76, 0x59, + 0x4e, 0x68, 0x63, 0x2f, 0x49, 0x45, 0x37, 0x4d, 0x76, 0x51, 0x31, 0x4a, + 0x42, 0x61, 0x4f, 0x62, 0x6c, 0x4a, 0x48, 0x0a, 0x63, 0x73, 0x63, 0x49, + 0x4c, 0x43, 0x38, 0x61, 0x69, 0x4a, 0x56, 0x46, 0x4f, 0x53, 0x5a, 0x6e, + 0x4e, 0x6a, 0x4b, 0x2f, 0x66, 0x6c, 0x6f, 0x72, 0x78, 0x74, 0x54, 0x4f, + 0x44, 0x44, 0x39, 0x49, 0x47, 0x33, 0x67, 0x48, 0x66, 0x78, 0x54, 0x52, + 0x31, 0x73, 0x66, 0x61, 0x4c, 0x76, 0x35, 0x7a, 0x43, 0x51, 0x66, 0x39, + 0x77, 0x4f, 0x49, 0x35, 0x52, 0x49, 0x2f, 0x78, 0x0a, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x52, 0x53, 0x41, 0x20, 0x50, 0x52, + 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a +}; + +unsigned char client_key_der[] = { + 0x30, 0x82, 0x09, 0x29, 0x02, 0x01, 0x00, 0x02, 0x82, 0x02, 0x01, 0x00, + 0xAF, 0x04, 0xB2, 0x36, 0x78, 0x5F, 0xBC, 0x38, 0x9C, 0x18, 0x93, 0x81, + 0x7D, 0x44, 0x0B, 0x18, 0x4A, 0x4A, 0x0B, 0x6F, 0xCD, 0xDB, 0x94, 0xE8, + 0xD8, 0xA7, 0xFA, 0xEB, 0x39, 0x03, 0x6F, 0x79, 0xB8, 0x20, 0xFC, 0x0B, + 0x71, 0xCC, 0x46, 0x58, 0x75, 0xAE, 0x7B, 0x18, 0xA9, 0x9A, 0xF0, 0x30, + 0x17, 0xE5, 0xF6, 0xE8, 0x51, 0xB3, 0xE5, 0xF2, 0x5D, 0xAB, 0xC1, 0x61, + 0xC2, 0x5A, 0xBE, 0x9E, 0xA8, 0xF8, 0x3E, 0x19, 0x7F, 0x43, 0xEC, 0x14, + 0x06, 0x54, 0x3E, 0x5C, 0xC8, 0xEA, 0x22, 0x9C, 0x32, 0xCE, 0x96, 0x66, + 0xE0, 0x79, 0xBE, 0x38, 0xE0, 0x8A, 0x7E, 0xCC, 0x97, 0x76, 0x62, 0xCB, + 0xA4, 0x5C, 0x95, 0x93, 0x8A, 0xEB, 0xD8, 0x64, 0x38, 0x28, 0x54, 0x90, + 0x5D, 0x05, 0xF2, 0x67, 0x1C, 0x5B, 0x34, 0x57, 0x60, 0x9C, 0x2A, 0xEF, + 0x9A, 0xD9, 0x08, 0x05, 0x06, 0xEF, 0x9A, 0x70, 0x72, 0x3F, 0xEC, 0x27, + 0x7B, 0x38, 0x54, 0x40, 0xA2, 0xE4, 0x99, 0x03, 0xF6, 0x4A, 0x3E, 0xDE, + 0x47, 0x32, 0xE8, 0xC9, 0x7D, 0xAA, 0x00, 0x8B, 0x82, 0x15, 0x86, 0xD6, + 0x03, 0xA0, 0xD6, 0x4B, 0xD3, 0xCB, 0x56, 0xE8, 0x2A, 0x4A, 0xF2, 0xDE, + 0x42, 0xAC, 0xF5, 0xB6, 0x9D, 0x9F, 0x66, 0x98, 0x41, 0x76, 0x37, 0xC8, + 0x95, 0xE0, 0xC1, 0xBB, 0x36, 0x20, 0x07, 0x46, 0x9C, 0xF7, 0x20, 0xC7, + 0xD6, 0x67, 0x9E, 0x40, 0xE0, 0x8D, 0x3D, 0xAC, 0xD1, 0x01, 0xC5, 0x96, + 0xC2, 0xEF, 0xA8, 0x55, 0x69, 0x84, 0x04, 0x49, 0x0F, 0x0C, 0x8D, 0x69, + 0x86, 0xE9, 0xC3, 0xF7, 0x7D, 0xC6, 0xD5, 0x17, 0x16, 0x76, 0x64, 0x14, + 0xAF, 0xB9, 0x07, 0x78, 0xF3, 0x0F, 0xC3, 0x9A, 0x77, 0x07, 0x3F, 0xAA, + 0x8E, 0xA6, 0x87, 0xFB, 0xAA, 0x02, 0x4E, 0x16, 0x42, 0xA3, 0xE6, 0x14, + 0xF6, 0x14, 0x3F, 0xE3, 0xE1, 0xF0, 0x30, 0xDC, 0xBC, 0x42, 0xCC, 0xA3, + 0x6A, 0x67, 0xFB, 0xD7, 0x3E, 0x60, 0xE0, 0x28, 0x5E, 0xDB, 0x84, 0x0F, + 0x7D, 0xC9, 0xF4, 0xDD, 0x6C, 0xBF, 0x65, 0x16, 0xAE, 0x4A, 0x0B, 0x10, + 0x9F, 0x2C, 0xE1, 0xAD, 0x9C, 0xFD, 0x58, 0x9E, 0x7E, 0xF3, 0x05, 0x2A, + 0x8F, 0x8E, 0x10, 0xF0, 0x4E, 0xA9, 0x61, 0x12, 0xF4, 0x87, 0x8C, 0x5E, + 0x22, 0xC1, 0x0E, 0xBF, 0xE2, 0x30, 0x35, 0xB5, 0x58, 0x9B, 0xC5, 0xB5, + 0x6C, 0x76, 0x5B, 0x75, 0x6D, 0x52, 0x78, 0xCC, 0x29, 0xB4, 0x46, 0xFB, + 0x18, 0xF8, 0x14, 0x68, 0xEF, 0xF2, 0x27, 0x3F, 0xE1, 0x2A, 0xA7, 0x2B, + 0xA4, 0x82, 0x7B, 0xFC, 0x8A, 0x1A, 0x01, 0x32, 0xBE, 0x8F, 0xDA, 0x7C, + 0xD9, 0xBD, 0xD0, 0x8B, 0x6F, 0xD4, 0x4E, 0x9A, 0x88, 0x0C, 0x88, 0x32, + 0xC9, 0x90, 0x0B, 0x52, 0x4E, 0xA1, 0x58, 0x7F, 0x7B, 0x4A, 0x83, 0x1A, + 0xAA, 0x48, 0x54, 0x8E, 0xC0, 0xFC, 0xEB, 0x33, 0xF6, 0xD4, 0xA1, 0xC4, + 0x37, 0x91, 0xB9, 0x91, 0x7D, 0xFA, 0x03, 0x0C, 0x9F, 0x48, 0x40, 0x26, + 0x46, 0xF9, 0x47, 0x73, 0x31, 0xA3, 0xC5, 0xFA, 0x90, 0x3C, 0x1A, 0xB3, + 0x31, 0xC7, 0xBD, 0x15, 0x2C, 0x7D, 0x37, 0xE4, 0x7D, 0x6F, 0x0F, 0xD5, + 0x06, 0xC7, 0xC2, 0x73, 0x31, 0x79, 0x2C, 0xA9, 0xBB, 0x46, 0x88, 0xC7, + 0x0F, 0x9C, 0xF0, 0x39, 0xB3, 0xB7, 0xE9, 0x6C, 0xED, 0xB8, 0xF6, 0x9B, + 0x6E, 0x87, 0xF3, 0xD2, 0x61, 0x62, 0x57, 0x2B, 0x85, 0xDD, 0xC3, 0x9B, + 0xFE, 0xDA, 0x86, 0x55, 0x5F, 0x77, 0x9C, 0x8A, 0xC0, 0x05, 0xBC, 0xBF, + 0x0D, 0xB0, 0x5B, 0x21, 0x02, 0x57, 0x2E, 0xCB, 0x2D, 0x9E, 0x9C, 0x29, + 0x95, 0x96, 0xDA, 0x66, 0x46, 0x2A, 0x3E, 0xF3, 0xBB, 0x19, 0x15, 0x7B, + 0xD0, 0x7F, 0x30, 0x34, 0xEE, 0x4E, 0x7F, 0x73, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x02, 0x00, 0x65, 0xC7, 0x75, 0x5B, 0xF7, 0xE4, 0xB4, + 0xB0, 0xB7, 0x16, 0x13, 0xB1, 0xDA, 0x07, 0x17, 0xEC, 0xA8, 0x47, 0x27, + 0x1F, 0x7D, 0xB7, 0x94, 0x81, 0x54, 0x7D, 0x5B, 0x8E, 0x2F, 0x39, 0xB0, + 0x3A, 0x38, 0xB9, 0xF5, 0xD9, 0x31, 0xF4, 0x79, 0x74, 0x37, 0xFB, 0x9F, + 0xDA, 0x57, 0xC7, 0xA2, 0xA6, 0x55, 0x3F, 0x86, 0xB6, 0xD3, 0xCC, 0xAA, + 0x8C, 0xA7, 0xAC, 0x9A, 0x69, 0x1F, 0x7D, 0x66, 0x1D, 0x1E, 0x66, 0x3E, + 0xB6, 0xD5, 0x2B, 0xCA, 0xBE, 0x5A, 0x6F, 0xE3, 0x54, 0x52, 0x02, 0xD6, + 0xDD, 0xBF, 0x2E, 0x24, 0x88, 0xEF, 0x41, 0xDD, 0x3D, 0x76, 0x57, 0x14, + 0x26, 0x26, 0x6E, 0xD2, 0x45, 0x5A, 0xEF, 0xFC, 0x8C, 0x30, 0xDD, 0xB6, + 0x99, 0x91, 0x8E, 0xE2, 0x9E, 0x91, 0x7A, 0x43, 0xDD, 0x72, 0xC0, 0x17, + 0x50, 0x38, 0xFE, 0x98, 0x79, 0x7E, 0xB3, 0x77, 0xD6, 0x74, 0x92, 0x22, + 0x5E, 0x02, 0x12, 0xEA, 0x62, 0x8B, 0x65, 0xD6, 0x99, 0x40, 0x21, 0x70, + 0x5A, 0xBD, 0x7F, 0xAF, 0xCF, 0xD3, 0xD7, 0xE7, 0x48, 0x45, 0xAB, 0xF7, + 0x65, 0x29, 0xE1, 0x1A, 0xE8, 0x9A, 0x73, 0x8E, 0x2B, 0xCA, 0x9F, 0x15, + 0x8B, 0x2E, 0x0D, 0x27, 0xEF, 0xD9, 0x16, 0x7C, 0x4E, 0x16, 0x9B, 0xFC, + 0x2D, 0xCE, 0xE3, 0xD0, 0xCC, 0xF5, 0x5C, 0x1F, 0x4D, 0x9F, 0xCC, 0x40, + 0x6E, 0xF0, 0x19, 0x47, 0x16, 0xEF, 0xB8, 0x92, 0x6F, 0x68, 0x26, 0xBB, + 0x17, 0x24, 0xF4, 0xC2, 0xD3, 0x49, 0xC9, 0xD5, 0x3A, 0xD6, 0x81, 0xCF, + 0x7C, 0x2D, 0x24, 0x3F, 0x98, 0x7E, 0xBC, 0xB3, 0x19, 0xDA, 0x2B, 0x27, + 0x00, 0xA6, 0x2F, 0x73, 0x44, 0x14, 0x6C, 0xA5, 0xCB, 0xD0, 0x8D, 0xFA, + 0x52, 0x79, 0xA5, 0xDD, 0x80, 0x72, 0x34, 0xA6, 0x1F, 0xD4, 0x5E, 0x42, + 0xCA, 0xEF, 0x36, 0x21, 0x37, 0x4F, 0x63, 0xB7, 0x42, 0xF7, 0xD5, 0x28, + 0x9F, 0xF1, 0xB9, 0x7D, 0x15, 0x15, 0x9D, 0xA9, 0x97, 0xE0, 0x0F, 0x8D, + 0x6D, 0xF1, 0x84, 0x86, 0x52, 0x76, 0x5D, 0x75, 0x10, 0x5D, 0x38, 0xA1, + 0x03, 0x01, 0x6E, 0x64, 0x7A, 0xD3, 0x1C, 0x02, 0x57, 0xF5, 0xDA, 0x30, + 0xE0, 0xB4, 0x10, 0xB8, 0x88, 0x43, 0x3C, 0xE6, 0x14, 0x37, 0x36, 0x68, + 0xE1, 0x6D, 0x38, 0x0C, 0x32, 0x7E, 0x6E, 0xE7, 0xF8, 0x83, 0x3A, 0x4D, + 0x87, 0x5F, 0x58, 0x45, 0xE0, 0x43, 0xBB, 0x38, 0x37, 0x09, 0x36, 0xD8, + 0x2B, 0xC4, 0x44, 0x9D, 0x7B, 0xCC, 0x63, 0x82, 0x83, 0xE9, 0xC8, 0x8C, + 0x3A, 0xB6, 0xB1, 0x25, 0x2D, 0x42, 0x22, 0x68, 0x12, 0xA6, 0x15, 0x69, + 0x3F, 0xB4, 0xF0, 0x38, 0xE9, 0x5B, 0xBB, 0x18, 0xFD, 0xBF, 0x51, 0x1C, + 0x4C, 0x0C, 0x78, 0x22, 0x1D, 0x7C, 0xAA, 0xBA, 0xA5, 0xEB, 0x6D, 0x95, + 0xE0, 0x4A, 0x78, 0x95, 0xBF, 0xC9, 0xC5, 0x69, 0xD7, 0x34, 0x7C, 0x3F, + 0xCF, 0x67, 0x69, 0x74, 0xC5, 0x24, 0x24, 0x97, 0xF7, 0x96, 0x69, 0xFE, + 0xFF, 0x86, 0x55, 0xFA, 0x03, 0x41, 0xB4, 0x1C, 0xAA, 0x2D, 0x12, 0x0F, + 0x4C, 0x6D, 0xA5, 0xEC, 0xEE, 0x14, 0x9D, 0x1F, 0xF6, 0x8A, 0xEA, 0xB1, + 0xD3, 0x02, 0xA9, 0x63, 0xB4, 0xE1, 0xE0, 0x87, 0xD7, 0x8D, 0x25, 0x56, + 0xEA, 0xC3, 0x9C, 0xF8, 0x9C, 0x3B, 0x77, 0x98, 0x6C, 0x57, 0xFC, 0xFB, + 0x01, 0x44, 0x44, 0x10, 0x9E, 0x16, 0x5F, 0xA3, 0x4D, 0xA4, 0x1C, 0x10, + 0x64, 0xDD, 0x95, 0xC4, 0x7D, 0x3B, 0x23, 0x1C, 0xAF, 0x93, 0x76, 0x50, + 0x5E, 0x05, 0x50, 0x5C, 0xD8, 0x6B, 0xC8, 0xD0, 0x88, 0x5A, 0x9B, 0x7F, + 0x7D, 0x68, 0xAF, 0xB8, 0xF0, 0xB3, 0xC2, 0xDD, 0x94, 0xB4, 0xEA, 0xE9, + 0xFF, 0x95, 0x82, 0xE7, 0xBC, 0x35, 0x79, 0xE3, 0x2C, 0x5D, 0xC8, 0x6B, + 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE4, 0xA1, 0xF3, 0x2F, 0x8B, 0xD6, + 0x05, 0x77, 0x9F, 0xA3, 0xAF, 0xCA, 0x87, 0x02, 0x09, 0x03, 0xBC, 0xB9, + 0x4D, 0x28, 0x00, 0x6C, 0x38, 0x58, 0xA5, 0x08, 0xB8, 0xD8, 0xB9, 0x6D, + 0xDF, 0xB3, 0x4C, 0x9C, 0x97, 0x32, 0x29, 0x35, 0x66, 0xCB, 0x1A, 0x5F, + 0x60, 0x6F, 0x1E, 0x9E, 0x9D, 0x45, 0xAB, 0x34, 0xD5, 0x62, 0xD7, 0x1E, + 0x64, 0x8C, 0x37, 0x7B, 0xD4, 0xF9, 0x1C, 0x63, 0x6A, 0x52, 0x3A, 0x68, + 0xC4, 0x2A, 0x79, 0xDC, 0xA7, 0x03, 0xBB, 0xC8, 0x86, 0x17, 0xA0, 0x90, + 0xA8, 0x93, 0x05, 0xAE, 0x4F, 0x06, 0x91, 0x54, 0x14, 0xF3, 0x5A, 0x97, + 0xB9, 0x6A, 0x00, 0x3F, 0x6C, 0x89, 0x7D, 0x04, 0xA6, 0x06, 0xE8, 0x60, + 0x3D, 0xD1, 0xCA, 0x50, 0xAD, 0x61, 0x1B, 0x5A, 0x3A, 0xD8, 0x31, 0xF6, + 0x81, 0x6D, 0xDE, 0x9F, 0xB8, 0xF7, 0xA7, 0x10, 0xA9, 0xE0, 0xD8, 0x69, + 0xF4, 0x8A, 0x7F, 0xE7, 0xD8, 0x34, 0x2E, 0xC3, 0xC3, 0x06, 0x0E, 0xA7, + 0x62, 0x2F, 0x31, 0xE5, 0xA0, 0xCD, 0xA1, 0x89, 0xAF, 0x11, 0x51, 0x00, + 0xE4, 0x54, 0x73, 0xEE, 0xEF, 0xF0, 0xC2, 0x0C, 0xA6, 0x18, 0x83, 0xE0, + 0x5A, 0x96, 0x06, 0xDD, 0xCE, 0x64, 0xB3, 0x59, 0xD0, 0x01, 0x91, 0x92, + 0x6B, 0xB7, 0x4B, 0x16, 0xD0, 0xCC, 0x11, 0x83, 0xB1, 0xE3, 0xBE, 0xB0, + 0x06, 0xE1, 0xC0, 0x55, 0x25, 0x92, 0x02, 0x97, 0x54, 0x33, 0xC5, 0x70, + 0x93, 0xF9, 0x1C, 0x7F, 0xFB, 0xDA, 0xA3, 0x73, 0xB3, 0xA9, 0xED, 0xE8, + 0xB3, 0xBE, 0xC2, 0xA0, 0x54, 0x70, 0x64, 0xC3, 0x5A, 0xCF, 0xF1, 0x7F, + 0xCF, 0x94, 0xB0, 0x70, 0x6C, 0x01, 0xA1, 0xEE, 0x10, 0x82, 0x20, 0x68, + 0xDF, 0xD2, 0x97, 0x47, 0x76, 0x5C, 0x5B, 0x71, 0xDC, 0x86, 0xFC, 0xF3, + 0x17, 0xC4, 0xFA, 0x9B, 0xCD, 0x31, 0x6B, 0x38, 0x13, 0x8D, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xC3, 0xF7, 0xD4, 0x6D, 0x2C, 0x62, 0x93, 0x7C, 0x7C, + 0x66, 0x96, 0x0F, 0xA9, 0x22, 0x59, 0xA1, 0xBD, 0x17, 0xBC, 0xA9, 0x2D, + 0x83, 0xB4, 0x79, 0x0C, 0xDD, 0x96, 0xFD, 0xE2, 0xE7, 0x17, 0x87, 0x14, + 0x13, 0x7A, 0x7E, 0xF1, 0x9D, 0xFC, 0x2D, 0x6B, 0xFB, 0x97, 0xAA, 0xEB, + 0x34, 0x63, 0x00, 0x34, 0x78, 0xD8, 0x0C, 0x64, 0x99, 0xFE, 0x0C, 0xBE, + 0x1F, 0x5A, 0x2E, 0xF2, 0x2A, 0xD3, 0xF9, 0x47, 0x41, 0x96, 0x55, 0xCA, + 0xA6, 0xA0, 0xF9, 0xF5, 0x95, 0x54, 0xB3, 0xEA, 0x2F, 0x36, 0xCC, 0xD1, + 0x84, 0xFE, 0xF0, 0x94, 0xB3, 0x34, 0x06, 0x01, 0x03, 0x5F, 0x79, 0x27, + 0xEB, 0x14, 0x40, 0x71, 0x9F, 0xAD, 0x91, 0x84, 0x9D, 0x5B, 0x27, 0x28, + 0xF9, 0x92, 0x8E, 0x1D, 0xC7, 0xEA, 0xB9, 0x3F, 0xEB, 0x27, 0x82, 0xB9, + 0x04, 0x27, 0xAE, 0xDF, 0x51, 0xE8, 0xC6, 0xD4, 0x25, 0xC4, 0x90, 0xD0, + 0x47, 0xA2, 0xA3, 0x04, 0xDF, 0x9A, 0x73, 0xB7, 0x37, 0x97, 0x65, 0x91, + 0x1B, 0x3F, 0xE9, 0x2A, 0x3D, 0x06, 0xF7, 0x9D, 0xAA, 0x04, 0x2A, 0xD7, + 0x2E, 0xAD, 0xB6, 0xD1, 0x33, 0x6A, 0x0C, 0xA0, 0xC3, 0x2C, 0xF4, 0xF6, + 0x64, 0x50, 0x24, 0x78, 0x7C, 0x33, 0x3D, 0x7D, 0x9E, 0x7A, 0xA4, 0xAE, + 0x6F, 0x89, 0x07, 0x10, 0x80, 0xF3, 0xAD, 0xF3, 0x14, 0x0F, 0x97, 0x26, + 0x4E, 0xC0, 0x4F, 0xAB, 0x42, 0xD8, 0xBA, 0x58, 0x2E, 0x05, 0x8C, 0x59, + 0x5A, 0xC3, 0xDC, 0x9B, 0xE7, 0xD6, 0x71, 0x29, 0xF0, 0xF6, 0xCD, 0x8B, + 0xF8, 0x42, 0xF6, 0xED, 0x1F, 0x7D, 0x22, 0x0C, 0xC2, 0xBD, 0xD1, 0x88, + 0xA9, 0x11, 0x2E, 0xFE, 0x27, 0x99, 0x7F, 0x29, 0xB5, 0x9E, 0x07, 0x97, + 0xDD, 0x5A, 0x6C, 0x3C, 0xD4, 0xFA, 0xC0, 0x25, 0x17, 0x2D, 0xA7, 0x29, + 0xC8, 0x91, 0x66, 0x61, 0x6A, 0x9E, 0xFF, 0x02, 0x82, 0x01, 0x00, 0x43, + 0xB8, 0x84, 0xA3, 0xCC, 0xB3, 0xB4, 0x22, 0xDE, 0xB2, 0x49, 0x5B, 0x9F, + 0xE9, 0xC6, 0x69, 0xC8, 0xF8, 0xCA, 0x15, 0xD3, 0x2A, 0x3A, 0xF6, 0x66, + 0xCB, 0xD2, 0x94, 0xBF, 0x38, 0x00, 0x4E, 0xC7, 0x9E, 0x8A, 0x5A, 0x5D, + 0x42, 0x68, 0x14, 0x38, 0xD7, 0x8F, 0x64, 0xDA, 0x98, 0xF8, 0xD4, 0xAE, + 0x05, 0x94, 0x3F, 0x16, 0xA0, 0xF5, 0xFF, 0x62, 0x5F, 0xD7, 0x91, 0x82, + 0x0B, 0x20, 0x6F, 0x02, 0xC6, 0x0D, 0x74, 0xD7, 0x6B, 0xB8, 0x3C, 0xAE, + 0xDE, 0x92, 0x79, 0x65, 0x21, 0xF4, 0x0C, 0xB8, 0x5E, 0x0D, 0x22, 0x50, + 0xE1, 0xAA, 0xE2, 0xFA, 0x2E, 0x7E, 0xC9, 0x1B, 0x11, 0xAE, 0x40, 0x7B, + 0x3B, 0x69, 0x42, 0x1A, 0x8A, 0x87, 0x45, 0x20, 0xBC, 0x0C, 0xDE, 0xFA, + 0x8F, 0x1B, 0x66, 0xF6, 0x45, 0xA1, 0xC8, 0xBD, 0x6C, 0x6A, 0x09, 0x50, + 0xFE, 0xEA, 0x92, 0xA3, 0x07, 0x9F, 0x5B, 0x76, 0xCF, 0x61, 0x86, 0xF3, + 0x63, 0x20, 0xDD, 0x26, 0x72, 0x05, 0xEF, 0x1C, 0xC4, 0x7B, 0x80, 0xED, + 0x1C, 0x2F, 0x32, 0xDD, 0xDF, 0x79, 0xC2, 0x5A, 0x24, 0xA8, 0x05, 0x94, + 0x34, 0xD2, 0xED, 0xE8, 0x02, 0x08, 0x52, 0xEF, 0xED, 0xBB, 0x38, 0xB0, + 0xB7, 0x25, 0x99, 0x91, 0x58, 0xE1, 0x77, 0x05, 0x52, 0xD8, 0xBD, 0xD9, + 0x62, 0x3C, 0xCA, 0xCC, 0xAC, 0xAF, 0x1D, 0x27, 0x2F, 0x11, 0x55, 0xD2, + 0x56, 0xA8, 0xA6, 0x3D, 0x11, 0x3F, 0x83, 0x46, 0x21, 0xD2, 0xA3, 0xCC, + 0x4E, 0x75, 0x25, 0x1C, 0x47, 0x15, 0x16, 0xA7, 0xE7, 0x7D, 0x9B, 0x5C, + 0xFD, 0x50, 0x64, 0xCF, 0x8F, 0xA0, 0x18, 0xD6, 0xBA, 0x36, 0x03, 0xB5, + 0xC7, 0x22, 0x34, 0xA9, 0x0C, 0xE7, 0x5F, 0x25, 0x58, 0x11, 0x83, 0x01, + 0xC3, 0x14, 0x23, 0x17, 0xC1, 0x99, 0x84, 0xCB, 0x06, 0x63, 0x1D, 0x04, + 0x39, 0x33, 0xF1, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x83, 0x5A, 0xCB, + 0xD9, 0x8D, 0xAC, 0xBF, 0xB3, 0x8A, 0xD3, 0xD1, 0xED, 0xF1, 0x02, 0x8B, + 0xFC, 0x73, 0xE6, 0x1C, 0xC2, 0x73, 0xF2, 0xC5, 0xAF, 0x68, 0x0B, 0x78, + 0x72, 0x87, 0xED, 0x8D, 0xA3, 0x9E, 0xF2, 0x5C, 0x75, 0xCC, 0x1E, 0xA2, + 0x80, 0x7E, 0xC1, 0x98, 0x6C, 0x29, 0xBC, 0xFC, 0x09, 0xF4, 0xE5, 0xF3, + 0xCD, 0x9F, 0xFE, 0xFA, 0xD3, 0xCC, 0xF5, 0x41, 0x6F, 0x3D, 0x31, 0xE8, + 0xB4, 0x58, 0x09, 0x24, 0x78, 0x2E, 0xE5, 0x51, 0x61, 0xDA, 0x91, 0xF6, + 0x76, 0xB2, 0x1F, 0x1F, 0x0B, 0x5E, 0xE0, 0x61, 0xCB, 0xB5, 0x4F, 0x93, + 0x53, 0x4D, 0x89, 0x85, 0xBF, 0x67, 0x6F, 0x2B, 0xF2, 0xB8, 0xBE, 0x32, + 0x9C, 0x9C, 0x43, 0x90, 0x5E, 0xE0, 0x42, 0x0B, 0x3E, 0xEE, 0xF2, 0x49, + 0x04, 0x3B, 0xE4, 0xAB, 0xF7, 0x39, 0xAC, 0x27, 0x34, 0xE7, 0x39, 0x7C, + 0x5B, 0x97, 0xE2, 0xA0, 0xCD, 0x8B, 0x10, 0x90, 0xC5, 0x67, 0xA8, 0x11, + 0xF0, 0x54, 0x59, 0xA4, 0xD2, 0x59, 0xB4, 0x17, 0x50, 0x1B, 0x2B, 0x51, + 0x6A, 0xDE, 0xD0, 0xFE, 0x91, 0x35, 0x1D, 0x22, 0xBD, 0xB3, 0x88, 0x80, + 0xA2, 0x55, 0x80, 0xA9, 0x59, 0x04, 0x9A, 0x7E, 0x4B, 0xAA, 0x31, 0xFE, + 0x99, 0x5B, 0xC7, 0x70, 0x8B, 0x91, 0x1E, 0x42, 0xFA, 0x03, 0x31, 0x69, + 0xE2, 0xCB, 0xBA, 0x16, 0xEA, 0xDF, 0xD3, 0x0B, 0x40, 0x7D, 0xEB, 0xD6, + 0x57, 0x6C, 0xDD, 0xDC, 0xF9, 0x82, 0x78, 0x9D, 0xB4, 0x49, 0x10, 0x81, + 0x3E, 0x20, 0xD8, 0x55, 0x43, 0x9D, 0xB2, 0xE6, 0x5F, 0x43, 0x34, 0x0E, + 0xBF, 0x44, 0x6D, 0xF3, 0x4A, 0x75, 0x57, 0x24, 0x79, 0xC2, 0xE8, 0xC5, + 0xED, 0xC5, 0xAC, 0xB5, 0x9F, 0x65, 0x8E, 0xD5, 0x2B, 0x84, 0xAD, 0x6F, + 0x49, 0x1D, 0xE9, 0x56, 0x8B, 0x5F, 0x18, 0x7B, 0xEF, 0xC5, 0xD1, 0xB3, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x62, 0x96, 0x5D, 0xA8, 0xCF, 0x33, + 0x87, 0x61, 0x00, 0xD2, 0xE8, 0x8D, 0x50, 0xE5, 0x3F, 0x65, 0xC9, 0x93, + 0xD5, 0x09, 0x4A, 0x83, 0xE8, 0x97, 0x29, 0xF5, 0x99, 0xAF, 0xE6, 0x9D, + 0x3D, 0x53, 0xE4, 0xD5, 0xD4, 0xFD, 0xC9, 0x4C, 0x7F, 0x9C, 0xDC, 0x27, + 0x7C, 0x26, 0xCA, 0x71, 0x31, 0x32, 0x56, 0xDE, 0xDE, 0x03, 0x38, 0xA2, + 0x2E, 0xC6, 0x70, 0xE9, 0xE5, 0xCE, 0x3C, 0xAF, 0x54, 0xF3, 0x3B, 0xC2, + 0x24, 0xF5, 0x2A, 0x82, 0x00, 0xD9, 0xDF, 0xE9, 0x1B, 0x93, 0x88, 0xCF, + 0x0D, 0x10, 0x2C, 0xD0, 0x9B, 0x48, 0x90, 0x2D, 0x1B, 0xB8, 0x7C, 0xE4, + 0x5C, 0x5D, 0x2B, 0x80, 0x0E, 0x1B, 0x27, 0x3B, 0x94, 0x1B, 0x40, 0xC4, + 0xB1, 0x72, 0x57, 0x62, 0xA1, 0xE8, 0x3A, 0x6D, 0x4E, 0x54, 0x97, 0xEC, + 0xC6, 0x82, 0xCF, 0xB6, 0x1A, 0x94, 0x0F, 0x40, 0xF6, 0xB0, 0x26, 0x3B, + 0x15, 0xB2, 0x57, 0x80, 0xD3, 0x1A, 0xDE, 0xF6, 0x1B, 0xD7, 0xD9, 0x31, + 0x78, 0x9E, 0xEF, 0xEE, 0x98, 0x81, 0xAE, 0x28, 0x4B, 0x93, 0x92, 0xD6, + 0x74, 0x0C, 0x76, 0x95, 0x25, 0x74, 0x95, 0xA1, 0x44, 0xF4, 0xEF, 0x02, + 0x87, 0xA1, 0xDA, 0x65, 0xE4, 0x7D, 0x24, 0x88, 0x81, 0xC9, 0xC6, 0x7D, + 0xA8, 0x72, 0x17, 0x3C, 0xF0, 0xB2, 0x65, 0x64, 0x0E, 0x96, 0x50, 0x88, + 0x56, 0x2D, 0x6A, 0x49, 0x09, 0x29, 0x40, 0xDE, 0x2F, 0x60, 0xD8, 0x5C, + 0xFC, 0x81, 0x3B, 0x32, 0xF4, 0x35, 0x24, 0x16, 0x8E, 0x6E, 0x52, 0x47, + 0x72, 0xC7, 0x08, 0x2C, 0x2F, 0x1A, 0x88, 0x95, 0x45, 0x39, 0x26, 0x67, + 0x36, 0x32, 0xBF, 0x7E, 0x5A, 0x2B, 0xC6, 0xD4, 0xCE, 0x0C, 0x3F, 0x48, + 0x1B, 0x78, 0x07, 0x7F, 0x14, 0xD1, 0xD6, 0xC7, 0xDA, 0x2E, 0xFE, 0x73, + 0x09, 0x07, 0xFD, 0xC0, 0xE2, 0x39, 0x44, 0x8F, 0xF1 +}; + +unsigned char client_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x44, + 0x43, + 0x43, + 0x41, + 0x77, + 0x43, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x45, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x78, + 0x4f, + 0x46, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x45, + 0x34, + 0x57, + 0x6a, + 0x42, + 0x4d, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x44, + 0x7a, + 0x41, + 0x4e, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x42, + 0x6d, + 0x4e, + 0x73, + 0x61, + 0x57, + 0x56, + 0x75, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x0a, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4b, + 0x38, + 0x45, + 0x73, + 0x6a, + 0x5a, + 0x34, + 0x58, + 0x37, + 0x77, + 0x34, + 0x6e, + 0x42, + 0x69, + 0x54, + 0x67, + 0x58, + 0x31, + 0x45, + 0x43, + 0x78, + 0x68, + 0x4b, + 0x53, + 0x67, + 0x74, + 0x76, + 0x7a, + 0x64, + 0x75, + 0x55, + 0x36, + 0x4e, + 0x69, + 0x6e, + 0x2b, + 0x75, + 0x73, + 0x35, + 0x0a, + 0x41, + 0x32, + 0x39, + 0x35, + 0x75, + 0x43, + 0x44, + 0x38, + 0x43, + 0x33, + 0x48, + 0x4d, + 0x52, + 0x6c, + 0x68, + 0x31, + 0x72, + 0x6e, + 0x73, + 0x59, + 0x71, + 0x5a, + 0x72, + 0x77, + 0x4d, + 0x42, + 0x66, + 0x6c, + 0x39, + 0x75, + 0x68, + 0x52, + 0x73, + 0x2b, + 0x58, + 0x79, + 0x58, + 0x61, + 0x76, + 0x42, + 0x59, + 0x63, + 0x4a, + 0x61, + 0x76, + 0x70, + 0x36, + 0x6f, + 0x2b, + 0x44, + 0x34, + 0x5a, + 0x66, + 0x30, + 0x50, + 0x73, + 0x46, + 0x41, + 0x5a, + 0x55, + 0x50, + 0x6c, + 0x7a, + 0x49, + 0x0a, + 0x36, + 0x69, + 0x4b, + 0x63, + 0x4d, + 0x73, + 0x36, + 0x57, + 0x5a, + 0x75, + 0x42, + 0x35, + 0x76, + 0x6a, + 0x6a, + 0x67, + 0x69, + 0x6e, + 0x37, + 0x4d, + 0x6c, + 0x33, + 0x5a, + 0x69, + 0x79, + 0x36, + 0x52, + 0x63, + 0x6c, + 0x5a, + 0x4f, + 0x4b, + 0x36, + 0x39, + 0x68, + 0x6b, + 0x4f, + 0x43, + 0x68, + 0x55, + 0x6b, + 0x46, + 0x30, + 0x46, + 0x38, + 0x6d, + 0x63, + 0x63, + 0x57, + 0x7a, + 0x52, + 0x58, + 0x59, + 0x4a, + 0x77, + 0x71, + 0x37, + 0x35, + 0x72, + 0x5a, + 0x43, + 0x41, + 0x55, + 0x47, + 0x0a, + 0x37, + 0x35, + 0x70, + 0x77, + 0x63, + 0x6a, + 0x2f, + 0x73, + 0x4a, + 0x33, + 0x73, + 0x34, + 0x56, + 0x45, + 0x43, + 0x69, + 0x35, + 0x4a, + 0x6b, + 0x44, + 0x39, + 0x6b, + 0x6f, + 0x2b, + 0x33, + 0x6b, + 0x63, + 0x79, + 0x36, + 0x4d, + 0x6c, + 0x39, + 0x71, + 0x67, + 0x43, + 0x4c, + 0x67, + 0x68, + 0x57, + 0x47, + 0x31, + 0x67, + 0x4f, + 0x67, + 0x31, + 0x6b, + 0x76, + 0x54, + 0x79, + 0x31, + 0x62, + 0x6f, + 0x4b, + 0x6b, + 0x72, + 0x79, + 0x33, + 0x6b, + 0x4b, + 0x73, + 0x39, + 0x62, + 0x61, + 0x64, + 0x0a, + 0x6e, + 0x32, + 0x61, + 0x59, + 0x51, + 0x58, + 0x59, + 0x33, + 0x79, + 0x4a, + 0x58, + 0x67, + 0x77, + 0x62, + 0x73, + 0x32, + 0x49, + 0x41, + 0x64, + 0x47, + 0x6e, + 0x50, + 0x63, + 0x67, + 0x78, + 0x39, + 0x5a, + 0x6e, + 0x6e, + 0x6b, + 0x44, + 0x67, + 0x6a, + 0x54, + 0x32, + 0x73, + 0x30, + 0x51, + 0x48, + 0x46, + 0x6c, + 0x73, + 0x4c, + 0x76, + 0x71, + 0x46, + 0x56, + 0x70, + 0x68, + 0x41, + 0x52, + 0x4a, + 0x44, + 0x77, + 0x79, + 0x4e, + 0x61, + 0x59, + 0x62, + 0x70, + 0x77, + 0x2f, + 0x64, + 0x39, + 0x0a, + 0x78, + 0x74, + 0x55, + 0x58, + 0x46, + 0x6e, + 0x5a, + 0x6b, + 0x46, + 0x4b, + 0x2b, + 0x35, + 0x42, + 0x33, + 0x6a, + 0x7a, + 0x44, + 0x38, + 0x4f, + 0x61, + 0x64, + 0x77, + 0x63, + 0x2f, + 0x71, + 0x6f, + 0x36, + 0x6d, + 0x68, + 0x2f, + 0x75, + 0x71, + 0x41, + 0x6b, + 0x34, + 0x57, + 0x51, + 0x71, + 0x50, + 0x6d, + 0x46, + 0x50, + 0x59, + 0x55, + 0x50, + 0x2b, + 0x50, + 0x68, + 0x38, + 0x44, + 0x44, + 0x63, + 0x76, + 0x45, + 0x4c, + 0x4d, + 0x6f, + 0x32, + 0x70, + 0x6e, + 0x2b, + 0x39, + 0x63, + 0x2b, + 0x0a, + 0x59, + 0x4f, + 0x41, + 0x6f, + 0x58, + 0x74, + 0x75, + 0x45, + 0x44, + 0x33, + 0x33, + 0x4a, + 0x39, + 0x4e, + 0x31, + 0x73, + 0x76, + 0x32, + 0x55, + 0x57, + 0x72, + 0x6b, + 0x6f, + 0x4c, + 0x45, + 0x4a, + 0x38, + 0x73, + 0x34, + 0x61, + 0x32, + 0x63, + 0x2f, + 0x56, + 0x69, + 0x65, + 0x66, + 0x76, + 0x4d, + 0x46, + 0x4b, + 0x6f, + 0x2b, + 0x4f, + 0x45, + 0x50, + 0x42, + 0x4f, + 0x71, + 0x57, + 0x45, + 0x53, + 0x39, + 0x49, + 0x65, + 0x4d, + 0x58, + 0x69, + 0x4c, + 0x42, + 0x44, + 0x72, + 0x2f, + 0x69, + 0x0a, + 0x4d, + 0x44, + 0x57, + 0x31, + 0x57, + 0x4a, + 0x76, + 0x46, + 0x74, + 0x57, + 0x78, + 0x32, + 0x57, + 0x33, + 0x56, + 0x74, + 0x55, + 0x6e, + 0x6a, + 0x4d, + 0x4b, + 0x62, + 0x52, + 0x47, + 0x2b, + 0x78, + 0x6a, + 0x34, + 0x46, + 0x47, + 0x6a, + 0x76, + 0x38, + 0x69, + 0x63, + 0x2f, + 0x34, + 0x53, + 0x71, + 0x6e, + 0x4b, + 0x36, + 0x53, + 0x43, + 0x65, + 0x2f, + 0x79, + 0x4b, + 0x47, + 0x67, + 0x45, + 0x79, + 0x76, + 0x6f, + 0x2f, + 0x61, + 0x66, + 0x4e, + 0x6d, + 0x39, + 0x30, + 0x49, + 0x74, + 0x76, + 0x0a, + 0x31, + 0x45, + 0x36, + 0x61, + 0x69, + 0x41, + 0x79, + 0x49, + 0x4d, + 0x73, + 0x6d, + 0x51, + 0x43, + 0x31, + 0x4a, + 0x4f, + 0x6f, + 0x56, + 0x68, + 0x2f, + 0x65, + 0x30, + 0x71, + 0x44, + 0x47, + 0x71, + 0x70, + 0x49, + 0x56, + 0x49, + 0x37, + 0x41, + 0x2f, + 0x4f, + 0x73, + 0x7a, + 0x39, + 0x74, + 0x53, + 0x68, + 0x78, + 0x44, + 0x65, + 0x52, + 0x75, + 0x5a, + 0x46, + 0x39, + 0x2b, + 0x67, + 0x4d, + 0x4d, + 0x6e, + 0x30, + 0x68, + 0x41, + 0x4a, + 0x6b, + 0x62, + 0x35, + 0x52, + 0x33, + 0x4d, + 0x78, + 0x0a, + 0x6f, + 0x38, + 0x58, + 0x36, + 0x6b, + 0x44, + 0x77, + 0x61, + 0x73, + 0x7a, + 0x48, + 0x48, + 0x76, + 0x52, + 0x55, + 0x73, + 0x66, + 0x54, + 0x66, + 0x6b, + 0x66, + 0x57, + 0x38, + 0x50, + 0x31, + 0x51, + 0x62, + 0x48, + 0x77, + 0x6e, + 0x4d, + 0x78, + 0x65, + 0x53, + 0x79, + 0x70, + 0x75, + 0x30, + 0x61, + 0x49, + 0x78, + 0x77, + 0x2b, + 0x63, + 0x38, + 0x44, + 0x6d, + 0x7a, + 0x74, + 0x2b, + 0x6c, + 0x73, + 0x37, + 0x62, + 0x6a, + 0x32, + 0x6d, + 0x32, + 0x36, + 0x48, + 0x38, + 0x39, + 0x4a, + 0x68, + 0x0a, + 0x59, + 0x6c, + 0x63, + 0x72, + 0x68, + 0x64, + 0x33, + 0x44, + 0x6d, + 0x2f, + 0x37, + 0x61, + 0x68, + 0x6c, + 0x56, + 0x66, + 0x64, + 0x35, + 0x79, + 0x4b, + 0x77, + 0x41, + 0x57, + 0x38, + 0x76, + 0x77, + 0x32, + 0x77, + 0x57, + 0x79, + 0x45, + 0x43, + 0x56, + 0x79, + 0x37, + 0x4c, + 0x4c, + 0x5a, + 0x36, + 0x63, + 0x4b, + 0x5a, + 0x57, + 0x57, + 0x32, + 0x6d, + 0x5a, + 0x47, + 0x4b, + 0x6a, + 0x37, + 0x7a, + 0x75, + 0x78, + 0x6b, + 0x56, + 0x65, + 0x39, + 0x42, + 0x2f, + 0x4d, + 0x44, + 0x54, + 0x75, + 0x0a, + 0x54, + 0x6e, + 0x39, + 0x7a, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4d, + 0x4c, + 0x70, + 0x6f, + 0x2b, + 0x53, + 0x58, + 0x43, + 0x46, + 0x2b, + 0x35, + 0x73, + 0x4d, + 0x35, + 0x68, + 0x57, + 0x62, + 0x4a, + 0x6d, + 0x38, + 0x77, + 0x52, + 0x64, + 0x4e, + 0x36, + 0x45, + 0x63, + 0x0a, + 0x4d, + 0x48, + 0x6a, + 0x54, + 0x33, + 0x74, + 0x61, + 0x2b, + 0x7a, + 0x79, + 0x74, + 0x6d, + 0x33, + 0x6f, + 0x79, + 0x71, + 0x59, + 0x63, + 0x53, + 0x2f, + 0x62, + 0x53, + 0x43, + 0x56, + 0x74, + 0x30, + 0x6a, + 0x38, + 0x65, + 0x71, + 0x72, + 0x5a, + 0x54, + 0x33, + 0x73, + 0x63, + 0x43, + 0x77, + 0x77, + 0x69, + 0x77, + 0x53, + 0x79, + 0x32, + 0x73, + 0x37, + 0x5a, + 0x63, + 0x65, + 0x74, + 0x35, + 0x75, + 0x6d, + 0x6b, + 0x46, + 0x55, + 0x48, + 0x62, + 0x36, + 0x4a, + 0x65, + 0x6e, + 0x30, + 0x51, + 0x0a, + 0x6c, + 0x58, + 0x4b, + 0x42, + 0x6f, + 0x61, + 0x53, + 0x47, + 0x54, + 0x57, + 0x42, + 0x4f, + 0x5a, + 0x62, + 0x6b, + 0x69, + 0x55, + 0x36, + 0x4f, + 0x36, + 0x6d, + 0x36, + 0x52, + 0x4c, + 0x4d, + 0x72, + 0x4d, + 0x6d, + 0x62, + 0x70, + 0x32, + 0x6b, + 0x68, + 0x4e, + 0x61, + 0x67, + 0x36, + 0x59, + 0x71, + 0x39, + 0x70, + 0x33, + 0x73, + 0x66, + 0x73, + 0x59, + 0x7a, + 0x38, + 0x69, + 0x45, + 0x31, + 0x38, + 0x7a, + 0x36, + 0x43, + 0x31, + 0x76, + 0x70, + 0x75, + 0x4b, + 0x63, + 0x39, + 0x51, + 0x54, + 0x0a, + 0x50, + 0x51, + 0x4c, + 0x51, + 0x65, + 0x7a, + 0x6c, + 0x68, + 0x77, + 0x5a, + 0x5a, + 0x57, + 0x52, + 0x4e, + 0x77, + 0x6b, + 0x43, + 0x79, + 0x4e, + 0x52, + 0x42, + 0x6a, + 0x69, + 0x39, + 0x4e, + 0x37, + 0x55, + 0x2f, + 0x75, + 0x4f, + 0x52, + 0x4f, + 0x49, + 0x38, + 0x64, + 0x6b, + 0x36, + 0x4b, + 0x48, + 0x6e, + 0x4d, + 0x5a, + 0x52, + 0x32, + 0x69, + 0x52, + 0x34, + 0x65, + 0x43, + 0x53, + 0x42, + 0x32, + 0x67, + 0x74, + 0x6b, + 0x76, + 0x68, + 0x50, + 0x65, + 0x4c, + 0x55, + 0x58, + 0x77, + 0x58, + 0x0a, + 0x38, + 0x4a, + 0x79, + 0x6d, + 0x32, + 0x51, + 0x48, + 0x58, + 0x35, + 0x43, + 0x59, + 0x37, + 0x2f, + 0x59, + 0x4d, + 0x66, + 0x73, + 0x34, + 0x44, + 0x30, + 0x49, + 0x38, + 0x66, + 0x70, + 0x6a, + 0x53, + 0x4f, + 0x65, + 0x65, + 0x52, + 0x59, + 0x33, + 0x63, + 0x6e, + 0x4a, + 0x76, + 0x33, + 0x37, + 0x4c, + 0x75, + 0x51, + 0x4c, + 0x35, + 0x72, + 0x4a, + 0x70, + 0x75, + 0x76, + 0x49, + 0x5a, + 0x78, + 0x46, + 0x4a, + 0x33, + 0x6e, + 0x56, + 0x72, + 0x37, + 0x56, + 0x2b, + 0x44, + 0x54, + 0x4d, + 0x72, + 0x0a, + 0x4e, + 0x71, + 0x4e, + 0x45, + 0x34, + 0x69, + 0x57, + 0x48, + 0x4d, + 0x56, + 0x58, + 0x71, + 0x63, + 0x45, + 0x62, + 0x4d, + 0x53, + 0x2f, + 0x59, + 0x4a, + 0x6a, + 0x38, + 0x34, + 0x69, + 0x63, + 0x47, + 0x65, + 0x31, + 0x70, + 0x43, + 0x55, + 0x30, + 0x31, + 0x65, + 0x49, + 0x2f, + 0x61, + 0x33, + 0x34, + 0x72, + 0x79, + 0x53, + 0x67, + 0x55, + 0x75, + 0x2b, + 0x33, + 0x53, + 0x43, + 0x55, + 0x55, + 0x61, + 0x58, + 0x5a, + 0x73, + 0x2f, + 0x2b, + 0x51, + 0x41, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char server_key[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x4a, + 0x4b, + 0x41, + 0x49, + 0x42, + 0x41, + 0x41, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x79, + 0x31, + 0x65, + 0x71, + 0x47, + 0x4a, + 0x69, + 0x45, + 0x61, + 0x4e, + 0x55, + 0x76, + 0x42, + 0x57, + 0x35, + 0x72, + 0x4b, + 0x38, + 0x77, + 0x77, + 0x69, + 0x75, + 0x4d, + 0x72, + 0x74, + 0x4c, + 0x67, + 0x74, + 0x74, + 0x45, + 0x65, + 0x55, + 0x31, + 0x51, + 0x4f, + 0x30, + 0x6e, + 0x4d, + 0x57, + 0x6a, + 0x59, + 0x43, + 0x4f, + 0x34, + 0x75, + 0x36, + 0x61, + 0x31, + 0x0a, + 0x34, + 0x58, + 0x34, + 0x6f, + 0x75, + 0x7a, + 0x68, + 0x57, + 0x4e, + 0x6a, + 0x50, + 0x47, + 0x35, + 0x31, + 0x6f, + 0x4a, + 0x43, + 0x76, + 0x4b, + 0x38, + 0x78, + 0x71, + 0x58, + 0x52, + 0x33, + 0x2f, + 0x7a, + 0x62, + 0x79, + 0x36, + 0x66, + 0x66, + 0x48, + 0x69, + 0x42, + 0x61, + 0x4e, + 0x68, + 0x4d, + 0x34, + 0x4c, + 0x6e, + 0x6c, + 0x78, + 0x44, + 0x30, + 0x77, + 0x4d, + 0x31, + 0x33, + 0x36, + 0x6d, + 0x59, + 0x77, + 0x51, + 0x54, + 0x6a, + 0x35, + 0x33, + 0x57, + 0x36, + 0x64, + 0x4e, + 0x2b, + 0x0a, + 0x6a, + 0x33, + 0x7a, + 0x7a, + 0x61, + 0x4d, + 0x7a, + 0x78, + 0x36, + 0x4f, + 0x55, + 0x30, + 0x56, + 0x4e, + 0x77, + 0x4f, + 0x68, + 0x4d, + 0x2b, + 0x47, + 0x67, + 0x33, + 0x76, + 0x43, + 0x70, + 0x2f, + 0x45, + 0x37, + 0x58, + 0x6b, + 0x57, + 0x65, + 0x4a, + 0x50, + 0x42, + 0x4d, + 0x4a, + 0x62, + 0x33, + 0x52, + 0x75, + 0x62, + 0x50, + 0x76, + 0x61, + 0x33, + 0x72, + 0x4d, + 0x73, + 0x36, + 0x62, + 0x63, + 0x4b, + 0x6c, + 0x6d, + 0x36, + 0x35, + 0x30, + 0x64, + 0x4b, + 0x64, + 0x63, + 0x65, + 0x71, + 0x0a, + 0x32, + 0x41, + 0x37, + 0x50, + 0x71, + 0x64, + 0x4a, + 0x4a, + 0x74, + 0x4f, + 0x4d, + 0x37, + 0x56, + 0x42, + 0x48, + 0x63, + 0x73, + 0x33, + 0x34, + 0x4f, + 0x38, + 0x33, + 0x54, + 0x68, + 0x73, + 0x78, + 0x63, + 0x46, + 0x39, + 0x37, + 0x2f, + 0x51, + 0x31, + 0x55, + 0x54, + 0x39, + 0x43, + 0x6c, + 0x34, + 0x34, + 0x34, + 0x59, + 0x39, + 0x6c, + 0x49, + 0x59, + 0x49, + 0x36, + 0x30, + 0x71, + 0x38, + 0x4e, + 0x6e, + 0x67, + 0x36, + 0x37, + 0x49, + 0x71, + 0x47, + 0x62, + 0x5a, + 0x47, + 0x69, + 0x73, + 0x0a, + 0x59, + 0x6d, + 0x63, + 0x75, + 0x5a, + 0x66, + 0x45, + 0x4d, + 0x55, + 0x65, + 0x59, + 0x45, + 0x79, + 0x6e, + 0x54, + 0x52, + 0x30, + 0x42, + 0x78, + 0x36, + 0x73, + 0x45, + 0x52, + 0x45, + 0x73, + 0x4f, + 0x51, + 0x74, + 0x47, + 0x6c, + 0x31, + 0x62, + 0x6a, + 0x77, + 0x72, + 0x61, + 0x4c, + 0x6d, + 0x30, + 0x46, + 0x43, + 0x38, + 0x42, + 0x77, + 0x6c, + 0x6e, + 0x4f, + 0x55, + 0x76, + 0x78, + 0x44, + 0x34, + 0x66, + 0x5a, + 0x2b, + 0x43, + 0x35, + 0x4a, + 0x73, + 0x48, + 0x62, + 0x5a, + 0x35, + 0x54, + 0x0a, + 0x4b, + 0x30, + 0x46, + 0x74, + 0x73, + 0x32, + 0x65, + 0x43, + 0x2f, + 0x71, + 0x48, + 0x4a, + 0x48, + 0x4c, + 0x52, + 0x73, + 0x41, + 0x6a, + 0x4e, + 0x70, + 0x67, + 0x66, + 0x42, + 0x71, + 0x6c, + 0x45, + 0x6a, + 0x47, + 0x73, + 0x74, + 0x4e, + 0x61, + 0x64, + 0x30, + 0x79, + 0x49, + 0x48, + 0x76, + 0x49, + 0x36, + 0x39, + 0x32, + 0x4b, + 0x79, + 0x56, + 0x54, + 0x37, + 0x56, + 0x4b, + 0x67, + 0x56, + 0x59, + 0x42, + 0x74, + 0x39, + 0x58, + 0x6e, + 0x4b, + 0x36, + 0x48, + 0x41, + 0x36, + 0x66, + 0x42, + 0x0a, + 0x4d, + 0x78, + 0x4b, + 0x68, + 0x31, + 0x48, + 0x54, + 0x30, + 0x37, + 0x47, + 0x42, + 0x52, + 0x71, + 0x34, + 0x44, + 0x62, + 0x4b, + 0x73, + 0x41, + 0x76, + 0x5a, + 0x44, + 0x73, + 0x45, + 0x7a, + 0x6b, + 0x61, + 0x64, + 0x67, + 0x75, + 0x6c, + 0x53, + 0x6a, + 0x6d, + 0x72, + 0x6b, + 0x57, + 0x41, + 0x44, + 0x43, + 0x53, + 0x77, + 0x6d, + 0x58, + 0x7a, + 0x44, + 0x4a, + 0x49, + 0x4f, + 0x6c, + 0x68, + 0x78, + 0x43, + 0x76, + 0x73, + 0x57, + 0x76, + 0x4e, + 0x4b, + 0x4f, + 0x70, + 0x7a, + 0x57, + 0x6a, + 0x0a, + 0x31, + 0x53, + 0x7a, + 0x75, + 0x45, + 0x51, + 0x53, + 0x57, + 0x53, + 0x38, + 0x30, + 0x46, + 0x6d, + 0x54, + 0x58, + 0x35, + 0x55, + 0x39, + 0x4e, + 0x6f, + 0x2f, + 0x6e, + 0x45, + 0x52, + 0x6a, + 0x4b, + 0x6c, + 0x78, + 0x75, + 0x44, + 0x36, + 0x37, + 0x35, + 0x67, + 0x4c, + 0x4e, + 0x79, + 0x30, + 0x6c, + 0x61, + 0x63, + 0x6b, + 0x70, + 0x73, + 0x49, + 0x48, + 0x50, + 0x5a, + 0x36, + 0x7a, + 0x6f, + 0x71, + 0x53, + 0x6c, + 0x38, + 0x4c, + 0x68, + 0x70, + 0x5a, + 0x7a, + 0x78, + 0x51, + 0x67, + 0x75, + 0x0a, + 0x56, + 0x51, + 0x72, + 0x39, + 0x62, + 0x69, + 0x4e, + 0x45, + 0x57, + 0x77, + 0x30, + 0x62, + 0x64, + 0x53, + 0x45, + 0x44, + 0x57, + 0x2b, + 0x63, + 0x68, + 0x6e, + 0x41, + 0x72, + 0x7a, + 0x71, + 0x4b, + 0x71, + 0x62, + 0x65, + 0x54, + 0x34, + 0x38, + 0x52, + 0x74, + 0x71, + 0x46, + 0x77, + 0x54, + 0x45, + 0x44, + 0x69, + 0x6f, + 0x48, + 0x38, + 0x73, + 0x47, + 0x47, + 0x69, + 0x43, + 0x2b, + 0x7a, + 0x6a, + 0x50, + 0x31, + 0x76, + 0x59, + 0x78, + 0x44, + 0x66, + 0x6d, + 0x32, + 0x49, + 0x69, + 0x68, + 0x0a, + 0x41, + 0x4f, + 0x50, + 0x74, + 0x48, + 0x6e, + 0x45, + 0x33, + 0x78, + 0x7a, + 0x6b, + 0x44, + 0x4c, + 0x41, + 0x50, + 0x4a, + 0x76, + 0x7a, + 0x45, + 0x61, + 0x2f, + 0x5a, + 0x69, + 0x61, + 0x54, + 0x69, + 0x4d, + 0x61, + 0x30, + 0x4f, + 0x52, + 0x32, + 0x71, + 0x74, + 0x75, + 0x30, + 0x68, + 0x54, + 0x6a, + 0x62, + 0x34, + 0x63, + 0x7a, + 0x58, + 0x49, + 0x36, + 0x54, + 0x79, + 0x37, + 0x78, + 0x6b, + 0x53, + 0x4c, + 0x45, + 0x74, + 0x44, + 0x4f, + 0x6a, + 0x43, + 0x61, + 0x70, + 0x53, + 0x71, + 0x74, + 0x0a, + 0x34, + 0x45, + 0x4b, + 0x47, + 0x67, + 0x43, + 0x69, + 0x78, + 0x7a, + 0x63, + 0x55, + 0x35, + 0x45, + 0x36, + 0x5a, + 0x4d, + 0x49, + 0x4f, + 0x56, + 0x6a, + 0x72, + 0x66, + 0x6a, + 0x39, + 0x70, + 0x77, + 0x67, + 0x67, + 0x2f, + 0x66, + 0x56, + 0x31, + 0x35, + 0x30, + 0x63, + 0x31, + 0x68, + 0x6e, + 0x6e, + 0x56, + 0x47, + 0x75, + 0x36, + 0x51, + 0x42, + 0x30, + 0x44, + 0x72, + 0x59, + 0x4e, + 0x73, + 0x59, + 0x50, + 0x43, + 0x44, + 0x44, + 0x50, + 0x70, + 0x4d, + 0x43, + 0x41, + 0x77, + 0x45, + 0x41, + 0x0a, + 0x41, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x67, + 0x45, + 0x41, + 0x74, + 0x69, + 0x33, + 0x49, + 0x65, + 0x31, + 0x6a, + 0x4a, + 0x36, + 0x4a, + 0x4a, + 0x33, + 0x47, + 0x6b, + 0x71, + 0x66, + 0x51, + 0x68, + 0x49, + 0x69, + 0x75, + 0x34, + 0x78, + 0x6b, + 0x4c, + 0x2f, + 0x6d, + 0x44, + 0x47, + 0x53, + 0x32, + 0x34, + 0x72, + 0x6c, + 0x41, + 0x76, + 0x6c, + 0x50, + 0x57, + 0x4e, + 0x4e, + 0x69, + 0x6c, + 0x4b, + 0x37, + 0x50, + 0x53, + 0x6a, + 0x70, + 0x32, + 0x38, + 0x38, + 0x56, + 0x75, + 0x42, + 0x57, + 0x0a, + 0x66, + 0x53, + 0x46, + 0x4f, + 0x5a, + 0x79, + 0x42, + 0x48, + 0x55, + 0x63, + 0x50, + 0x41, + 0x56, + 0x63, + 0x6c, + 0x69, + 0x69, + 0x32, + 0x63, + 0x6b, + 0x46, + 0x51, + 0x5a, + 0x46, + 0x34, + 0x39, + 0x76, + 0x67, + 0x64, + 0x75, + 0x6f, + 0x70, + 0x50, + 0x35, + 0x6f, + 0x57, + 0x4c, + 0x54, + 0x70, + 0x5a, + 0x6c, + 0x77, + 0x67, + 0x6c, + 0x55, + 0x6a, + 0x6a, + 0x6f, + 0x76, + 0x5a, + 0x63, + 0x51, + 0x59, + 0x56, + 0x58, + 0x57, + 0x34, + 0x66, + 0x39, + 0x30, + 0x70, + 0x4d, + 0x52, + 0x5a, + 0x0a, + 0x52, + 0x2b, + 0x74, + 0x79, + 0x51, + 0x30, + 0x55, + 0x55, + 0x7a, + 0x50, + 0x43, + 0x51, + 0x39, + 0x32, + 0x50, + 0x2b, + 0x4e, + 0x6a, + 0x44, + 0x75, + 0x6d, + 0x37, + 0x75, + 0x4c, + 0x77, + 0x46, + 0x52, + 0x36, + 0x4a, + 0x4b, + 0x59, + 0x34, + 0x4c, + 0x45, + 0x53, + 0x46, + 0x6b, + 0x4b, + 0x6e, + 0x6f, + 0x6c, + 0x46, + 0x77, + 0x2b, + 0x6a, + 0x4d, + 0x4c, + 0x76, + 0x72, + 0x52, + 0x34, + 0x32, + 0x47, + 0x2b, + 0x66, + 0x54, + 0x72, + 0x4e, + 0x63, + 0x34, + 0x50, + 0x6c, + 0x48, + 0x6c, + 0x0a, + 0x4c, + 0x32, + 0x63, + 0x57, + 0x56, + 0x47, + 0x2f, + 0x63, + 0x61, + 0x41, + 0x50, + 0x63, + 0x71, + 0x55, + 0x70, + 0x69, + 0x6d, + 0x2b, + 0x31, + 0x59, + 0x72, + 0x32, + 0x72, + 0x76, + 0x55, + 0x75, + 0x51, + 0x49, + 0x51, + 0x53, + 0x33, + 0x42, + 0x6b, + 0x48, + 0x54, + 0x45, + 0x69, + 0x6f, + 0x7a, + 0x2b, + 0x33, + 0x70, + 0x74, + 0x76, + 0x65, + 0x39, + 0x68, + 0x2b, + 0x32, + 0x4c, + 0x77, + 0x70, + 0x57, + 0x49, + 0x42, + 0x66, + 0x55, + 0x5a, + 0x70, + 0x58, + 0x2b, + 0x34, + 0x56, + 0x48, + 0x0a, + 0x49, + 0x47, + 0x6b, + 0x51, + 0x69, + 0x63, + 0x4b, + 0x33, + 0x35, + 0x39, + 0x68, + 0x63, + 0x49, + 0x32, + 0x68, + 0x47, + 0x6b, + 0x71, + 0x55, + 0x62, + 0x73, + 0x36, + 0x39, + 0x49, + 0x78, + 0x4a, + 0x4e, + 0x59, + 0x66, + 0x70, + 0x65, + 0x32, + 0x47, + 0x5a, + 0x31, + 0x45, + 0x51, + 0x63, + 0x63, + 0x7a, + 0x30, + 0x53, + 0x73, + 0x48, + 0x4e, + 0x71, + 0x57, + 0x65, + 0x77, + 0x2b, + 0x6f, + 0x52, + 0x63, + 0x61, + 0x69, + 0x54, + 0x6f, + 0x77, + 0x46, + 0x76, + 0x6f, + 0x33, + 0x68, + 0x74, + 0x0a, + 0x36, + 0x47, + 0x78, + 0x53, + 0x6e, + 0x71, + 0x45, + 0x57, + 0x30, + 0x37, + 0x48, + 0x30, + 0x4b, + 0x54, + 0x4b, + 0x59, + 0x69, + 0x6c, + 0x6e, + 0x5a, + 0x69, + 0x34, + 0x58, + 0x49, + 0x57, + 0x33, + 0x48, + 0x35, + 0x30, + 0x51, + 0x61, + 0x75, + 0x61, + 0x68, + 0x62, + 0x77, + 0x48, + 0x30, + 0x75, + 0x56, + 0x73, + 0x64, + 0x32, + 0x55, + 0x39, + 0x6f, + 0x34, + 0x6c, + 0x37, + 0x54, + 0x38, + 0x5a, + 0x4d, + 0x65, + 0x34, + 0x52, + 0x50, + 0x6d, + 0x66, + 0x39, + 0x77, + 0x46, + 0x6e, + 0x2f, + 0x0a, + 0x45, + 0x7a, + 0x34, + 0x46, + 0x74, + 0x68, + 0x62, + 0x6e, + 0x51, + 0x4f, + 0x57, + 0x42, + 0x6f, + 0x31, + 0x44, + 0x78, + 0x61, + 0x42, + 0x6d, + 0x78, + 0x39, + 0x42, + 0x66, + 0x6e, + 0x35, + 0x6e, + 0x43, + 0x35, + 0x49, + 0x36, + 0x52, + 0x79, + 0x72, + 0x36, + 0x58, + 0x4d, + 0x6e, + 0x6e, + 0x4a, + 0x4a, + 0x2b, + 0x65, + 0x41, + 0x74, + 0x53, + 0x6a, + 0x58, + 0x53, + 0x41, + 0x56, + 0x37, + 0x2f, + 0x36, + 0x33, + 0x43, + 0x72, + 0x70, + 0x63, + 0x42, + 0x2b, + 0x73, + 0x42, + 0x74, + 0x46, + 0x0a, + 0x69, + 0x7a, + 0x46, + 0x33, + 0x79, + 0x45, + 0x51, + 0x38, + 0x72, + 0x74, + 0x57, + 0x41, + 0x43, + 0x71, + 0x78, + 0x6a, + 0x4d, + 0x55, + 0x50, + 0x43, + 0x33, + 0x4a, + 0x71, + 0x39, + 0x6c, + 0x39, + 0x33, + 0x39, + 0x38, + 0x2f, + 0x6a, + 0x7a, + 0x43, + 0x70, + 0x5a, + 0x5a, + 0x4c, + 0x76, + 0x7a, + 0x34, + 0x4e, + 0x55, + 0x6a, + 0x6a, + 0x74, + 0x66, + 0x4e, + 0x68, + 0x33, + 0x69, + 0x36, + 0x44, + 0x50, + 0x36, + 0x49, + 0x68, + 0x41, + 0x4b, + 0x31, + 0x33, + 0x54, + 0x56, + 0x37, + 0x49, + 0x0a, + 0x34, + 0x47, + 0x34, + 0x30, + 0x62, + 0x72, + 0x5a, + 0x68, + 0x6d, + 0x58, + 0x53, + 0x56, + 0x4b, + 0x42, + 0x6e, + 0x78, + 0x47, + 0x37, + 0x44, + 0x35, + 0x69, + 0x59, + 0x36, + 0x50, + 0x62, + 0x67, + 0x56, + 0x57, + 0x48, + 0x46, + 0x44, + 0x45, + 0x6a, + 0x50, + 0x44, + 0x77, + 0x6f, + 0x4a, + 0x6b, + 0x74, + 0x4b, + 0x31, + 0x42, + 0x50, + 0x61, + 0x64, + 0x33, + 0x71, + 0x57, + 0x66, + 0x39, + 0x34, + 0x66, + 0x6d, + 0x74, + 0x35, + 0x79, + 0x6b, + 0x5a, + 0x64, + 0x2f, + 0x74, + 0x65, + 0x71, + 0x0a, + 0x65, + 0x52, + 0x49, + 0x45, + 0x76, + 0x55, + 0x7a, + 0x4a, + 0x47, + 0x35, + 0x50, + 0x46, + 0x47, + 0x77, + 0x4f, + 0x7a, + 0x78, + 0x56, + 0x52, + 0x48, + 0x44, + 0x4e, + 0x38, + 0x51, + 0x51, + 0x6d, + 0x4e, + 0x4a, + 0x63, + 0x43, + 0x4a, + 0x75, + 0x79, + 0x74, + 0x77, + 0x34, + 0x44, + 0x6e, + 0x73, + 0x78, + 0x6f, + 0x43, + 0x38, + 0x33, + 0x69, + 0x7a, + 0x66, + 0x68, + 0x2b, + 0x6e, + 0x45, + 0x71, + 0x45, + 0x50, + 0x50, + 0x51, + 0x69, + 0x44, + 0x46, + 0x6f, + 0x54, + 0x4c, + 0x79, + 0x44, + 0x0a, + 0x2b, + 0x34, + 0x56, + 0x52, + 0x67, + 0x46, + 0x42, + 0x62, + 0x36, + 0x61, + 0x69, + 0x72, + 0x56, + 0x63, + 0x79, + 0x58, + 0x57, + 0x64, + 0x72, + 0x32, + 0x30, + 0x43, + 0x64, + 0x4f, + 0x47, + 0x67, + 0x58, + 0x6f, + 0x42, + 0x4d, + 0x72, + 0x4e, + 0x42, + 0x6c, + 0x2b, + 0x45, + 0x36, + 0x32, + 0x4d, + 0x59, + 0x41, + 0x65, + 0x32, + 0x71, + 0x63, + 0x36, + 0x37, + 0x63, + 0x51, + 0x59, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4f, + 0x39, + 0x32, + 0x58, + 0x4e, + 0x41, + 0x46, + 0x0a, + 0x52, + 0x41, + 0x77, + 0x5a, + 0x79, + 0x64, + 0x7a, + 0x59, + 0x63, + 0x68, + 0x35, + 0x45, + 0x35, + 0x65, + 0x6a, + 0x7a, + 0x39, + 0x54, + 0x5a, + 0x62, + 0x48, + 0x66, + 0x65, + 0x66, + 0x34, + 0x7a, + 0x4e, + 0x42, + 0x48, + 0x55, + 0x2f, + 0x6d, + 0x56, + 0x31, + 0x76, + 0x68, + 0x38, + 0x39, + 0x64, + 0x39, + 0x4b, + 0x6a, + 0x53, + 0x46, + 0x2f, + 0x4f, + 0x69, + 0x4c, + 0x33, + 0x45, + 0x6e, + 0x6f, + 0x59, + 0x78, + 0x50, + 0x6b, + 0x48, + 0x67, + 0x55, + 0x58, + 0x79, + 0x6e, + 0x39, + 0x41, + 0x0a, + 0x34, + 0x43, + 0x34, + 0x75, + 0x31, + 0x72, + 0x6c, + 0x64, + 0x5a, + 0x36, + 0x64, + 0x77, + 0x43, + 0x56, + 0x41, + 0x62, + 0x5a, + 0x4a, + 0x6e, + 0x54, + 0x38, + 0x55, + 0x46, + 0x32, + 0x6e, + 0x4c, + 0x52, + 0x46, + 0x4d, + 0x58, + 0x68, + 0x61, + 0x71, + 0x79, + 0x4a, + 0x45, + 0x6d, + 0x54, + 0x37, + 0x6a, + 0x43, + 0x59, + 0x52, + 0x79, + 0x37, + 0x65, + 0x72, + 0x64, + 0x37, + 0x56, + 0x4c, + 0x39, + 0x35, + 0x67, + 0x56, + 0x68, + 0x55, + 0x4f, + 0x73, + 0x4a, + 0x59, + 0x77, + 0x4c, + 0x6d, + 0x0a, + 0x74, + 0x59, + 0x66, + 0x63, + 0x69, + 0x6b, + 0x30, + 0x37, + 0x51, + 0x6d, + 0x44, + 0x53, + 0x50, + 0x56, + 0x75, + 0x47, + 0x68, + 0x56, + 0x32, + 0x4b, + 0x65, + 0x6b, + 0x6e, + 0x7a, + 0x54, + 0x61, + 0x31, + 0x4e, + 0x53, + 0x54, + 0x55, + 0x38, + 0x76, + 0x77, + 0x51, + 0x46, + 0x2b, + 0x77, + 0x4b, + 0x48, + 0x69, + 0x38, + 0x36, + 0x34, + 0x64, + 0x6d, + 0x7a, + 0x6b, + 0x73, + 0x6c, + 0x59, + 0x6e, + 0x51, + 0x71, + 0x62, + 0x46, + 0x6f, + 0x54, + 0x56, + 0x52, + 0x5a, + 0x74, + 0x4e, + 0x65, + 0x0a, + 0x72, + 0x4c, + 0x62, + 0x36, + 0x67, + 0x36, + 0x6e, + 0x63, + 0x77, + 0x36, + 0x4a, + 0x57, + 0x78, + 0x39, + 0x79, + 0x65, + 0x42, + 0x30, + 0x37, + 0x56, + 0x64, + 0x70, + 0x4a, + 0x70, + 0x36, + 0x78, + 0x39, + 0x54, + 0x59, + 0x54, + 0x2b, + 0x48, + 0x47, + 0x6a, + 0x33, + 0x6b, + 0x62, + 0x35, + 0x61, + 0x63, + 0x75, + 0x49, + 0x6e, + 0x50, + 0x2b, + 0x6a, + 0x67, + 0x37, + 0x47, + 0x5a, + 0x7a, + 0x6b, + 0x72, + 0x48, + 0x4a, + 0x4d, + 0x6e, + 0x48, + 0x4c, + 0x77, + 0x65, + 0x46, + 0x76, + 0x62, + 0x0a, + 0x4e, + 0x57, + 0x5a, + 0x66, + 0x6c, + 0x50, + 0x66, + 0x69, + 0x41, + 0x4e, + 0x6d, + 0x77, + 0x4d, + 0x38, + 0x77, + 0x4d, + 0x52, + 0x2b, + 0x6b, + 0x66, + 0x50, + 0x32, + 0x76, + 0x6f, + 0x67, + 0x47, + 0x61, + 0x6d, + 0x4b, + 0x43, + 0x69, + 0x65, + 0x6f, + 0x6a, + 0x56, + 0x61, + 0x54, + 0x69, + 0x38, + 0x76, + 0x67, + 0x71, + 0x71, + 0x6a, + 0x78, + 0x34, + 0x55, + 0x47, + 0x50, + 0x64, + 0x35, + 0x39, + 0x36, + 0x46, + 0x4e, + 0x46, + 0x65, + 0x69, + 0x5a, + 0x52, + 0x76, + 0x58, + 0x4e, + 0x6b, + 0x0a, + 0x30, + 0x64, + 0x42, + 0x30, + 0x41, + 0x4b, + 0x32, + 0x51, + 0x2f, + 0x31, + 0x32, + 0x36, + 0x53, + 0x6c, + 0x45, + 0x43, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4e, + 0x6c, + 0x69, + 0x74, + 0x64, + 0x78, + 0x48, + 0x74, + 0x74, + 0x78, + 0x68, + 0x2f, + 0x57, + 0x71, + 0x77, + 0x46, + 0x4b, + 0x33, + 0x35, + 0x62, + 0x63, + 0x4f, + 0x54, + 0x58, + 0x61, + 0x68, + 0x36, + 0x50, + 0x61, + 0x70, + 0x50, + 0x64, + 0x67, + 0x2f, + 0x37, + 0x32, + 0x50, + 0x75, + 0x4f, + 0x6c, + 0x33, + 0x57, + 0x74, + 0x0a, + 0x37, + 0x57, + 0x6e, + 0x38, + 0x41, + 0x74, + 0x36, + 0x46, + 0x49, + 0x47, + 0x51, + 0x33, + 0x58, + 0x5a, + 0x4f, + 0x4f, + 0x50, + 0x66, + 0x39, + 0x76, + 0x35, + 0x48, + 0x66, + 0x71, + 0x50, + 0x70, + 0x53, + 0x6c, + 0x4e, + 0x56, + 0x39, + 0x33, + 0x41, + 0x58, + 0x58, + 0x61, + 0x4f, + 0x36, + 0x49, + 0x54, + 0x7a, + 0x77, + 0x49, + 0x45, + 0x62, + 0x38, + 0x49, + 0x47, + 0x33, + 0x69, + 0x45, + 0x4f, + 0x74, + 0x54, + 0x4c, + 0x77, + 0x32, + 0x39, + 0x32, + 0x4b, + 0x55, + 0x38, + 0x6c, + 0x4a, + 0x0a, + 0x4c, + 0x6c, + 0x38, + 0x58, + 0x70, + 0x68, + 0x4f, + 0x6b, + 0x56, + 0x55, + 0x72, + 0x50, + 0x6b, + 0x7a, + 0x47, + 0x47, + 0x36, + 0x64, + 0x66, + 0x56, + 0x4e, + 0x70, + 0x36, + 0x49, + 0x32, + 0x68, + 0x54, + 0x70, + 0x59, + 0x4a, + 0x49, + 0x53, + 0x77, + 0x41, + 0x55, + 0x35, + 0x48, + 0x76, + 0x4b, + 0x70, + 0x63, + 0x39, + 0x6f, + 0x42, + 0x30, + 0x59, + 0x4b, + 0x68, + 0x56, + 0x4c, + 0x66, + 0x43, + 0x2b, + 0x69, + 0x64, + 0x61, + 0x6b, + 0x56, + 0x43, + 0x35, + 0x66, + 0x5a, + 0x7a, + 0x36, + 0x0a, + 0x7a, + 0x46, + 0x41, + 0x6f, + 0x32, + 0x37, + 0x46, + 0x65, + 0x6f, + 0x49, + 0x45, + 0x64, + 0x48, + 0x74, + 0x39, + 0x32, + 0x5a, + 0x65, + 0x36, + 0x51, + 0x33, + 0x34, + 0x4e, + 0x4a, + 0x67, + 0x70, + 0x56, + 0x62, + 0x67, + 0x6d, + 0x54, + 0x30, + 0x2b, + 0x77, + 0x74, + 0x75, + 0x75, + 0x72, + 0x71, + 0x6f, + 0x5a, + 0x2f, + 0x7a, + 0x52, + 0x74, + 0x6b, + 0x78, + 0x34, + 0x35, + 0x77, + 0x75, + 0x71, + 0x49, + 0x33, + 0x45, + 0x2b, + 0x54, + 0x44, + 0x61, + 0x31, + 0x53, + 0x75, + 0x71, + 0x33, + 0x0a, + 0x34, + 0x4f, + 0x51, + 0x73, + 0x65, + 0x32, + 0x57, + 0x63, + 0x67, + 0x6a, + 0x7a, + 0x53, + 0x66, + 0x64, + 0x4c, + 0x4a, + 0x74, + 0x54, + 0x66, + 0x79, + 0x65, + 0x4c, + 0x31, + 0x70, + 0x34, + 0x56, + 0x68, + 0x47, + 0x79, + 0x63, + 0x78, + 0x67, + 0x2b, + 0x4a, + 0x45, + 0x35, + 0x48, + 0x34, + 0x6a, + 0x69, + 0x33, + 0x7a, + 0x35, + 0x33, + 0x46, + 0x78, + 0x72, + 0x4d, + 0x6e, + 0x41, + 0x30, + 0x6d, + 0x53, + 0x45, + 0x6c, + 0x79, + 0x45, + 0x6a, + 0x69, + 0x38, + 0x38, + 0x78, + 0x54, + 0x47, + 0x0a, + 0x48, + 0x43, + 0x59, + 0x47, + 0x49, + 0x6e, + 0x4a, + 0x70, + 0x5a, + 0x72, + 0x2b, + 0x51, + 0x7a, + 0x67, + 0x50, + 0x54, + 0x7a, + 0x57, + 0x66, + 0x35, + 0x52, + 0x34, + 0x36, + 0x6b, + 0x30, + 0x38, + 0x47, + 0x4b, + 0x73, + 0x6d, + 0x56, + 0x67, + 0x6a, + 0x31, + 0x50, + 0x32, + 0x7a, + 0x39, + 0x4f, + 0x65, + 0x33, + 0x61, + 0x4d, + 0x43, + 0x67, + 0x67, + 0x45, + 0x41, + 0x48, + 0x42, + 0x6b, + 0x64, + 0x5a, + 0x75, + 0x4e, + 0x58, + 0x53, + 0x72, + 0x77, + 0x7a, + 0x37, + 0x5a, + 0x41, + 0x51, + 0x0a, + 0x51, + 0x2f, + 0x44, + 0x39, + 0x73, + 0x55, + 0x6e, + 0x2b, + 0x2b, + 0x66, + 0x50, + 0x54, + 0x48, + 0x6c, + 0x31, + 0x4b, + 0x67, + 0x5a, + 0x63, + 0x67, + 0x59, + 0x32, + 0x47, + 0x35, + 0x32, + 0x6e, + 0x51, + 0x32, + 0x38, + 0x70, + 0x41, + 0x6a, + 0x52, + 0x61, + 0x70, + 0x37, + 0x4e, + 0x4b, + 0x5a, + 0x45, + 0x6f, + 0x50, + 0x39, + 0x39, + 0x73, + 0x4c, + 0x58, + 0x52, + 0x74, + 0x2f, + 0x4e, + 0x45, + 0x74, + 0x59, + 0x33, + 0x64, + 0x51, + 0x45, + 0x34, + 0x4b, + 0x73, + 0x42, + 0x46, + 0x2f, + 0x0a, + 0x75, + 0x69, + 0x76, + 0x78, + 0x53, + 0x38, + 0x38, + 0x4c, + 0x44, + 0x4f, + 0x6e, + 0x4c, + 0x6f, + 0x30, + 0x7a, + 0x52, + 0x73, + 0x6d, + 0x31, + 0x30, + 0x45, + 0x70, + 0x56, + 0x42, + 0x41, + 0x33, + 0x4a, + 0x64, + 0x4d, + 0x50, + 0x33, + 0x65, + 0x2f, + 0x67, + 0x57, + 0x6d, + 0x57, + 0x53, + 0x72, + 0x56, + 0x55, + 0x43, + 0x6d, + 0x75, + 0x74, + 0x65, + 0x37, + 0x6e, + 0x57, + 0x63, + 0x7a, + 0x75, + 0x4b, + 0x30, + 0x62, + 0x37, + 0x41, + 0x67, + 0x67, + 0x6b, + 0x79, + 0x6b, + 0x38, + 0x72, + 0x0a, + 0x79, + 0x4d, + 0x53, + 0x69, + 0x6f, + 0x6e, + 0x79, + 0x30, + 0x5a, + 0x58, + 0x64, + 0x38, + 0x52, + 0x66, + 0x55, + 0x67, + 0x70, + 0x6a, + 0x63, + 0x74, + 0x59, + 0x65, + 0x76, + 0x51, + 0x31, + 0x68, + 0x34, + 0x46, + 0x69, + 0x42, + 0x52, + 0x7a, + 0x6d, + 0x54, + 0x77, + 0x58, + 0x32, + 0x55, + 0x73, + 0x30, + 0x69, + 0x64, + 0x74, + 0x74, + 0x66, + 0x4c, + 0x67, + 0x76, + 0x35, + 0x46, + 0x75, + 0x36, + 0x33, + 0x77, + 0x35, + 0x36, + 0x34, + 0x62, + 0x57, + 0x66, + 0x67, + 0x57, + 0x30, + 0x41, + 0x0a, + 0x48, + 0x36, + 0x70, + 0x6a, + 0x4a, + 0x55, + 0x58, + 0x39, + 0x77, + 0x59, + 0x68, + 0x57, + 0x73, + 0x33, + 0x4e, + 0x75, + 0x50, + 0x57, + 0x7a, + 0x42, + 0x4f, + 0x6e, + 0x30, + 0x56, + 0x33, + 0x54, + 0x46, + 0x53, + 0x7a, + 0x71, + 0x39, + 0x78, + 0x44, + 0x2b, + 0x71, + 0x78, + 0x68, + 0x36, + 0x75, + 0x58, + 0x6f, + 0x38, + 0x74, + 0x4a, + 0x57, + 0x4e, + 0x67, + 0x59, + 0x65, + 0x33, + 0x77, + 0x77, + 0x41, + 0x30, + 0x64, + 0x44, + 0x69, + 0x74, + 0x47, + 0x6f, + 0x58, + 0x7a, + 0x58, + 0x4c, + 0x0a, + 0x74, + 0x37, + 0x39, + 0x76, + 0x4e, + 0x49, + 0x41, + 0x30, + 0x5a, + 0x2b, + 0x78, + 0x63, + 0x75, + 0x73, + 0x4b, + 0x37, + 0x58, + 0x6c, + 0x41, + 0x63, + 0x34, + 0x6a, + 0x64, + 0x4b, + 0x2f, + 0x55, + 0x75, + 0x74, + 0x5a, + 0x4f, + 0x67, + 0x45, + 0x51, + 0x78, + 0x57, + 0x59, + 0x73, + 0x63, + 0x6b, + 0x37, + 0x74, + 0x55, + 0x47, + 0x38, + 0x34, + 0x78, + 0x69, + 0x42, + 0x37, + 0x73, + 0x45, + 0x6b, + 0x6f, + 0x38, + 0x65, + 0x75, + 0x76, + 0x76, + 0x44, + 0x39, + 0x71, + 0x32, + 0x41, + 0x61, + 0x0a, + 0x4e, + 0x74, + 0x74, + 0x48, + 0x49, + 0x51, + 0x4b, + 0x43, + 0x41, + 0x51, + 0x42, + 0x44, + 0x77, + 0x30, + 0x5a, + 0x6e, + 0x56, + 0x52, + 0x2b, + 0x47, + 0x55, + 0x54, + 0x46, + 0x61, + 0x71, + 0x57, + 0x71, + 0x34, + 0x31, + 0x4a, + 0x44, + 0x48, + 0x72, + 0x30, + 0x38, + 0x35, + 0x72, + 0x6d, + 0x6b, + 0x38, + 0x68, + 0x71, + 0x71, + 0x4a, + 0x7a, + 0x76, + 0x72, + 0x54, + 0x5a, + 0x35, + 0x36, + 0x45, + 0x2b, + 0x32, + 0x4f, + 0x50, + 0x62, + 0x78, + 0x52, + 0x42, + 0x34, + 0x77, + 0x4f, + 0x32, + 0x0a, + 0x32, + 0x5a, + 0x39, + 0x63, + 0x32, + 0x4c, + 0x45, + 0x77, + 0x78, + 0x2b, + 0x4e, + 0x54, + 0x45, + 0x66, + 0x7a, + 0x52, + 0x2f, + 0x7a, + 0x48, + 0x46, + 0x41, + 0x57, + 0x61, + 0x2b, + 0x49, + 0x76, + 0x6f, + 0x46, + 0x79, + 0x75, + 0x44, + 0x7a, + 0x67, + 0x77, + 0x69, + 0x48, + 0x56, + 0x66, + 0x61, + 0x32, + 0x48, + 0x55, + 0x79, + 0x55, + 0x68, + 0x59, + 0x6f, + 0x7a, + 0x34, + 0x2b, + 0x4f, + 0x38, + 0x55, + 0x67, + 0x69, + 0x58, + 0x63, + 0x64, + 0x71, + 0x2b, + 0x4d, + 0x72, + 0x4b, + 0x33, + 0x0a, + 0x73, + 0x44, + 0x37, + 0x30, + 0x36, + 0x46, + 0x5a, + 0x4b, + 0x68, + 0x6c, + 0x63, + 0x52, + 0x55, + 0x68, + 0x32, + 0x51, + 0x2b, + 0x54, + 0x4b, + 0x32, + 0x4e, + 0x67, + 0x77, + 0x50, + 0x56, + 0x4c, + 0x2f, + 0x31, + 0x36, + 0x71, + 0x61, + 0x48, + 0x5a, + 0x5a, + 0x75, + 0x32, + 0x65, + 0x61, + 0x33, + 0x62, + 0x33, + 0x6e, + 0x5a, + 0x32, + 0x41, + 0x41, + 0x42, + 0x65, + 0x52, + 0x6f, + 0x51, + 0x47, + 0x68, + 0x42, + 0x4e, + 0x65, + 0x52, + 0x57, + 0x34, + 0x2b, + 0x4f, + 0x76, + 0x34, + 0x54, + 0x0a, + 0x59, + 0x55, + 0x31, + 0x33, + 0x59, + 0x51, + 0x50, + 0x6a, + 0x4f, + 0x74, + 0x76, + 0x42, + 0x70, + 0x41, + 0x2b, + 0x46, + 0x6d, + 0x56, + 0x48, + 0x72, + 0x51, + 0x49, + 0x5a, + 0x6c, + 0x43, + 0x4f, + 0x35, + 0x43, + 0x4d, + 0x79, + 0x36, + 0x2f, + 0x47, + 0x32, + 0x77, + 0x56, + 0x4b, + 0x48, + 0x62, + 0x31, + 0x4a, + 0x75, + 0x4d, + 0x48, + 0x63, + 0x4f, + 0x44, + 0x33, + 0x43, + 0x4d, + 0x70, + 0x43, + 0x6f, + 0x6e, + 0x45, + 0x44, + 0x44, + 0x32, + 0x36, + 0x6a, + 0x76, + 0x51, + 0x62, + 0x49, + 0x0a, + 0x43, + 0x4f, + 0x46, + 0x5a, + 0x37, + 0x4c, + 0x43, + 0x46, + 0x73, + 0x52, + 0x71, + 0x38, + 0x43, + 0x4a, + 0x35, + 0x4c, + 0x45, + 0x7a, + 0x33, + 0x69, + 0x62, + 0x4c, + 0x50, + 0x76, + 0x4b, + 0x50, + 0x78, + 0x51, + 0x67, + 0x6c, + 0x41, + 0x4f, + 0x75, + 0x37, + 0x62, + 0x44, + 0x44, + 0x74, + 0x62, + 0x79, + 0x4c, + 0x6d, + 0x58, + 0x49, + 0x4a, + 0x7a, + 0x76, + 0x51, + 0x33, + 0x65, + 0x31, + 0x30, + 0x49, + 0x31, + 0x45, + 0x77, + 0x39, + 0x53, + 0x61, + 0x56, + 0x76, + 0x59, + 0x39, + 0x65, + 0x0a, + 0x31, + 0x62, + 0x65, + 0x79, + 0x62, + 0x37, + 0x71, + 0x4f, + 0x31, + 0x36, + 0x44, + 0x62, + 0x73, + 0x54, + 0x75, + 0x6c, + 0x45, + 0x43, + 0x51, + 0x4e, + 0x2f, + 0x2b, + 0x79, + 0x4e, + 0x77, + 0x74, + 0x6b, + 0x44, + 0x34, + 0x58, + 0x69, + 0x37, + 0x41, + 0x6f, + 0x49, + 0x42, + 0x41, + 0x41, + 0x63, + 0x73, + 0x6f, + 0x4f, + 0x7a, + 0x75, + 0x50, + 0x6d, + 0x37, + 0x35, + 0x4a, + 0x4f, + 0x32, + 0x42, + 0x33, + 0x76, + 0x32, + 0x45, + 0x64, + 0x2b, + 0x37, + 0x47, + 0x4b, + 0x31, + 0x53, + 0x63, + 0x0a, + 0x6c, + 0x76, + 0x56, + 0x4e, + 0x55, + 0x33, + 0x43, + 0x7a, + 0x30, + 0x4e, + 0x66, + 0x4c, + 0x50, + 0x4a, + 0x56, + 0x79, + 0x73, + 0x56, + 0x53, + 0x48, + 0x65, + 0x67, + 0x61, + 0x53, + 0x5a, + 0x38, + 0x33, + 0x5a, + 0x6f, + 0x4e, + 0x50, + 0x68, + 0x6d, + 0x54, + 0x42, + 0x47, + 0x62, + 0x72, + 0x56, + 0x45, + 0x72, + 0x31, + 0x62, + 0x33, + 0x42, + 0x55, + 0x59, + 0x30, + 0x6c, + 0x6b, + 0x46, + 0x49, + 0x4f, + 0x6f, + 0x33, + 0x6d, + 0x66, + 0x30, + 0x32, + 0x6c, + 0x37, + 0x57, + 0x48, + 0x59, + 0x0a, + 0x78, + 0x4d, + 0x51, + 0x38, + 0x48, + 0x63, + 0x32, + 0x61, + 0x6e, + 0x55, + 0x38, + 0x71, + 0x64, + 0x4f, + 0x58, + 0x76, + 0x6f, + 0x58, + 0x62, + 0x51, + 0x34, + 0x2f, + 0x59, + 0x59, + 0x4b, + 0x31, + 0x57, + 0x42, + 0x76, + 0x4b, + 0x75, + 0x2f, + 0x43, + 0x6c, + 0x42, + 0x53, + 0x45, + 0x63, + 0x61, + 0x4f, + 0x39, + 0x30, + 0x62, + 0x71, + 0x79, + 0x44, + 0x73, + 0x37, + 0x33, + 0x6d, + 0x69, + 0x46, + 0x57, + 0x2f, + 0x43, + 0x31, + 0x54, + 0x66, + 0x78, + 0x50, + 0x58, + 0x6e, + 0x6b, + 0x53, + 0x0a, + 0x54, + 0x2b, + 0x72, + 0x4f, + 0x4d, + 0x6d, + 0x77, + 0x52, + 0x51, + 0x67, + 0x4c, + 0x46, + 0x50, + 0x6c, + 0x35, + 0x46, + 0x41, + 0x62, + 0x48, + 0x4f, + 0x6b, + 0x31, + 0x41, + 0x58, + 0x5a, + 0x45, + 0x34, + 0x46, + 0x46, + 0x47, + 0x50, + 0x75, + 0x55, + 0x62, + 0x52, + 0x74, + 0x6b, + 0x4f, + 0x45, + 0x72, + 0x64, + 0x77, + 0x34, + 0x74, + 0x5a, + 0x68, + 0x53, + 0x77, + 0x66, + 0x58, + 0x74, + 0x2f, + 0x46, + 0x41, + 0x36, + 0x2b, + 0x32, + 0x4c, + 0x6e, + 0x48, + 0x79, + 0x37, + 0x38, + 0x38, + 0x0a, + 0x58, + 0x4f, + 0x46, + 0x62, + 0x71, + 0x30, + 0x46, + 0x4b, + 0x6e, + 0x63, + 0x6e, + 0x68, + 0x32, + 0x6a, + 0x61, + 0x2b, + 0x64, + 0x37, + 0x65, + 0x4e, + 0x6c, + 0x48, + 0x77, + 0x4c, + 0x4b, + 0x63, + 0x53, + 0x43, + 0x34, + 0x43, + 0x46, + 0x4e, + 0x46, + 0x4b, + 0x51, + 0x6d, + 0x6c, + 0x45, + 0x68, + 0x5a, + 0x5a, + 0x4a, + 0x48, + 0x63, + 0x65, + 0x6e, + 0x2f, + 0x42, + 0x30, + 0x6d, + 0x7a, + 0x72, + 0x48, + 0x6c, + 0x35, + 0x76, + 0x53, + 0x6c, + 0x59, + 0x79, + 0x37, + 0x50, + 0x37, + 0x73, + 0x0a, + 0x63, + 0x32, + 0x52, + 0x34, + 0x33, + 0x54, + 0x6c, + 0x45, + 0x41, + 0x4d, + 0x77, + 0x72, + 0x32, + 0x4a, + 0x30, + 0x4c, + 0x37, + 0x58, + 0x65, + 0x34, + 0x2b, + 0x33, + 0x45, + 0x36, + 0x31, + 0x4f, + 0x64, + 0x43, + 0x4f, + 0x30, + 0x46, + 0x37, + 0x4e, + 0x46, + 0x30, + 0x76, + 0x33, + 0x66, + 0x2f, + 0x34, + 0x5a, + 0x6f, + 0x4a, + 0x49, + 0x6d, + 0x4c, + 0x74, + 0x4e, + 0x7a, + 0x4d, + 0x4f, + 0x31, + 0x68, + 0x69, + 0x61, + 0x51, + 0x34, + 0x69, + 0x67, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x52, + 0x53, + 0x41, + 0x20, + 0x50, + 0x52, + 0x49, + 0x56, + 0x41, + 0x54, + 0x45, + 0x20, + 0x4b, + 0x45, + 0x59, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +unsigned char server_cert[] = { 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x42, + 0x45, + 0x47, + 0x49, + 0x4e, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a, + 0x4d, + 0x49, + 0x49, + 0x45, + 0x47, + 0x7a, + 0x43, + 0x43, + 0x41, + 0x77, + 0x4f, + 0x67, + 0x41, + 0x77, + 0x49, + 0x42, + 0x41, + 0x67, + 0x49, + 0x44, + 0x45, + 0x41, + 0x41, + 0x44, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x43, + 0x53, + 0x71, + 0x47, + 0x53, + 0x49, + 0x62, + 0x33, + 0x44, + 0x51, + 0x45, + 0x42, + 0x43, + 0x77, + 0x55, + 0x41, + 0x4d, + 0x45, + 0x38, + 0x78, + 0x47, + 0x7a, + 0x41, + 0x5a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x6f, + 0x54, + 0x45, + 0x6d, + 0x78, + 0x70, + 0x0a, + 0x59, + 0x6e, + 0x64, + 0x6c, + 0x59, + 0x6e, + 0x4e, + 0x76, + 0x59, + 0x32, + 0x74, + 0x6c, + 0x64, + 0x48, + 0x4d, + 0x74, + 0x64, + 0x47, + 0x56, + 0x7a, + 0x64, + 0x44, + 0x45, + 0x53, + 0x4d, + 0x42, + 0x41, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x42, + 0x78, + 0x4d, + 0x4a, + 0x57, + 0x47, + 0x6c, + 0x68, + 0x62, + 0x32, + 0x4a, + 0x70, + 0x64, + 0x47, + 0x46, + 0x75, + 0x4d, + 0x51, + 0x38, + 0x77, + 0x44, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x49, + 0x45, + 0x77, + 0x5a, + 0x55, + 0x0a, + 0x59, + 0x57, + 0x6c, + 0x77, + 0x5a, + 0x57, + 0x6b, + 0x78, + 0x43, + 0x7a, + 0x41, + 0x4a, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x59, + 0x54, + 0x41, + 0x6c, + 0x52, + 0x58, + 0x4d, + 0x43, + 0x41, + 0x58, + 0x44, + 0x54, + 0x49, + 0x79, + 0x4d, + 0x44, + 0x63, + 0x77, + 0x4e, + 0x6a, + 0x45, + 0x78, + 0x4d, + 0x6a, + 0x55, + 0x77, + 0x4d, + 0x31, + 0x6f, + 0x59, + 0x44, + 0x7a, + 0x49, + 0x77, + 0x4e, + 0x54, + 0x41, + 0x77, + 0x4e, + 0x7a, + 0x45, + 0x35, + 0x4d, + 0x54, + 0x45, + 0x79, + 0x0a, + 0x4e, + 0x54, + 0x41, + 0x7a, + 0x57, + 0x6a, + 0x42, + 0x50, + 0x4d, + 0x51, + 0x73, + 0x77, + 0x43, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x47, + 0x45, + 0x77, + 0x4a, + 0x55, + 0x56, + 0x7a, + 0x45, + 0x50, + 0x4d, + 0x41, + 0x30, + 0x47, + 0x41, + 0x31, + 0x55, + 0x45, + 0x43, + 0x42, + 0x4d, + 0x47, + 0x56, + 0x47, + 0x46, + 0x70, + 0x63, + 0x47, + 0x56, + 0x70, + 0x4d, + 0x52, + 0x73, + 0x77, + 0x47, + 0x51, + 0x59, + 0x44, + 0x56, + 0x51, + 0x51, + 0x4b, + 0x45, + 0x78, + 0x4a, + 0x73, + 0x0a, + 0x61, + 0x57, + 0x4a, + 0x33, + 0x5a, + 0x57, + 0x4a, + 0x7a, + 0x62, + 0x32, + 0x4e, + 0x72, + 0x5a, + 0x58, + 0x52, + 0x7a, + 0x4c, + 0x58, + 0x52, + 0x6c, + 0x63, + 0x33, + 0x51, + 0x78, + 0x45, + 0x6a, + 0x41, + 0x51, + 0x42, + 0x67, + 0x4e, + 0x56, + 0x42, + 0x41, + 0x4d, + 0x54, + 0x43, + 0x57, + 0x78, + 0x76, + 0x59, + 0x32, + 0x46, + 0x73, + 0x61, + 0x47, + 0x39, + 0x7a, + 0x64, + 0x44, + 0x43, + 0x43, + 0x41, + 0x69, + 0x49, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x0a, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x42, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x49, + 0x50, + 0x41, + 0x44, + 0x43, + 0x43, + 0x41, + 0x67, + 0x6f, + 0x43, + 0x67, + 0x67, + 0x49, + 0x42, + 0x41, + 0x4d, + 0x74, + 0x58, + 0x71, + 0x68, + 0x69, + 0x59, + 0x68, + 0x47, + 0x6a, + 0x56, + 0x4c, + 0x77, + 0x56, + 0x75, + 0x61, + 0x79, + 0x76, + 0x4d, + 0x4d, + 0x49, + 0x72, + 0x6a, + 0x4b, + 0x37, + 0x53, + 0x34, + 0x4c, + 0x62, + 0x52, + 0x48, + 0x6c, + 0x4e, + 0x55, + 0x44, + 0x0a, + 0x74, + 0x4a, + 0x7a, + 0x46, + 0x6f, + 0x32, + 0x41, + 0x6a, + 0x75, + 0x4c, + 0x75, + 0x6d, + 0x74, + 0x65, + 0x46, + 0x2b, + 0x4b, + 0x4c, + 0x73, + 0x34, + 0x56, + 0x6a, + 0x59, + 0x7a, + 0x78, + 0x75, + 0x64, + 0x61, + 0x43, + 0x51, + 0x72, + 0x79, + 0x76, + 0x4d, + 0x61, + 0x6c, + 0x30, + 0x64, + 0x2f, + 0x38, + 0x32, + 0x38, + 0x75, + 0x6e, + 0x33, + 0x78, + 0x34, + 0x67, + 0x57, + 0x6a, + 0x59, + 0x54, + 0x4f, + 0x43, + 0x35, + 0x35, + 0x63, + 0x51, + 0x39, + 0x4d, + 0x44, + 0x4e, + 0x64, + 0x2b, + 0x0a, + 0x70, + 0x6d, + 0x4d, + 0x45, + 0x45, + 0x34, + 0x2b, + 0x64, + 0x31, + 0x75, + 0x6e, + 0x54, + 0x66, + 0x6f, + 0x39, + 0x38, + 0x38, + 0x32, + 0x6a, + 0x4d, + 0x38, + 0x65, + 0x6a, + 0x6c, + 0x4e, + 0x46, + 0x54, + 0x63, + 0x44, + 0x6f, + 0x54, + 0x50, + 0x68, + 0x6f, + 0x4e, + 0x37, + 0x77, + 0x71, + 0x66, + 0x78, + 0x4f, + 0x31, + 0x35, + 0x46, + 0x6e, + 0x69, + 0x54, + 0x77, + 0x54, + 0x43, + 0x57, + 0x39, + 0x30, + 0x62, + 0x6d, + 0x7a, + 0x37, + 0x32, + 0x74, + 0x36, + 0x7a, + 0x4c, + 0x4f, + 0x6d, + 0x0a, + 0x33, + 0x43, + 0x70, + 0x5a, + 0x75, + 0x75, + 0x64, + 0x48, + 0x53, + 0x6e, + 0x58, + 0x48, + 0x71, + 0x74, + 0x67, + 0x4f, + 0x7a, + 0x36, + 0x6e, + 0x53, + 0x53, + 0x62, + 0x54, + 0x6a, + 0x4f, + 0x31, + 0x51, + 0x52, + 0x33, + 0x4c, + 0x4e, + 0x2b, + 0x44, + 0x76, + 0x4e, + 0x30, + 0x34, + 0x62, + 0x4d, + 0x58, + 0x42, + 0x66, + 0x65, + 0x2f, + 0x30, + 0x4e, + 0x56, + 0x45, + 0x2f, + 0x51, + 0x70, + 0x65, + 0x4f, + 0x4f, + 0x47, + 0x50, + 0x5a, + 0x53, + 0x47, + 0x43, + 0x4f, + 0x74, + 0x4b, + 0x76, + 0x0a, + 0x44, + 0x5a, + 0x34, + 0x4f, + 0x75, + 0x79, + 0x4b, + 0x68, + 0x6d, + 0x32, + 0x52, + 0x6f, + 0x72, + 0x47, + 0x4a, + 0x6e, + 0x4c, + 0x6d, + 0x58, + 0x78, + 0x44, + 0x46, + 0x48, + 0x6d, + 0x42, + 0x4d, + 0x70, + 0x30, + 0x30, + 0x64, + 0x41, + 0x63, + 0x65, + 0x72, + 0x42, + 0x45, + 0x52, + 0x4c, + 0x44, + 0x6b, + 0x4c, + 0x52, + 0x70, + 0x64, + 0x57, + 0x34, + 0x38, + 0x4b, + 0x32, + 0x69, + 0x35, + 0x74, + 0x42, + 0x51, + 0x76, + 0x41, + 0x63, + 0x4a, + 0x5a, + 0x7a, + 0x6c, + 0x4c, + 0x38, + 0x51, + 0x0a, + 0x2b, + 0x48, + 0x32, + 0x66, + 0x67, + 0x75, + 0x53, + 0x62, + 0x42, + 0x32, + 0x32, + 0x65, + 0x55, + 0x79, + 0x74, + 0x42, + 0x62, + 0x62, + 0x4e, + 0x6e, + 0x67, + 0x76, + 0x36, + 0x68, + 0x79, + 0x52, + 0x79, + 0x30, + 0x62, + 0x41, + 0x49, + 0x7a, + 0x61, + 0x59, + 0x48, + 0x77, + 0x61, + 0x70, + 0x52, + 0x49, + 0x78, + 0x72, + 0x4c, + 0x54, + 0x57, + 0x6e, + 0x64, + 0x4d, + 0x69, + 0x42, + 0x37, + 0x79, + 0x4f, + 0x76, + 0x64, + 0x69, + 0x73, + 0x6c, + 0x55, + 0x2b, + 0x31, + 0x53, + 0x6f, + 0x46, + 0x0a, + 0x57, + 0x41, + 0x62, + 0x66, + 0x56, + 0x35, + 0x79, + 0x75, + 0x68, + 0x77, + 0x4f, + 0x6e, + 0x77, + 0x54, + 0x4d, + 0x53, + 0x6f, + 0x64, + 0x52, + 0x30, + 0x39, + 0x4f, + 0x78, + 0x67, + 0x55, + 0x61, + 0x75, + 0x41, + 0x32, + 0x79, + 0x72, + 0x41, + 0x4c, + 0x32, + 0x51, + 0x37, + 0x42, + 0x4d, + 0x35, + 0x47, + 0x6e, + 0x59, + 0x4c, + 0x70, + 0x55, + 0x6f, + 0x35, + 0x71, + 0x35, + 0x46, + 0x67, + 0x41, + 0x77, + 0x6b, + 0x73, + 0x4a, + 0x6c, + 0x38, + 0x77, + 0x79, + 0x53, + 0x44, + 0x70, + 0x59, + 0x0a, + 0x63, + 0x51, + 0x72, + 0x37, + 0x46, + 0x72, + 0x7a, + 0x53, + 0x6a, + 0x71, + 0x63, + 0x31, + 0x6f, + 0x39, + 0x55, + 0x73, + 0x37, + 0x68, + 0x45, + 0x45, + 0x6c, + 0x6b, + 0x76, + 0x4e, + 0x42, + 0x5a, + 0x6b, + 0x31, + 0x2b, + 0x56, + 0x50, + 0x54, + 0x61, + 0x50, + 0x35, + 0x78, + 0x45, + 0x59, + 0x79, + 0x70, + 0x63, + 0x62, + 0x67, + 0x2b, + 0x75, + 0x2b, + 0x59, + 0x43, + 0x7a, + 0x63, + 0x74, + 0x4a, + 0x57, + 0x6e, + 0x4a, + 0x4b, + 0x62, + 0x43, + 0x42, + 0x7a, + 0x32, + 0x65, + 0x73, + 0x36, + 0x0a, + 0x4b, + 0x6b, + 0x70, + 0x66, + 0x43, + 0x34, + 0x61, + 0x57, + 0x63, + 0x38, + 0x55, + 0x49, + 0x4c, + 0x6c, + 0x55, + 0x4b, + 0x2f, + 0x57, + 0x34, + 0x6a, + 0x52, + 0x46, + 0x73, + 0x4e, + 0x47, + 0x33, + 0x55, + 0x68, + 0x41, + 0x31, + 0x76, + 0x6e, + 0x49, + 0x5a, + 0x77, + 0x4b, + 0x38, + 0x36, + 0x69, + 0x71, + 0x6d, + 0x33, + 0x6b, + 0x2b, + 0x50, + 0x45, + 0x62, + 0x61, + 0x68, + 0x63, + 0x45, + 0x78, + 0x41, + 0x34, + 0x71, + 0x42, + 0x2f, + 0x4c, + 0x42, + 0x68, + 0x6f, + 0x67, + 0x76, + 0x73, + 0x0a, + 0x34, + 0x7a, + 0x39, + 0x62, + 0x32, + 0x4d, + 0x51, + 0x33, + 0x35, + 0x74, + 0x69, + 0x49, + 0x6f, + 0x51, + 0x44, + 0x6a, + 0x37, + 0x52, + 0x35, + 0x78, + 0x4e, + 0x38, + 0x63, + 0x35, + 0x41, + 0x79, + 0x77, + 0x44, + 0x79, + 0x62, + 0x38, + 0x78, + 0x47, + 0x76, + 0x32, + 0x59, + 0x6d, + 0x6b, + 0x34, + 0x6a, + 0x47, + 0x74, + 0x44, + 0x6b, + 0x64, + 0x71, + 0x72, + 0x62, + 0x74, + 0x49, + 0x55, + 0x34, + 0x32, + 0x2b, + 0x48, + 0x4d, + 0x31, + 0x79, + 0x4f, + 0x6b, + 0x38, + 0x75, + 0x38, + 0x5a, + 0x0a, + 0x45, + 0x69, + 0x78, + 0x4c, + 0x51, + 0x7a, + 0x6f, + 0x77, + 0x6d, + 0x71, + 0x55, + 0x71, + 0x72, + 0x65, + 0x42, + 0x43, + 0x68, + 0x6f, + 0x41, + 0x6f, + 0x73, + 0x63, + 0x33, + 0x46, + 0x4f, + 0x52, + 0x4f, + 0x6d, + 0x54, + 0x43, + 0x44, + 0x6c, + 0x59, + 0x36, + 0x33, + 0x34, + 0x2f, + 0x61, + 0x63, + 0x49, + 0x49, + 0x50, + 0x33, + 0x31, + 0x64, + 0x65, + 0x64, + 0x48, + 0x4e, + 0x59, + 0x5a, + 0x35, + 0x31, + 0x52, + 0x72, + 0x75, + 0x6b, + 0x41, + 0x64, + 0x41, + 0x36, + 0x32, + 0x44, + 0x62, + 0x0a, + 0x47, + 0x44, + 0x77, + 0x67, + 0x77, + 0x7a, + 0x36, + 0x54, + 0x41, + 0x67, + 0x4d, + 0x42, + 0x41, + 0x41, + 0x45, + 0x77, + 0x44, + 0x51, + 0x59, + 0x4a, + 0x4b, + 0x6f, + 0x5a, + 0x49, + 0x68, + 0x76, + 0x63, + 0x4e, + 0x41, + 0x51, + 0x45, + 0x4c, + 0x42, + 0x51, + 0x41, + 0x44, + 0x67, + 0x67, + 0x45, + 0x42, + 0x41, + 0x4b, + 0x56, + 0x76, + 0x2f, + 0x53, + 0x43, + 0x50, + 0x4f, + 0x67, + 0x77, + 0x50, + 0x41, + 0x59, + 0x30, + 0x47, + 0x34, + 0x37, + 0x79, + 0x63, + 0x76, + 0x55, + 0x48, + 0x67, + 0x0a, + 0x6b, + 0x33, + 0x44, + 0x51, + 0x67, + 0x6d, + 0x62, + 0x78, + 0x56, + 0x54, + 0x46, + 0x46, + 0x57, + 0x51, + 0x79, + 0x72, + 0x50, + 0x36, + 0x58, + 0x76, + 0x4b, + 0x48, + 0x4f, + 0x6c, + 0x33, + 0x65, + 0x5a, + 0x55, + 0x32, + 0x52, + 0x65, + 0x4a, + 0x78, + 0x65, + 0x54, + 0x78, + 0x43, + 0x4d, + 0x55, + 0x4b, + 0x63, + 0x4e, + 0x6c, + 0x57, + 0x34, + 0x2b, + 0x65, + 0x4d, + 0x32, + 0x6f, + 0x43, + 0x5a, + 0x37, + 0x57, + 0x31, + 0x76, + 0x69, + 0x74, + 0x61, + 0x75, + 0x70, + 0x6e, + 0x4f, + 0x30, + 0x0a, + 0x7a, + 0x4c, + 0x57, + 0x36, + 0x47, + 0x42, + 0x46, + 0x53, + 0x56, + 0x58, + 0x55, + 0x34, + 0x54, + 0x2b, + 0x59, + 0x5a, + 0x63, + 0x69, + 0x49, + 0x48, + 0x6e, + 0x78, + 0x6b, + 0x71, + 0x6c, + 0x48, + 0x33, + 0x58, + 0x78, + 0x63, + 0x6b, + 0x67, + 0x7a, + 0x4e, + 0x56, + 0x4c, + 0x79, + 0x57, + 0x44, + 0x41, + 0x41, + 0x4a, + 0x6f, + 0x61, + 0x77, + 0x32, + 0x63, + 0x2b, + 0x58, + 0x31, + 0x45, + 0x39, + 0x42, + 0x7a, + 0x59, + 0x62, + 0x49, + 0x54, + 0x2b, + 0x7a, + 0x4d, + 0x59, + 0x6a, + 0x5a, + 0x0a, + 0x31, + 0x62, + 0x59, + 0x41, + 0x61, + 0x76, + 0x79, + 0x51, + 0x4f, + 0x6f, + 0x4d, + 0x50, + 0x64, + 0x2f, + 0x31, + 0x7a, + 0x30, + 0x64, + 0x59, + 0x6d, + 0x49, + 0x39, + 0x46, + 0x72, + 0x77, + 0x78, + 0x5a, + 0x32, + 0x4f, + 0x42, + 0x6e, + 0x76, + 0x2b, + 0x63, + 0x58, + 0x41, + 0x72, + 0x69, + 0x30, + 0x2f, + 0x4a, + 0x4d, + 0x47, + 0x54, + 0x63, + 0x7a, + 0x58, + 0x36, + 0x66, + 0x56, + 0x67, + 0x53, + 0x45, + 0x50, + 0x47, + 0x66, + 0x48, + 0x48, + 0x63, + 0x6f, + 0x36, + 0x53, + 0x4b, + 0x4a, + 0x0a, + 0x52, + 0x5a, + 0x57, + 0x38, + 0x56, + 0x49, + 0x39, + 0x67, + 0x49, + 0x4f, + 0x52, + 0x75, + 0x57, + 0x68, + 0x79, + 0x39, + 0x76, + 0x78, + 0x55, + 0x57, + 0x41, + 0x43, + 0x4a, + 0x6f, + 0x30, + 0x4f, + 0x6b, + 0x4a, + 0x44, + 0x75, + 0x55, + 0x37, + 0x2b, + 0x57, + 0x54, + 0x61, + 0x39, + 0x65, + 0x44, + 0x66, + 0x6a, + 0x46, + 0x2b, + 0x62, + 0x62, + 0x68, + 0x2b, + 0x2f, + 0x49, + 0x73, + 0x38, + 0x36, + 0x66, + 0x72, + 0x4c, + 0x32, + 0x67, + 0x76, + 0x38, + 0x79, + 0x39, + 0x69, + 0x4d, + 0x55, + 0x0a, + 0x74, + 0x32, + 0x36, + 0x37, + 0x75, + 0x58, + 0x39, + 0x52, + 0x52, + 0x64, + 0x30, + 0x46, + 0x70, + 0x69, + 0x4b, + 0x70, + 0x2b, + 0x70, + 0x4b, + 0x4d, + 0x48, + 0x6d, + 0x50, + 0x77, + 0x47, + 0x33, + 0x70, + 0x75, + 0x6b, + 0x78, + 0x57, + 0x64, + 0x38, + 0x76, + 0x6b, + 0x4f, + 0x72, + 0x67, + 0x6a, + 0x4a, + 0x44, + 0x46, + 0x2b, + 0x54, + 0x56, + 0x48, + 0x67, + 0x67, + 0x4c, + 0x31, + 0x4a, + 0x59, + 0x58, + 0x5a, + 0x37, + 0x45, + 0x4a, + 0x39, + 0x79, + 0x6a, + 0x50, + 0x64, + 0x77, + 0x3d, + 0x0a, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x45, + 0x4e, + 0x44, + 0x20, + 0x43, + 0x45, + 0x52, + 0x54, + 0x49, + 0x46, + 0x49, + 0x43, + 0x41, + 0x54, + 0x45, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x2d, + 0x0a +#ifdef CONFIG_MBEDTLS + , + 0x00 +#endif +}; + +#ifndef BSC_NETWORK_IFACE +#define BSC_NETWORK_IFACE "127.0.0.1" +#endif + +#define BACNET_WEBSOCKET_SERVER_PORT 39000 +#ifdef ZEPHYR_TEST +#define BACNET_WEBSOCKET_SERVER_ADDR CONFIG_NET_CONFIG_MY_IPV4_ADDR +#else +#define BACNET_WEBSOCKET_SERVER_ADDR "127.0.0.1" +#endif +#define INFINITE_TIMEOUT 10000000 +#define DEFAULT_TIMEOUT 10 +#define DEFAULT_WAIT_TIMEOUT_MS 100 + +// MbedTLS expects a key in DER format +#ifdef CONFIG_MBEDTLS +#define CLIENT_KEY client_key_der +#else +#define CLIENT_KEY client_key +#endif + +typedef struct { + BSC_WEBSOCKET_EVENT ev; + BSC_WEBSOCKET_HANDLE h; + BSC_WEBSOCKET_SRV_HANDLE sh; + uint8_t in_buf[12 * 1024]; + size_t in_buf_size; + uint8_t out_buf[12 * 1024]; + size_t out_buf_size; +} test_ctx_t; + +static void wait_for_event(test_ctx_t *ctx, BSC_WEBSOCKET_EVENT ev) +{ + while (ctx->ev != ev) { +#ifdef ZEPHYR_TEST + k_msleep(DEFAULT_WAIT_TIMEOUT_MS); +#else + bsc_wait_ms(DEFAULT_WAIT_TIMEOUT_MS); +#endif + } + ctx->ev = -1; +} + +static void cli_event( + BSC_WEBSOCKET_HANDLE h, + BSC_WEBSOCKET_EVENT ev, + BACNET_ERROR_CODE ws_reason, + char *ws_reason_desc, + uint8_t *buf, + size_t bufsize, + void *user_param) +{ + test_ctx_t *ctx = (test_ctx_t *)user_param; + BSC_WEBSOCKET_RET ret; + (void)ws_reason; + (void)ws_reason_desc; + if (ev == BSC_WEBSOCKET_CONNECTED) { + ctx->ev = BSC_WEBSOCKET_CONNECTED; + } else if (ev == BSC_WEBSOCKET_SENDABLE) { + uint8_t *p = malloc(sizeof(ctx->out_buf) + BSC_CONF_TX_PRE); + memcpy(&p[BSC_CONF_TX_PRE], ctx->out_buf, ctx->out_buf_size); + ret = bws_cli_dispatch_send(h, &p[BSC_CONF_TX_PRE], ctx->out_buf_size); + zassert_equal(ret == BSC_WEBSOCKET_SUCCESS, true, 0); + free(p); + } else if (ev == BSC_WEBSOCKET_RECEIVED) { + zassert_equal(sizeof(ctx->in_buf) >= bufsize, true, NULL); + memcpy(ctx->in_buf, buf, bufsize); + ctx->in_buf_size = bufsize; + ctx->ev = BSC_WEBSOCKET_RECEIVED; + } else if (ev == BSC_WEBSOCKET_DISCONNECTED) { + ctx->ev = BSC_WEBSOCKET_DISCONNECTED; + } +} + +static void srv_event( + BSC_WEBSOCKET_SRV_HANDLE sh, + BSC_WEBSOCKET_HANDLE h, + BSC_WEBSOCKET_EVENT ev, + BACNET_ERROR_CODE ws_reason, + char *ws_reason_desc, + uint8_t *buf, + size_t bufsize, + void *user_param) +{ + test_ctx_t *ctx = (test_ctx_t *)user_param; + BSC_WEBSOCKET_RET ret; + (void)ws_reason; + (void)ws_reason_desc; + + if (ev == BSC_WEBSOCKET_SERVER_STARTED) { + ctx->ev = BSC_WEBSOCKET_SERVER_STARTED; + } else if (ev == BSC_WEBSOCKET_SERVER_STOPPED) { + ctx->ev = BSC_WEBSOCKET_SERVER_STOPPED; + } else if (ev == BSC_WEBSOCKET_CONNECTED) { + ctx->ev = BSC_WEBSOCKET_CONNECTED; + ctx->h = h; + } else if (ev == BSC_WEBSOCKET_SENDABLE) { + uint8_t *p = malloc(sizeof(ctx->out_buf) + BSC_CONF_TX_PRE); + memcpy(&p[BSC_CONF_TX_PRE], ctx->out_buf, ctx->out_buf_size); + ret = bws_srv_dispatch_send( + sh, h, &p[BSC_CONF_TX_PRE], ctx->out_buf_size); + zassert_equal(ret, BSC_WEBSOCKET_SUCCESS, NULL); + free(p); + } else if (ev == BSC_WEBSOCKET_RECEIVED) { + zassert_equal(sizeof(ctx->in_buf) >= bufsize, true, NULL); + memcpy(ctx->in_buf, buf, bufsize); + ctx->in_buf_size = bufsize; + ctx->ev = BSC_WEBSOCKET_RECEIVED; + } +} + +static void fill_buf(uint8_t *buf, size_t bufsize, uint8_t shift) +{ + size_t i; + for (i = 0; i < bufsize; i++) { + buf[i] = (uint8_t)(i + shift); + } +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(websocket_srv_test_1, test_simple) +#else +static void test_simple(void) +#endif +{ + BSC_WEBSOCKET_RET ret; + static test_ctx_t cli_ctx; + static test_ctx_t srv_ctx; + BSC_WEBSOCKET_HANDLE h; + char url[128]; + char ip_addr[128]; + uint16_t port; + bool res; + + memset(&cli_ctx, 0, sizeof(cli_ctx)); + cli_ctx.ev = -1; + cli_ctx.h = BSC_WEBSOCKET_INVALID_HANDLE; + memset(&srv_ctx, 0, sizeof(srv_ctx)); + srv_ctx.ev = -1; + srv_ctx.h = BSC_WEBSOCKET_INVALID_HANDLE; + sprintf( + url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + ret = bws_srv_start( + BSC_WEBSOCKET_HUB_PROTOCOL, BACNET_WEBSOCKET_SERVER_PORT, + BSC_NETWORK_IFACE, ca_cert, sizeof(ca_cert), server_cert, + sizeof(server_cert), server_key, sizeof(server_key), DEFAULT_TIMEOUT, + srv_event, &srv_ctx, &srv_ctx.sh); + + zassert_equal(ret, BSC_WEBSOCKET_SUCCESS, NULL); + wait_for_event(&srv_ctx, BSC_WEBSOCKET_SERVER_STARTED); + + ret = bws_cli_connect( + BSC_WEBSOCKET_HUB_PROTOCOL, url, ca_cert, sizeof(ca_cert), client_cert, + sizeof(client_cert), CLIENT_KEY, sizeof(CLIENT_KEY), DEFAULT_TIMEOUT, + cli_event, &cli_ctx, &h); + + zassert_equal(ret, BSC_WEBSOCKET_SUCCESS, NULL); + wait_for_event(&cli_ctx, BSC_WEBSOCKET_CONNECTED); + res = bws_srv_get_peer_ip_addr( + srv_ctx.sh, srv_ctx.h, (uint8_t *)ip_addr, sizeof(ip_addr), &port); + zassert_equal(res, true, NULL); + /*printf("client %s:%d connected.\n", ip_addr, port);*/ + /*printf("client sending data...\n");*/ + cli_ctx.out_buf_size = sizeof(cli_ctx.out_buf); + fill_buf(cli_ctx.out_buf, sizeof(cli_ctx.out_buf), 1); + bws_cli_send(h); + wait_for_event(&srv_ctx, BSC_WEBSOCKET_RECEIVED); + zassert_equal(srv_ctx.in_buf_size, cli_ctx.out_buf_size, NULL); + ret = memcmp(srv_ctx.in_buf, cli_ctx.out_buf, srv_ctx.in_buf_size); + zassert_equal(ret, 0, NULL); + + /*printf("server sending data...\n");*/ + srv_ctx.out_buf_size = sizeof(srv_ctx.out_buf); + fill_buf(srv_ctx.out_buf, sizeof(srv_ctx.out_buf), 2); + bws_srv_send(srv_ctx.sh, srv_ctx.h); + wait_for_event(&cli_ctx, BSC_WEBSOCKET_RECEIVED); + zassert_equal(cli_ctx.in_buf_size, srv_ctx.out_buf_size, NULL); + ret = memcmp(cli_ctx.in_buf, srv_ctx.out_buf, cli_ctx.in_buf_size); + zassert_equal(ret, 0, NULL); + + bws_cli_disconnect(h); + wait_for_event(&cli_ctx, BSC_WEBSOCKET_DISCONNECTED); + + ret = bws_srv_stop(srv_ctx.sh); + zassert_equal(ret, BSC_WEBSOCKET_SUCCESS, NULL); + wait_for_event(&srv_ctx, BSC_WEBSOCKET_SERVER_STOPPED); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(websocket_srv_test_2, test_onoff) +#else +static void test_onoff(void) +#endif +{ + BSC_WEBSOCKET_RET ret; + static test_ctx_t cli_ctx; + static test_ctx_t srv_ctx; + BSC_WEBSOCKET_HANDLE h; + char url[128]; + + memset(&cli_ctx, 0, sizeof(cli_ctx)); + cli_ctx.ev = -1; + cli_ctx.h = BSC_WEBSOCKET_INVALID_HANDLE; + memset(&srv_ctx, 0, sizeof(srv_ctx)); + srv_ctx.ev = -1; + srv_ctx.h = BSC_WEBSOCKET_INVALID_HANDLE; + sprintf( + url, "wss://%s:%d", BACNET_WEBSOCKET_SERVER_ADDR, + BACNET_WEBSOCKET_SERVER_PORT); + + ret = bws_srv_start( + BSC_WEBSOCKET_HUB_PROTOCOL, BACNET_WEBSOCKET_SERVER_PORT, NULL, ca_cert, + sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, + sizeof(server_key), DEFAULT_TIMEOUT, srv_event, &srv_ctx, &srv_ctx.sh); + zassert_equal(ret, BSC_WEBSOCKET_SUCCESS, NULL); + + ret = bws_srv_stop(srv_ctx.sh); + zassert_equal(ret, BSC_WEBSOCKET_SUCCESS, NULL); + wait_for_event(&srv_ctx, BSC_WEBSOCKET_SERVER_STOPPED); + + ret = bws_cli_connect( + BSC_WEBSOCKET_HUB_PROTOCOL, url, ca_cert, sizeof(ca_cert), client_cert, + sizeof(client_cert), CLIENT_KEY, sizeof(CLIENT_KEY), DEFAULT_TIMEOUT, + cli_event, &cli_ctx, &h); + bws_cli_disconnect(h); + wait_for_event(&cli_ctx, BSC_WEBSOCKET_DISCONNECTED); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST_SUITE(websocket_srv_test_1, NULL, NULL, NULL, NULL, NULL); +ZTEST_SUITE(websocket_srv_test_2, NULL, NULL, NULL, NULL, NULL); +#else +void test_main(void) +{ + // Tests must not be run in parallel threads! + // Thats why tests functions are in different suites. + + ztest_test_suite(websocket_srv_test_1, ztest_unit_test(test_simple)); + ztest_test_suite(websocket_srv_test_2, ztest_unit_test(test_onoff)); + ztest_run_test_suite(websocket_srv_test_1); + ztest_run_test_suite(websocket_srv_test_2); +} +#endif diff --git a/test/bacnet/delete_object/CMakeLists.txt b/test/bacnet/delete_object/CMakeLists.txt index 83b2ae48..4e704c0e 100644 --- a/test/bacnet/delete_object/CMakeLists.txt +++ b/test/bacnet/delete_object/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/event/CMakeLists.txt b/test/bacnet/event/CMakeLists.txt index 2ba2e747..08afbf57 100644 --- a/test/bacnet/event/CMakeLists.txt +++ b/test/bacnet/event/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/getalarm/CMakeLists.txt b/test/bacnet/getalarm/CMakeLists.txt index 6d0bb26a..2e5f0140 100644 --- a/test/bacnet/getalarm/CMakeLists.txt +++ b/test/bacnet/getalarm/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/getevent/CMakeLists.txt b/test/bacnet/getevent/CMakeLists.txt index f2038a65..cfc6d5e6 100644 --- a/test/bacnet/getevent/CMakeLists.txt +++ b/test/bacnet/getevent/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/hostnport/CMakeLists.txt b/test/bacnet/hostnport/CMakeLists.txt index 510084e1..1172d907 100644 --- a/test/bacnet/hostnport/CMakeLists.txt +++ b/test/bacnet/hostnport/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/list_element/CMakeLists.txt b/test/bacnet/list_element/CMakeLists.txt index e650161a..10c2f6ad 100644 --- a/test/bacnet/list_element/CMakeLists.txt +++ b/test/bacnet/list_element/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/lso/CMakeLists.txt b/test/bacnet/lso/CMakeLists.txt index dd3fbf76..b31eab45 100644 --- a/test/bacnet/lso/CMakeLists.txt +++ b/test/bacnet/lso/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/ptransfer/CMakeLists.txt b/test/bacnet/ptransfer/CMakeLists.txt index 4a4f64f3..272b825a 100644 --- a/test/bacnet/ptransfer/CMakeLists.txt +++ b/test/bacnet/ptransfer/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/rpm/CMakeLists.txt b/test/bacnet/rpm/CMakeLists.txt index d753ee0c..adc4ef5f 100644 --- a/test/bacnet/rpm/CMakeLists.txt +++ b/test/bacnet/rpm/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/secure_connect/CMakeLists.txt b/test/bacnet/secure_connect/CMakeLists.txt new file mode 100644 index 00000000..c20ad668 --- /dev/null +++ b/test/bacnet/secure_connect/CMakeLists.txt @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +add_executable(${PROJECT_NAME} + # File(s) under test + ${SRC_DIR}/bacnet/secure_connect.c + # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/bacaction.c + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/calendar_entry.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/dailyschedule.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/bacnet/secure_connect/src/main.c b/test/bacnet/secure_connect/src/main.c new file mode 100644 index 00000000..a71476c8 --- /dev/null +++ b/test/bacnet/secure_connect/src/main.c @@ -0,0 +1,75 @@ +/** + * @file + * @brief Unit test for BACnetSpecialEvent. This test also indirectly tests + * BACnetCalendarEntry + * @author Ondřej Hruška + * @date Aug 2023 + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include +#include +#include "bacnet/secure_connect.h" +#include "bacnet/datetime.h" + +/** + * @addtogroup bacnet_tests + * @{ + */ + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(BACnet_Secure_Connect_Tests, test_BACnet_Secure_Connect) +#else +static void test_BACnet_Secure_Connect(void) +#endif +{ + int apdu_len = 0, test_len = 0, null_len = 0, diff = 0; + uint8_t apdu[MAX_APDU] = { 0 }; + BACNET_SC_HUB_CONNECTION_STATUS data = { 0 }, test_data; + + data.State = BACNET_SC_CONNECTION_STATE_CONNECTED; + datetime_init_ascii(&data.Connect_Timestamp, "2023/08/01-12:00:00"); + datetime_init_ascii(&data.Disconnect_Timestamp, "2023/08/02-12:00:00"); + data.Error = ERROR_CODE_DEFAULT; + data.Error_Details[0] = 0; + + null_len = bacapp_encode_SCHubConnection(NULL, &data); + apdu_len = bacapp_encode_SCHubConnection(apdu, &data); + zassert_true(null_len == apdu_len, NULL); + + test_len = bacapp_decode_SCHubConnection(apdu, apdu_len, &test_data); + zassert_true(test_len == apdu_len, NULL); + zassert_true(test_data.State == data.State, NULL); + diff = + datetime_compare(&test_data.Connect_Timestamp, &data.Connect_Timestamp); + zassert_equal(diff, 0, NULL); + diff = datetime_compare( + &test_data.Disconnect_Timestamp, &data.Disconnect_Timestamp); + zassert_equal(diff, 0, NULL); + zassert_true(test_data.Error == data.Error, NULL); + diff = strcmp(test_data.Error_Details, data.Error_Details); + zassert_equal(diff, 0, NULL); +} + +/** + * @} + */ + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST_SUITE(BACnet_Secure_Connect_Tests, NULL, NULL, NULL, NULL, NULL); +#else +void test_main(void) +{ + ztest_test_suite( + BACnet_Secure_Connect_Tests, + ztest_unit_test(test_BACnet_Secure_Connect)); + + ztest_run_test_suite(BACnet_Secure_Connect_Tests); +} +#endif diff --git a/test/bacnet/specialevent/CMakeLists.txt b/test/bacnet/specialevent/CMakeLists.txt index a9f858c3..fcbc5a81 100644 --- a/test/bacnet/specialevent/CMakeLists.txt +++ b/test/bacnet/specialevent/CMakeLists.txt @@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/weeklyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/timesync/CMakeLists.txt b/test/bacnet/timesync/CMakeLists.txt index ae1df5f5..bb573965 100644 --- a/test/bacnet/timesync/CMakeLists.txt +++ b/test/bacnet/timesync/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/weeklyschedule/CMakeLists.txt b/test/bacnet/weeklyschedule/CMakeLists.txt index b5fb1100..68a4a9cd 100644 --- a/test/bacnet/weeklyschedule/CMakeLists.txt +++ b/test/bacnet/weeklyschedule/CMakeLists.txt @@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/wp/CMakeLists.txt b/test/bacnet/wp/CMakeLists.txt index 6aaa2f88..41c7483b 100644 --- a/test/bacnet/wp/CMakeLists.txt +++ b/test/bacnet/wp/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/wpm/CMakeLists.txt b/test/bacnet/wpm/CMakeLists.txt index c362c2d8..f3728736 100644 --- a/test/bacnet/wpm/CMakeLists.txt +++ b/test/bacnet/wpm/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c ${SRC_DIR}/bacnet/channel_value.c + ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/write_group/CMakeLists.txt b/test/bacnet/write_group/CMakeLists.txt index 79c08c53..1465fed5 100644 --- a/test/bacnet/write_group/CMakeLists.txt +++ b/test/bacnet/write_group/CMakeLists.txt @@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/secure_connect.c ${SRC_DIR}/bacnet/channel_value.c # Test and test library files ./src/main.c diff --git a/test/ports/bsd/bsc_event/CMakeLists.txt b/test/ports/bsd/bsc_event/CMakeLists.txt new file mode 100644 index 00000000..48ca4d6d --- /dev/null +++ b/test/ports/bsd/bsc_event/CMakeLists.txt @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +find_package(Threads) +find_package(PkgConfig) + +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +message(SRC ${SRC_DIR}) +message(STATUS "bsc_event test: building for APPLE") +set(BACNET_PORT_DIRECTORY_PATH ${PORTS_DIR}) + +add_executable(${PROJECT_NAME} + ${PORTS_DIR}/bsd/bsc-event.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/ports/bsd/bsc_event/src/main.c b/test/ports/bsd/bsc_event/src/main.c new file mode 100644 index 00000000..19f669ac --- /dev/null +++ b/test/ports/bsd/bsc_event/src/main.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2022 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +/* @file + * @brief test of bsc-event interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + STAGE_NONE, + STAGE_WAIT_1, + STAGE_WAIT_2, + STAGE_TIMEDWAIT_TIMEOUT, + STAGE_TIMEDWAIT_OK, +} TEST_STAGE; +static TEST_STAGE test_stage = STAGE_NONE; + +#define TIMEOUT_CHILD 400 +#define TIMEOUT_MIN 200 +#define TIMEOUT_MAX 600 +#define MSEC_PER_SEC 1000 +#define USEC_PER_MSEC 1000000 +#define TIMEOUT_SLEEP 2 +#define WAITTIME_MIN (TIMEOUT_SLEEP * MSEC_PER_SEC - 100) +#define WAITTIME_MAX (TIMEOUT_SLEEP * MSEC_PER_SEC + 100) +#define MULTIPLE_WAIT_THREADS_NUM 50 + +static void *child_func(void *arg) +{ + BSC_EVENT *event = (BSC_EVENT *)arg; + zassert_not_null(event, NULL); + + while (test_stage != STAGE_WAIT_1) { + usleep(10); + } + bsc_event_signal(event); + + while (test_stage != STAGE_WAIT_2) { + usleep(10); + } + bsc_event_signal(event); + + while (test_stage != STAGE_TIMEDWAIT_TIMEOUT) { + usleep(10); + } + usleep(1000 * TIMEOUT_CHILD); + bsc_event_signal(event); + + while (test_stage != STAGE_TIMEDWAIT_OK) { + usleep(10); + } + usleep(1000 * TIMEOUT_CHILD); + bsc_event_signal(event); + + return NULL; +} + +static void test_bsc_event1(void) +{ + BSC_EVENT *event; + pthread_t tid_child; + bool b; + struct timespec t1; + struct timespec t2; + int64_t timediff; + + test_stage = STAGE_NONE; + event = bsc_event_init(); + zassert_not_null(event, NULL); + + // run child and wait when child running + zassert_equal( + pthread_create(&tid_child, NULL, &child_func, event), 0, NULL); + + test_stage = STAGE_WAIT_1; + bsc_event_wait(event); + + test_stage = STAGE_WAIT_2; + bsc_event_wait(event); + + test_stage = STAGE_TIMEDWAIT_TIMEOUT; + b = bsc_event_timedwait(event, TIMEOUT_MIN); + zassert_false(b, NULL); + + test_stage = STAGE_TIMEDWAIT_OK; + b = bsc_event_timedwait(event, TIMEOUT_MAX); + zassert_true(b, NULL); + + clock_gettime(CLOCK_REALTIME, &t1); + bsc_wait(TIMEOUT_SLEEP); + clock_gettime(CLOCK_REALTIME, &t2); + timediff = (t2.tv_sec * MSEC_PER_SEC + t2.tv_nsec / USEC_PER_MSEC) - + (t1.tv_sec * MSEC_PER_SEC + t1.tv_nsec / USEC_PER_MSEC); + zassert_true(timediff >= TIMEOUT_SLEEP * MSEC_PER_SEC, NULL); + pthread_join(tid_child, NULL); + bsc_event_deinit(event); +} + +static void *thread_func(void *arg) +{ + BSC_EVENT *event = (BSC_EVENT *)arg; + zassert_not_null(event, NULL); + bsc_event_wait(event); + return NULL; +} + +static void test_bsc_event2(void) +{ + BSC_EVENT *event; + pthread_t tid[MULTIPLE_WAIT_THREADS_NUM]; + int i; + + event = bsc_event_init(); + zassert_not_null(event, NULL); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + zassert_equal( + pthread_create(&tid[i], NULL, &thread_func, event), 0, NULL); + } + + bsc_wait(1); + bsc_event_signal(event); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + pthread_join(tid[i], NULL); + } + + bsc_event_deinit(event); +} + +typedef struct { + BSC_EVENT *event; + bool result; +} test_param_t; + +static void *thread_func2(void *arg) +{ + test_param_t *p = (test_param_t *)arg; + zassert_not_null(p->event, NULL); + // use some big timeout value, 24 hours seems to be enough + p->result = bsc_event_timedwait(p->event, 24 * 60 * 60 * 1000); + return NULL; +} + +static void test_bsc_event3(void) +{ + BSC_EVENT *event; + pthread_t tid[MULTIPLE_WAIT_THREADS_NUM]; + test_param_t results[MULTIPLE_WAIT_THREADS_NUM]; + int i; + + event = bsc_event_init(); + zassert_not_null(event, NULL); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + results[i].event = event; + results[i].result = false; + zassert_equal( + pthread_create(&tid[i], NULL, &thread_func2, &results[i]), 0, NULL); + } + + bsc_wait(1); + bsc_event_signal(event); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + pthread_join(tid[i], NULL); + } + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + zassert_equal(results[i].result == true, true, NULL); + } + + bsc_event_deinit(event); +} + +void test_main(void) +{ + ztest_test_suite(bsc_event_test1, ztest_unit_test(test_bsc_event1)); + ztest_test_suite(bsc_event_test2, ztest_unit_test(test_bsc_event2)); + ztest_test_suite(bsc_event_test3, ztest_unit_test(test_bsc_event3)); + ztest_run_test_suite(bsc_event_test1); + ztest_run_test_suite(bsc_event_test2); + ztest_run_test_suite(bsc_event_test3); +} diff --git a/test/ports/linux/bsc_event/CMakeLists.txt b/test/ports/linux/bsc_event/CMakeLists.txt new file mode 100644 index 00000000..3df046fd --- /dev/null +++ b/test/ports/linux/bsc_event/CMakeLists.txt @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +find_package(Threads) +find_package(PkgConfig) + +set(CMAKE_C_FLAGS -pthread) + +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +message(STATUS "bsc_event test: building for linux") +set(BACNET_PORT_DIRECTORY_PATH ${PORTS_DIR}) + +add_executable(${PROJECT_NAME} + ${PORTS_DIR}/linux/bsc-event.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/ports/linux/bsc_event/src/main.c b/test/ports/linux/bsc_event/src/main.c new file mode 100644 index 00000000..3031805d --- /dev/null +++ b/test/ports/linux/bsc_event/src/main.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2022 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +/* @file + * @brief test of bsc-event interface + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + STAGE_NONE, + STAGE_WAIT_1, + STAGE_WAIT_2, + STAGE_TIMEDWAIT_TIMEOUT, + STAGE_TIMEDWAIT_OK, +} TEST_STAGE; +static TEST_STAGE test_stage = STAGE_NONE; + +#define TIMEOUT_CHILD 400 +#define TIMEOUT_MIN 200 +#define TIMEOUT_MAX 600 +#define MSEC_PER_SEC 1000 +#define USEC_PER_MSEC 1000000 +#define TIMEOUT_SLEEP 2 +#define WAITTIME_MIN (TIMEOUT_SLEEP * MSEC_PER_SEC - 100) +#define WAITTIME_MAX (TIMEOUT_SLEEP * MSEC_PER_SEC + 100) +#define MULTIPLE_WAIT_THREADS_NUM 50 + +static void *child_func(void *arg) +{ + BSC_EVENT *event = (BSC_EVENT *)arg; + zassert_not_null(event, NULL); + + while (test_stage != STAGE_WAIT_1) { + usleep(10); + } + bsc_event_signal(event); + + while (test_stage != STAGE_WAIT_2) { + usleep(10); + } + bsc_event_signal(event); + + while (test_stage != STAGE_TIMEDWAIT_TIMEOUT) { + usleep(10); + } + usleep(1000 * TIMEOUT_CHILD); + bsc_event_signal(event); + + while (test_stage != STAGE_TIMEDWAIT_OK) { + usleep(10); + } + usleep(1000 * TIMEOUT_CHILD); + bsc_event_signal(event); + + return NULL; +} + +static void test_bsc_event1(void) +{ + BSC_EVENT *event; + pthread_t tid_child; + bool b; + struct timespec t1; + struct timespec t2; + int64_t timediff; + + test_stage = STAGE_NONE; + event = bsc_event_init(); + zassert_not_null(event, NULL); + + // run child and wait when child running + zassert_equal( + pthread_create(&tid_child, NULL, &child_func, event), 0, NULL); + + test_stage = STAGE_WAIT_1; + bsc_event_wait(event); + + test_stage = STAGE_WAIT_2; + bsc_event_wait(event); + + test_stage = STAGE_TIMEDWAIT_TIMEOUT; + b = bsc_event_timedwait(event, TIMEOUT_MIN); + zassert_false(b, NULL); + + test_stage = STAGE_TIMEDWAIT_OK; + b = bsc_event_timedwait(event, TIMEOUT_MAX); + zassert_true(b, NULL); + + clock_gettime(CLOCK_REALTIME, &t1); + bsc_wait(TIMEOUT_SLEEP); + clock_gettime(CLOCK_REALTIME, &t2); + timediff = (t2.tv_sec * MSEC_PER_SEC + t2.tv_nsec / USEC_PER_MSEC) - + (t1.tv_sec * MSEC_PER_SEC + t1.tv_nsec / USEC_PER_MSEC); + zassert_true(timediff >= TIMEOUT_SLEEP * MSEC_PER_SEC, NULL); + pthread_join(tid_child, NULL); + bsc_event_deinit(event); +} + +static void *thread_func(void *arg) +{ + BSC_EVENT *event = (BSC_EVENT *)arg; + zassert_not_null(event, NULL); + bsc_event_wait(event); + return NULL; +} + +static void test_bsc_event2(void) +{ + BSC_EVENT *event; + pthread_t tid[MULTIPLE_WAIT_THREADS_NUM]; + int i; + + event = bsc_event_init(); + zassert_not_null(event, NULL); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + zassert_equal( + pthread_create(&tid[i], NULL, &thread_func, event), 0, NULL); + } + + bsc_wait(1); + bsc_event_signal(event); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + pthread_join(tid[i], NULL); + } + + bsc_event_deinit(event); +} + +typedef struct { + BSC_EVENT *event; + bool result; +} test_param_t; + +static void *thread_func2(void *arg) +{ + test_param_t *p = (test_param_t *)arg; + zassert_not_null(p->event, NULL); + // use some big timeout value, 24 hours seems to be enough + p->result = bsc_event_timedwait(p->event, 24 * 60 * 60 * 1000); + return NULL; +} + +static void test_bsc_event3(void) +{ + BSC_EVENT *event; + pthread_t tid[MULTIPLE_WAIT_THREADS_NUM]; + test_param_t results[MULTIPLE_WAIT_THREADS_NUM]; + int i; + + event = bsc_event_init(); + zassert_not_null(event, NULL); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + results[i].event = event; + results[i].result = false; + zassert_equal( + pthread_create(&tid[i], NULL, &thread_func2, &results[i]), 0, NULL); + } + + bsc_wait(1); + bsc_event_signal(event); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + pthread_join(tid[i], NULL); + } + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + zassert_equal(results[i].result == true, true, NULL); + } + + bsc_event_deinit(event); +} + +void test_main(void) +{ + ztest_test_suite(bsc_event_test1, ztest_unit_test(test_bsc_event1)); + ztest_test_suite(bsc_event_test2, ztest_unit_test(test_bsc_event2)); + ztest_test_suite(bsc_event_test3, ztest_unit_test(test_bsc_event3)); + ztest_run_test_suite(bsc_event_test1); + ztest_run_test_suite(bsc_event_test2); + ztest_run_test_suite(bsc_event_test3); +} diff --git a/test/ports/win32/bsc_event/CMakeLists.txt b/test/ports/win32/bsc_event/CMakeLists.txt new file mode 100644 index 00000000..db0b52e7 --- /dev/null +++ b/test/ports/win32/bsc_event/CMakeLists.txt @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +string(REGEX REPLACE + "/test/ports/[a-z0-9A-Z_/\\-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/ports/[a-z0-9A-Z_/\\-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/ports/[a-z0-9A-Z_/\\-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +message(STATUS "bsc_event test: building for win32") +set(BACNET_PORT_DIRECTORY_PATH ${PORTS_DIR}) +add_compile_definitions(BACNET_PORT=win32) + +add_executable(${PROJECT_NAME} + ${PORTS_DIR}/win32/bsc-event.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/ports/win32/bsc_event/src/main.c b/test/ports/win32/bsc_event/src/main.c new file mode 100644 index 00000000..ad33f0c9 --- /dev/null +++ b/test/ports/win32/bsc_event/src/main.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2022 Legrand North America, LLC. + * + * SPDX-License-Identifier: MIT + */ + +/* @file + * @brief test of bsc-event interface + */ + +#include +#include +#include +#include +#include +#include + +typedef enum { + STAGE_NONE, + STAGE_WAIT_1, + STAGE_WAIT_2, + STAGE_TIMEDWAIT_TIMEOUT, + STAGE_TIMEDWAIT_OK, +} TEST_STAGE; +static TEST_STAGE test_stage = STAGE_NONE; + +#define TIMEOUT_CHILD 400 +#define TIMEOUT_MIN 200 +#define TIMEOUT_MAX 600 +#define TIMEOUT_SLEEP 2 +#define WAITTIME_MIN (TIMEOUT_SLEEP * 1000 - 100) +#define WAITTIME_MAX (TIMEOUT_SLEEP * 1000 + 100) +#define MULTIPLE_WAIT_THREADS_NUM 50 + +DWORD WINAPI child_func(LPVOID lpParam) +{ + BSC_EVENT *event = (BSC_EVENT *)lpParam; + zassert_not_null(event, NULL); + + while (test_stage != STAGE_WAIT_1) { + Sleep(1); + } + bsc_event_signal(event); + + while (test_stage != STAGE_WAIT_2) { + Sleep(1); + } + bsc_event_signal(event); + + while (test_stage != STAGE_TIMEDWAIT_TIMEOUT) { + Sleep(1); + } + Sleep(TIMEOUT_CHILD); + bsc_event_signal(event); + + while (test_stage != STAGE_TIMEDWAIT_OK) { + Sleep(1); + } + + Sleep(TIMEOUT_CHILD); + bsc_event_signal(event); + + return 0; +} + +static void test_bsc_event1(void) +{ + BSC_EVENT *event; + HANDLE thread; + DWORD threadID; + bool b; + ULONGLONG time1; + ULONGLONG time2; + ULONGLONG timediff; + + test_stage = STAGE_NONE; + event = bsc_event_init(); + zassert_not_null(event, NULL); + + // run child and wait when child running + thread = CreateThread( + NULL, // default security attributes + 0, // default stack size + (LPTHREAD_START_ROUTINE)child_func, event, + 0, // default creation flags + &threadID); // receive thread identifier + zassert_not_null(thread, NULL); + + test_stage = STAGE_WAIT_1; + bsc_event_wait(event); + + test_stage = STAGE_WAIT_2; + bsc_event_wait(event); + + test_stage = STAGE_TIMEDWAIT_TIMEOUT; + b = bsc_event_timedwait(event, TIMEOUT_MIN); + zassert_false(b, NULL); + + test_stage = STAGE_TIMEDWAIT_OK; + b = bsc_event_timedwait(event, TIMEOUT_MAX); + zassert_true(b, NULL); + + time1 = GetTickCount64(); + bsc_wait(TIMEOUT_SLEEP); + time2 = GetTickCount64(); + timediff = time2 - time1; + zassert_true(timediff >= TIMEOUT_SLEEP * 1000, NULL); + + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + bsc_event_deinit(event); +} + +DWORD WINAPI thread_func(LPVOID lpParam) +{ + BSC_EVENT *event = (BSC_EVENT *)lpParam; + zassert_not_null(event, NULL); + bsc_event_wait(event); + return 0; +} + +static void test_bsc_event2(void) +{ + int i; + BSC_EVENT *event; + DWORD tid[MULTIPLE_WAIT_THREADS_NUM]; + HANDLE thread[MULTIPLE_WAIT_THREADS_NUM]; + + event = bsc_event_init(); + zassert_not_null(event, NULL); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + thread[i] = CreateThread( + NULL, // default security attributes + 0, // default stack size + (LPTHREAD_START_ROUTINE)thread_func, event, + 0, // default creation flags + &tid[i]); // receive thread identifier + zassert_not_null(thread[i], NULL); + } + + bsc_wait(1); + bsc_event_signal(event); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + WaitForSingleObject(thread[i], INFINITE); + CloseHandle(thread[i]); + } + + bsc_event_deinit(event); +} + +typedef struct { + BSC_EVENT *event; + BOOL result; +} test_param_t; + +DWORD WINAPI thread_func2(LPVOID lpParam) +{ + test_param_t *p = (test_param_t *)lpParam; + zassert_not_null(p->event, NULL); + // use some big timeout value, 24 hours seems to be enough + p->result = bsc_event_timedwait(p->event, 24 * 60 * 60 * 1000); + return 0; +} + +static void test_bsc_event3(void) +{ + int i; + BSC_EVENT *event; + DWORD tid[MULTIPLE_WAIT_THREADS_NUM]; + HANDLE thread[MULTIPLE_WAIT_THREADS_NUM]; + test_param_t results[MULTIPLE_WAIT_THREADS_NUM]; + + event = bsc_event_init(); + zassert_not_null(event, NULL); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + results[i].event = event; + results[i].result = FALSE; + thread[i] = CreateThread( + NULL, // default security attributes + 0, // default stack size + (LPTHREAD_START_ROUTINE)thread_func2, &results[i], + 0, // default creation flags + &tid[i]); // receive thread identifier + zassert_not_null(thread[i], NULL); + } + + bsc_wait(1); + bsc_event_signal(event); + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + WaitForSingleObject(thread[i], INFINITE); + CloseHandle(thread[i]); + } + + for (i = 0; i < MULTIPLE_WAIT_THREADS_NUM; i++) { + zassert_equal(results[i].result == true, true, NULL); + } + + bsc_event_deinit(event); +} + +void test_main(void) +{ + ztest_test_suite(bsc_event_test1, ztest_unit_test(test_bsc_event1)); + ztest_test_suite(bsc_event_test2, ztest_unit_test(test_bsc_event2)); + ztest_test_suite(bsc_event_test3, ztest_unit_test(test_bsc_event3)); + ztest_run_test_suite(bsc_event_test1); + ztest_run_test_suite(bsc_event_test2); + ztest_run_test_suite(bsc_event_test3); +} diff --git a/test/ztest/src/ztest.c b/test/ztest/src/ztest.c index a4bb4817..cc8a6555 100644 --- a/test/ztest/src/ztest.c +++ b/test/ztest/src/ztest.c @@ -32,7 +32,7 @@ ZTEST_DMEM enum { TEST_PHASE_FRAMEWORK } phase = TEST_PHASE_FRAMEWORK; -static ZTEST_BMEM int test_status; +static ZTEST_BMEM int test_status = 0; /** * @brief Try to shorten a filename by removing the current directory