diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5afed159ec..5fc822e155 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: - name: Mac Intel runner: macos-13 - name: Mac Apple Silicon - runner: macos-15 + runner: macos-14 compiler: [ gcc, clang ] config: - name: Shared OpenSSL @@ -123,10 +123,10 @@ jobs: export CXX="clang++" fi echo "Using CC: $CC and CXX: $CXX" - + mkdir -p build cd build - + if [[ "${{ matrix.compiler }}" == "gcc" ]]; then # Skip building the tests for Mac with GCC # https://github.com/awslabs/aws-crt-cpp/issues/605 @@ -246,7 +246,7 @@ jobs: run: | mkdir build cd build - cmake .. -DBUILD_TEST=ON -DENABLE_AWS_SDK_IN_TESTS=OFF -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON + cmake .. -DBUILD_TEST=ON -DENABLE_AWS_SDK_IN_TESTS=OFF -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON -DBUILD_OLD_MBEDTLS_VERSION=ON make -j - name: Configure AWS Credentials @@ -350,11 +350,13 @@ jobs: steps: - name: Clone repository uses: actions/checkout@v4 - - name: Move cloned repo + - name: Move cloned repo to shorter path shell: powershell run: | - mkdir C:\webrtc - Move-Item -Path "D:\a\amazon-kinesis-video-streams-webrtc-sdk-c\amazon-kinesis-video-streams-webrtc-sdk-c\*" -Destination "C:\webrtc" + $src = "${env:GITHUB_WORKSPACE}\*" + $dest = "C:\webrtc" + mkdir "$dest" + Move-Item -Path $src -Destination $dest - name: Install dependencies shell: powershell run: | diff --git a/CMake/Dependencies/libkvsCommonLws-CMakeLists.txt b/CMake/Dependencies/libkvsCommonLws-CMakeLists.txt index 4737705531..5cd8f1df67 100644 --- a/CMake/Dependencies/libkvsCommonLws-CMakeLists.txt +++ b/CMake/Dependencies/libkvsCommonLws-CMakeLists.txt @@ -11,7 +11,7 @@ ExternalProject_Add(libkvsCommonLws-download GIT_SHALLOW TRUE PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build LIST_SEPARATOR | - CMAKE_ARGS + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} -DCMAKE_PREFIX_PATH=${OPEN_SRC_INSTALL_PREFIX} -DBUILD_COMMON_LWS=ON @@ -22,7 +22,7 @@ ExternalProject_Add(libkvsCommonLws-download -DUSE_OPENSSL=${USE_OPENSSL} -DUSE_MBEDTLS=${USE_MBEDTLS} -DKVS_DEFAULT_STACK_SIZE=${KVS_DEFAULT_STACK_SIZE} - -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} + -DCMAKE_C_FLAGS=-D_GNU_SOURCE\ ${CMAKE_C_FLAGS} -DBUILD_STATIC=${BUILD_STATIC} BUILD_ALWAYS TRUE TEST_COMMAND "" diff --git a/CMake/Dependencies/libmbedtls-CMakeLists.txt b/CMake/Dependencies/libmbedtls-CMakeLists.txt index 76ce18779b..0c908697ec 100644 --- a/CMake/Dependencies/libmbedtls-CMakeLists.txt +++ b/CMake/Dependencies/libmbedtls-CMakeLists.txt @@ -4,6 +4,12 @@ project(libmbedtls-download NONE) include(ExternalProject) +if (BUILD_OLD_MBEDTLS_VERSION) + SET(MBEDTLS_GIT_TAG "v2.28.8") +else() + SET(MBEDTLS_GIT_TAG "v3.6.3") +endif() + if (BUILD_STATIC_LIBS) set(BUILD_SHARED 0) else() @@ -21,7 +27,7 @@ message(STATUS "C flags here are ${CMAKE_C_FLAGS}") ExternalProject_Add( project_libmbedtls GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git - GIT_TAG v2.28.8 + GIT_TAG ${MBEDTLS_GIT_TAG} GIT_PROGRESS TRUE GIT_SHALLOW TRUE PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build @@ -32,6 +38,6 @@ ExternalProject_Add( -DCMAKE_MACOSX_RPATH=${CMAKE_MACOSX_RPATH} -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF - -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} + "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -Wno-array-bounds" BUILD_ALWAYS TRUE TEST_COMMAND "") diff --git a/CMake/Dependencies/libwebsockets-CMakeLists.txt b/CMake/Dependencies/libwebsockets-CMakeLists.txt index bdbbb3ff6f..c040ea2dda 100644 --- a/CMake/Dependencies/libwebsockets-CMakeLists.txt +++ b/CMake/Dependencies/libwebsockets-CMakeLists.txt @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.6.3) project(libwebsocket-download NONE) -SET(PATCH_COMMAND git apply --verbose --ignore-whitespace ${CMAKE_CURRENT_LIST_DIR}/libwebsockets-old-gcc-fix-cast-cmakelists.patch) - include(ExternalProject) if (BUILD_STATIC_LIBS) set(LWS_WITH_SHARED 0) @@ -30,10 +28,9 @@ endif() ExternalProject_Add(project_libwebsockets GIT_REPOSITORY https://github.com/warmcat/libwebsockets.git - GIT_TAG v4.3.3 + GIT_TAG v4.3.5 GIT_PROGRESS TRUE GIT_SHALLOW TRUE - PATCH_COMMAND ${PATCH_COMMAND} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build LIST_SEPARATOR | CMAKE_ARGS @@ -58,7 +55,7 @@ ExternalProject_Add(project_libwebsockets -DLWS_MBEDTLS_LIBRARIES=${LWS_MBEDTLS_LIBRARIES} -DLWS_MBEDTLS_INCLUDE_DIRS=${LWS_MBEDTLS_INCLUDE_DIRS} -DLWS_WITH_MINIMAL_EXAMPLES=1 - -DLWS_HAVE_PTHREAD_H=1 + -DLWS_HAVE_PTHREAD_H=1 -DLWS_WITH_THREADPOOL=${LWS_WITH_THREADPOOL} -DLWS_WITH_ZLIB=0 -DLWS_HAVE_EVENTFD=0 diff --git a/CMake/Dependencies/libwebsockets-leak-pipe-fix.patch b/CMake/Dependencies/libwebsockets-leak-pipe-fix.patch deleted file mode 100644 index c5a22f1f56..0000000000 --- a/CMake/Dependencies/libwebsockets-leak-pipe-fix.patch +++ /dev/null @@ -1,36 +0,0 @@ -Author: Andy Green -Date: Wed Sep 08 12:25:47 2021 +0200 - -cancel pipe: make sure we closed it on destroy with no EVENTFD case - - -diff --git a/lib/core/context.c b/lib/core/context.c -index 6194801..4f3bb45 100644 ---- a/lib/core/context.c -+++ b/lib/core/context.c -@@ -1625,11 +1625,25 @@ lws_pt_destroy(struct lws_context_per_thread *pt) - vpt->foreign_pfd_list = NULL; - - lws_pt_lock(pt, __func__); -+ - if (pt->pipe_wsi) { - lws_destroy_event_pipe(pt->pipe_wsi); - pt->pipe_wsi = NULL; - } - -+ if (pt->dummy_pipe_fds[0] -+#if !defined(WIN32) -+ && (int)pt->dummy_pipe_fds[0] != -1 -+#endif -+ ) { -+ struct lws wsi; -+ -+ memset(&wsi, 0, sizeof(wsi)); -+ wsi.a.context = pt->context; -+ wsi.tsi = (char)pt->tid; -+ lws_plat_pipe_close(&wsi); -+ } -+ - #if defined(LWS_WITH_SECURE_STREAMS) - lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_destroy_dll); - diff --git a/CMake/Dependencies/libwebsockets-old-gcc-fix-cast-cmakelists.patch b/CMake/Dependencies/libwebsockets-old-gcc-fix-cast-cmakelists.patch deleted file mode 100644 index 6fd33a5bef..0000000000 --- a/CMake/Dependencies/libwebsockets-old-gcc-fix-cast-cmakelists.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/lib/roles/h2/hpack.c b/lib/roles/h2/hpack.c -index 68629e6f..6ef628b8 100644 ---- a/lib/roles/h2/hpack.c -+++ b/lib/roles/h2/hpack.c -@@ -1110,7 +1110,7 @@ pre_data: - - for (n = 0; n < 8; n++) { - if (h2n->huff) { -- char b = (c >> 7) & 1; -+ char b = (char)((c >> 7) & 1); - prev = h2n->hpack_pos; - h2n->hpack_pos = (uint16_t)huftable_decode( - (int)h2n->hpack_pos, b); -diff --git a/lib/core/lws_map.c b/lib/core/lws_map.c -index d149d86752..b319d79f49 100644 ---- a/lib/core/lws_map.c -+++ b/lib/core/lws_map.c -@@ -29,11 +29,11 @@ typedef struct lws_map_hashtable { - lws_dll2_owner_t ho; - } lws_map_hashtable_t; - --typedef struct lws_map { -+struct lws_map { - lws_map_info_t info; - - /* array of info.modulo x lws_map_hashtable_t overallocated */ --} lws_map_t; -+}; - - typedef struct lws_map_item { - lws_dll2_t list; /* owned by hashtable */ - diff --git a/CMakeLists.txt b/CMakeLists.txt index 51f67057b7..e6d2546979 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ include(CheckIncludeFiles) include(CheckFunctionExists) # The version MUST be updated before every release -project(KinesisVideoWebRTCClient VERSION 1.12.1 LANGUAGES C) +project(KinesisVideoWebRTCClient VERSION 1.14.0 LANGUAGES C) # User Flags option(ADD_MUCLIBC "Add -muclibc c flag" OFF) @@ -22,6 +22,7 @@ option(ENABLE_KVS_THREADPOOL "Enable support for KVS thread pool in signaling" O option(INSTRUMENTED_ALLOCATORS "Enable memory instrumentation" OFF) option(ENABLE_AWS_SDK_IN_TESTS "Enable support for compiling AWS SDKs for tests" ON) option(ENABLE_STATS_CALCULATION_CONTROL "Enable support for runtime control of ice agent stat calculations." OFF) +option(BUILD_OLD_MBEDTLS_VERSION "Use MbedTLS version 2.28.8." OFF) # Developer Flags option(BUILD_TEST "Build the testing tree." OFF) @@ -76,11 +77,25 @@ if(NOT WIN32) CHECK_INCLUDE_FILES(ifaddrs.h KVSWEBRTC_HAVE_IFADDRS_H) if(NOT KVSWEBRTC_HAVE_IFADDRS_H) message(FATAL_ERROR "Platform should support the ifaddrs interface.") +else() + add_definitions(-DHAVE_IFADDRS_H=1) endif() CHECK_FUNCTION_EXISTS(getifaddrs KVSWEBRTC_HAVE_GETIFADDRS) if(NOT KVSWEBRTC_HAVE_GETIFADDRS) message(FATAL_ERROR "Platform should support getifaddrs API.") endif() + +# Check for poll.h +CHECK_INCLUDE_FILES(poll.h HAVE_POLL_H) +if (HAVE_POLL_H) + add_definitions(-DHAVE_POLL_H=1) +endif() + +# Check for socketpair availability +CHECK_FUNCTION_EXISTS(socketpair HAVE_SOCKETPAIR) +if (HAVE_SOCKETPAIR) + add_definitions(-DHAVE_SOCKETPAIR=1) +endif() endif() set(CMAKE_MACOSX_RPATH TRUE) @@ -182,6 +197,7 @@ if(BUILD_DEPENDENCIES) elseif(USE_MBEDTLS) set(BUILD_ARGS -DBUILD_STATIC_LIBS=${BUILD_STATIC_LIBS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DBUILD_OLD_MBEDTLS_VERSION=${BUILD_OLD_MBEDTLS_VERSION} "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -std=c99") build_dependency(mbedtls ${BUILD_ARGS}) endif() @@ -446,7 +462,7 @@ target_link_libraries( kvsWebrtcSignalingClient PUBLIC kvsCommonLws - ${LIBWEBSOCKETS_LIBRARIES} + ${LIBWEBSOCKETS_LIBRARIES} PRIVATE kvspicUtils kvspicState ${CMAKE_THREAD_LIBS_INIT} diff --git a/samples/kvsWebRTCClientMaster.c b/samples/kvsWebRTCClientMaster.c index b2ddbd214f..5123137b08 100644 --- a/samples/kvsWebRTCClientMaster.c +++ b/samples/kvsWebRTCClientMaster.c @@ -185,10 +185,11 @@ PVOID sendVideoPackets(PVOID args) lastFrameTime = startTime; while (!ATOMIC_LOAD_BOOL(&pSampleConfiguration->appTerminateFlag)) { - fileIndex = fileIndex % NUMBER_OF_H264_FRAME_FILES + 1; if (pSampleConfiguration->videoCodec == RTC_CODEC_H264_PROFILE_42E01F_LEVEL_ASYMMETRY_ALLOWED_PACKETIZATION_MODE) { + fileIndex = fileIndex % NUMBER_OF_H264_FRAME_FILES + 1; SNPRINTF(filePath, MAX_PATH_LEN, "./h264SampleFrames/frame-%04d.h264", fileIndex); } else if (pSampleConfiguration->videoCodec == RTC_CODEC_H265) { + fileIndex = fileIndex % NUMBER_OF_H265_FRAME_FILES + 1; SNPRINTF(filePath, MAX_PATH_LEN, "./h265SampleFrames/frame-%04d.h265", fileIndex); } diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h index d25906b7ee..3fd3a3711f 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h @@ -449,12 +449,27 @@ extern "C" { */ #define MAX_ICE_CONFIG_USER_NAME_LEN 256 +/** + * Buffer length for ICE configuration user name, including null terminator. + * + * \sa MAX_ICE_CONFIG_USER_NAME_LEN + */ +#define MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN (MAX_ICE_CONFIG_USER_NAME_LEN + 1) + /** * Maximum allowed ICE configuration password length - * https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_AWSAcuitySignalingService_IceServer.html#KinesisVideo-Type-AWSAcuitySignalingService_IceServer-Password + * + * \sa https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_signaling_IceServer.html#KinesisVideo-Type-signaling_IceServer-Password */ #define MAX_ICE_CONFIG_CREDENTIAL_LEN 256 +/** + * Buffer length for ICE configuration password, including null terminator. + * + * \sa MAX_ICE_CONFIG_USER_NAME_LEN + */ +#define MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN (MAX_ICE_CONFIG_CREDENTIAL_LEN + 1) + /** * Maximum allowed signaling URI length */ @@ -473,7 +488,7 @@ extern "C" { /** * Maximum allowed code string length */ -#define MAX_STATUS_CODE_STRING_LEN 256 +#define MAX_STATUS_CODE_STRING_LEN 8 /** * Maximum allowed message description length @@ -722,10 +737,12 @@ extern "C" { /** * Parameterized string for KVS STUN Server */ -#define KINESIS_VIDEO_STUN_URL_POSTFIX "amazonaws.com" -#define KINESIS_VIDEO_STUN_URL_POSTFIX_CN "amazonaws.com.cn" -#define KINESIS_VIDEO_STUN_URL "stun:stun.kinesisvideo.%s.%s:443" -#define KINESIS_VIDEO_STUN_URL_WITHOUT_PORT "stun.kinesisvideo.%s.%s" +#define KINESIS_VIDEO_STUN_URL_POSTFIX "amazonaws.com" +#define KINESIS_VIDEO_STUN_URL_POSTFIX_CN "amazonaws.com.cn" +#define KINESIS_VIDEO_STUN_URL_PREFIX "stun." +#define KINESIS_VIDEO_STUN_URL_PREFIX_LENGTH 5 +#define KINESIS_VIDEO_STUN_URL "stun:stun.kinesisvideo.%s.%s:443" +#define KINESIS_VIDEO_STUN_URL_WITHOUT_PORT "stun.kinesisvideo.%s.%s" /** * Default signaling SSL port @@ -1137,9 +1154,9 @@ typedef struct { * https://www.w3.org/TR/webrtc/#rtciceserver-dictionary */ typedef struct { - CHAR urls[MAX_ICE_CONFIG_URI_LEN + 1]; //!< URL of STUN/TURN Server - CHAR username[MAX_ICE_CONFIG_USER_NAME_LEN + 1]; //!< Username to be used with TURN server - CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1]; //!< Password to be used with TURN server + CHAR urls[MAX_ICE_CONFIG_URI_LEN + 1]; //!< URL of STUN/TURN Server + CHAR username[MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN]; //!< Username to be used with TURN server + CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN]; //!< Password to be used with TURN server } RtcIceServer, *PRtcIceServer; /** @@ -1388,12 +1405,12 @@ typedef struct { * NOTE:TTL is given in default which is 100ns duration */ typedef struct { - UINT32 version; //!< Version of the struct - UINT64 ttl; //!< TTL of the configuration is 100ns - UINT32 uriCount; //!< Number of Ice URI objects - CHAR uris[MAX_ICE_CONFIG_URI_COUNT][MAX_ICE_CONFIG_URI_LEN + 1]; //!< List of Ice server URIs - CHAR userName[MAX_ICE_CONFIG_USER_NAME_LEN + 1]; //!< Username for the server - CHAR password[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1]; //!< Password for the server + UINT32 version; //!< Version of the struct + UINT64 ttl; //!< TTL of the configuration is 100ns + UINT32 uriCount; //!< Number of Ice URI objects + CHAR uris[MAX_ICE_CONFIG_URI_COUNT][MAX_ICE_CONFIG_URI_BUFFER_LEN]; //!< List of Ice server URIs + CHAR userName[MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN]; //!< Username for the server + CHAR password[MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN]; //!< Password for the server } IceConfigInfo, *PIceConfigInfo; typedef struct { diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h index 59be39768c..066350a790 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h @@ -29,6 +29,13 @@ extern "C" { */ #define MAX_ICE_CONFIG_URI_LEN 127 +/** + * Maximum allowed ICE URI buffer length, including the null terminator. + * + * \sa MAX_ICE_CONFIG_URI_LEN + */ +#define MAX_ICE_CONFIG_URI_BUFFER_LEN (MAX_ICE_CONFIG_URI_LEN + 1) + /** * Maximum allowed relay protocol length */ @@ -215,7 +222,7 @@ typedef struct { UINT64 bytesReceived; //!< Total number of bytes (minus header and padding) received on this candidate pair UINT64 lastPacketSentTimestamp; //!< Represents the timestamp at which the last packet was sent on this particular //!< candidate pair, excluding STUN packets. - UINT64 lastPacketReceivedTimestamp; //!< Represents the timestamp at which the last packet was sent on this particular + UINT64 lastPacketReceivedTimestamp; //!< Represents the timestamp at which the last packet was received on this particular //!< candidate pair, excluding STUN packets. UINT64 firstRequestTimestamp; //!< Represents the timestamp at which the first STUN request was sent on this particular candidate pair. UINT64 lastRequestTimestamp; //!< Represents the timestamp at which the last STUN request was sent on this particular candidate pair. @@ -244,14 +251,14 @@ typedef struct { * Reference: https://www.w3.org/TR/webrtc-stats/#ice-server-dict* */ typedef struct { - CHAR url[MAX_ICE_CONFIG_URI_LEN + 1]; //!< STUN/TURN server URL - CHAR protocol[MAX_PROTOCOL_LENGTH + 1]; //!< Valid values: UDP, TCP - UINT32 iceServerIndex; //!< Ice server index to get stats from. Not available in spec! Needs to be - //!< populated by the application to get specific server stats - INT32 port; //!< Port number used by client - UINT64 totalRequestsSent; //!< Total amount of requests that have been sent to the server - UINT64 totalResponsesReceived; //!< Total number of responses received from the server - UINT64 totalRoundTripTime; //!< Sum of RTTs of all the requests for which response has been received + CHAR url[MAX_ICE_CONFIG_URI_BUFFER_LEN]; //!< STUN/TURN server URL + CHAR protocol[MAX_PROTOCOL_LENGTH + 1]; //!< Valid values: UDP, TCP + UINT32 iceServerIndex; //!< Ice server index to get stats from. Not available in spec! Needs to be + //!< populated by the application to get specific server stats + INT32 port; //!< Port number used by client + UINT64 totalRequestsSent; //!< Total amount of requests that have been sent to the server + UINT64 totalResponsesReceived; //!< Total number of responses received from the server + UINT64 totalRoundTripTime; //!< Sum of RTTs of all the requests for which response has been received } RtcIceServerStats, *PRtcIceServerStats; /** diff --git a/src/source/Crypto/Crypto.h b/src/source/Crypto/Crypto.h index 81db2a51b6..99d3e5f147 100644 --- a/src/source/Crypto/Crypto.h +++ b/src/source/Crypto/Crypto.h @@ -32,10 +32,14 @@ typedef enum { KVS_SRTP_PROFILE_AES128_CM_HMAC_SHA1_32 = SRTP_AES128_CM_SHA1_32, } KVS_SRTP_PROFILE; #elif KVS_USE_MBEDTLS -#define KVS_RSA_F4 0x10001L -#define KVS_MD5_DIGEST_LENGTH 16 -#define KVS_SHA1_DIGEST_LENGTH 20 +#define KVS_RSA_F4 0x10001L +#define KVS_MD5_DIGEST_LENGTH 16 +#define KVS_SHA1_DIGEST_LENGTH 20 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#define KVS_MD5_DIGEST(m, mlen, ob) mbedtls_md5((m), (mlen), (ob)); +#else #define KVS_MD5_DIGEST(m, mlen, ob) mbedtls_md5_ret((m), (mlen), (ob)); +#endif #define KVS_SHA1_HMAC(k, klen, m, mlen, ob, plen) \ CHK(0 == mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), (k), (klen), (m), (mlen), (ob)), STATUS_HMAC_GENERATION_ERROR); \ *(plen) = mbedtls_md_get_size(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1)); diff --git a/src/source/Crypto/Dtls.h b/src/source/Crypto/Dtls.h index 7f0fbd9532..8a869b88bd 100644 --- a/src/source/Crypto/Dtls.h +++ b/src/source/Crypto/Dtls.h @@ -7,6 +7,8 @@ #pragma once +#include "Tls.h" + #ifdef __cplusplus extern "C" { #endif @@ -197,7 +199,7 @@ STATUS dtlsValidateRtcCertificates(PRtcCertificate, PUINT32); STATUS createSslCtx(PDtlsSessionCertificateInfo, UINT32, SSL_CTX**); #elif KVS_USE_MBEDTLS STATUS dtlsCertificateFingerprint(mbedtls_x509_crt*, PCHAR); -STATUS copyCertificateAndKey(mbedtls_x509_crt*, mbedtls_pk_context*, PDtlsSessionCertificateInfo); +STATUS copyCertificateAndKey(mbedtls_x509_crt*, mbedtls_pk_context*, PDtlsSessionCertificateInfo, mbedtls_ctr_drbg_context*); STATUS createCertificateAndKey(INT32, BOOL, mbedtls_x509_crt*, mbedtls_pk_context*); STATUS freeCertificateAndKey(mbedtls_x509_crt*, mbedtls_pk_context*); @@ -208,9 +210,17 @@ INT32 dtlsSessionSendCallback(PVOID, const unsigned char*, ULONG); INT32 dtlsSessionReceiveCallback(PVOID, unsigned char*, ULONG); VOID dtlsSessionSetTimerCallback(PVOID, UINT32, UINT32); INT32 dtlsSessionGetTimerCallback(PVOID); + +#if MBEDTLS_BEFORE_V3 INT32 dtlsSessionKeyDerivationCallback(PVOID, const unsigned char*, const unsigned char*, ULONG, ULONG, ULONG, const unsigned char[MAX_DTLS_RANDOM_BYTES_LEN], const unsigned char[MAX_DTLS_RANDOM_BYTES_LEN], mbedtls_tls_prf_types); +#else +VOID dtlsSessionKeyDerivationCallback(PVOID customData, mbedtls_ssl_key_export_type secret_type, const unsigned char* pMasterSecret, + size_t pMasterSecretLen, const unsigned char clientRandom[MAX_DTLS_RANDOM_BYTES_LEN], + const unsigned char serverRandom[MAX_DTLS_RANDOM_BYTES_LEN], mbedtls_tls_prf_types tlsProfile); +#endif + #else #error "A Crypto implementation is required." #endif diff --git a/src/source/Crypto/Dtls_mbedtls.c b/src/source/Crypto/Dtls_mbedtls.c index ac59402f67..ece9acf328 100644 --- a/src/source/Crypto/Dtls_mbedtls.c +++ b/src/source/Crypto/Dtls_mbedtls.c @@ -47,7 +47,8 @@ STATUS createDtlsSession(PDtlsSessionCallbacks pDtlsSessionCallbacks, TIMER_QUEU } else { for (i = 0; i < certCount; i++) { CHK_STATUS(copyCertificateAndKey((mbedtls_x509_crt*) pRtcCertificates[i].pCertificate, - (mbedtls_pk_context*) pRtcCertificates[i].pPrivateKey, &pDtlsSession->certificates[i])); + (mbedtls_pk_context*) pRtcCertificates[i].pPrivateKey, &pDtlsSession->certificates[i], + &pDtlsSession->ctrDrbg)); // in case of a failure in between, we'll only free up to current position pDtlsSession->certificateCount++; } @@ -229,6 +230,7 @@ STATUS dtlsTransmissionTimerCallback(UINT32 timerID, UINT64 currentTime, UINT64 return retStatus; } +#if MBEDTLS_BEFORE_V3 INT32 dtlsSessionKeyDerivationCallback(PVOID customData, const unsigned char* pMasterSecret, const unsigned char* pKeyBlock, ULONG maclen, ULONG keylen, ULONG ivlen, const unsigned char clientRandom[MAX_DTLS_RANDOM_BYTES_LEN], const unsigned char serverRandom[MAX_DTLS_RANDOM_BYTES_LEN], mbedtls_tls_prf_types tlsProfile) @@ -247,6 +249,37 @@ INT32 dtlsSessionKeyDerivationCallback(PVOID customData, const unsigned char* pM LEAVES(); return 0; } +#else +VOID dtlsSessionKeyDerivationCallback(PVOID customData, mbedtls_ssl_key_export_type secret_type, const unsigned char* pMasterSecret, + size_t pMasterSecretLen, const unsigned char clientRandom[MAX_DTLS_RANDOM_BYTES_LEN], + const unsigned char serverRandom[MAX_DTLS_RANDOM_BYTES_LEN], mbedtls_tls_prf_types tlsProfile) +{ + ENTERS(); + + /* We're only interested in the TLS 1.2 master secret */ + if (secret_type != MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET) { + DLOGE("SRTP only supports TLS 1.2 master secret, bailing out for type %d", secret_type); + goto CleanUp; + } + + PDtlsSession pDtlsSession = (PDtlsSession) customData; + PTlsKeys pKeys = &pDtlsSession->tlsKeys; + + if (pMasterSecretLen != SIZEOF(pKeys->masterSecret)) { + DLOGE("Secret length check failed, pMasterSecretLen = %d, sizeof(pKeys->masterSecret) = %d", pMasterSecretLen, SIZEOF(pKeys->masterSecret)); + MEMSET(pKeys->masterSecret, 0, SIZEOF(pKeys->masterSecret)); + goto CleanUp; + } + + MEMCPY(pKeys->masterSecret, pMasterSecret, pMasterSecretLen); + MEMCPY(pKeys->randBytes, clientRandom, MAX_DTLS_RANDOM_BYTES_LEN); + MEMCPY(pKeys->randBytes + MAX_DTLS_RANDOM_BYTES_LEN, serverRandom, MAX_DTLS_RANDOM_BYTES_LEN); + pKeys->tlsProfile = tlsProfile; + +CleanUp: + LEAVES(); +} +#endif STATUS dtlsSessionHandshakeInThread(PDtlsSession pDtlsSession, BOOL isServer) { @@ -287,11 +320,18 @@ STATUS dtlsSessionStart(PDtlsSession pDtlsSession, BOOL isServer) } mbedtls_ssl_conf_dtls_cookies(&pDtlsSession->sslCtxConfig, NULL, NULL, NULL); CHK(mbedtls_ssl_conf_dtls_srtp_protection_profiles(&pDtlsSession->sslCtxConfig, DTLS_SRTP_SUPPORTED_PROFILES) == 0, STATUS_CREATE_SSL_FAILED); + +#if MBEDTLS_BEFORE_V3 mbedtls_ssl_conf_export_keys_ext_cb(&pDtlsSession->sslCtxConfig, dtlsSessionKeyDerivationCallback, pDtlsSession); +#endif CHK(mbedtls_ssl_setup(&pDtlsSession->sslCtx, &pDtlsSession->sslCtxConfig) == 0, STATUS_SSL_CTX_CREATION_FAILED); mbedtls_ssl_set_mtu(&pDtlsSession->sslCtx, DEFAULT_MTU_SIZE_BYTES); mbedtls_ssl_set_bio(&pDtlsSession->sslCtx, pDtlsSession, dtlsSessionSendCallback, dtlsSessionReceiveCallback, NULL); + +#if !MBEDTLS_BEFORE_V3 + mbedtls_ssl_set_export_keys_cb(&pDtlsSession->sslCtx, dtlsSessionKeyDerivationCallback, pDtlsSession); +#endif mbedtls_ssl_set_timer_cb(&pDtlsSession->sslCtx, &pDtlsSession->transmissionTimer, dtlsSessionSetTimerCallback, dtlsSessionGetTimerCallback); // Start non-blocking handshaking @@ -363,7 +403,11 @@ STATUS dtlsSessionProcessPacket(PDtlsSession pDtlsSession, PBYTE pData, PINT32 p } } +#if MBEDTLS_BEFORE_V3 if (pDtlsSession->sslCtx.state == MBEDTLS_SSL_HANDSHAKE_OVER) { +#else + if (pDtlsSession->sslCtx.MBEDTLS_PRIVATE(state) == MBEDTLS_SSL_HANDSHAKE_OVER) { +#endif CHK_STATUS(dtlsSessionChangeState(pDtlsSession, RTC_DTLS_TRANSPORT_STATE_CONNECTED)); } @@ -505,7 +549,11 @@ STATUS dtlsSessionPopulateKeyingMaterial(PDtlsSession pDtlsSession, PDtlsKeyingM MEMCPY(pDtlsKeyingMaterial->serverWriteKey + MAX_SRTP_MASTER_KEY_LEN, &keyingMaterialBuffer[offset], MAX_SRTP_SALT_KEY_LEN); mbedtls_ssl_get_dtls_srtp_negotiation_result(&pDtlsSession->sslCtx, &negotiatedSRTPProfile); +#if MBEDTLS_BEFORE_V3 switch (negotiatedSRTPProfile.chosen_dtls_srtp_profile) { +#else + switch (negotiatedSRTPProfile.MBEDTLS_PRIVATE(chosen_dtls_srtp_profile)) { +#endif case MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80: pDtlsKeyingMaterial->srtpProfile = KVS_SRTP_PROFILE_AES128_CM_HMAC_SHA1_80; break; @@ -517,9 +565,11 @@ STATUS dtlsSessionPopulateKeyingMaterial(PDtlsSession pDtlsSession, PDtlsKeyingM } CleanUp: + if (locked) { MUTEX_UNLOCK(pDtlsSession->sslLock); } + CHK_LOG_ERR(retStatus); LEAVES(); return retStatus; @@ -554,7 +604,7 @@ STATUS dtlsSessionShutdown(PDtlsSession pDtlsSession) return retStatus; } -STATUS copyCertificateAndKey(mbedtls_x509_crt* pCert, mbedtls_pk_context* pKey, PDtlsSessionCertificateInfo pDst) +STATUS copyCertificateAndKey(mbedtls_x509_crt* pCert, mbedtls_pk_context* pKey, PDtlsSessionCertificateInfo pDst, mbedtls_ctr_drbg_context* pCtrDrbg) { ENTERS(); STATUS retStatus = STATUS_SUCCESS; @@ -562,14 +612,22 @@ STATUS copyCertificateAndKey(mbedtls_x509_crt* pCert, mbedtls_pk_context* pKey, mbedtls_ecp_keypair *pSrcECP, *pDstECP; CHK(pCert != NULL && pKey != NULL && pDst != NULL, STATUS_NULL_ARG); +#if MBEDTLS_BEFORE_V3 CHK(mbedtls_pk_check_pair(&pCert->pk, pKey) == 0, STATUS_CERTIFICATE_GENERATION_FAILED); +#else + CHK(mbedtls_pk_check_pair(&pCert->pk, pKey, mbedtls_ctr_drbg_random, pCtrDrbg) == 0, STATUS_CERTIFICATE_GENERATION_FAILED); +#endif mbedtls_x509_crt_init(&pDst->cert); mbedtls_pk_init(&pDst->privateKey); initialized = TRUE; CHK(mbedtls_x509_crt_parse_der(&pDst->cert, pCert->raw.p, pCert->raw.len) == 0, STATUS_CERTIFICATE_GENERATION_FAILED); +#if MBEDTLS_BEFORE_V3 CHK(mbedtls_pk_setup(&pDst->privateKey, pKey->pk_info) == 0, STATUS_CERTIFICATE_GENERATION_FAILED); +#else + CHK(mbedtls_pk_setup(&pDst->privateKey, pKey->MBEDTLS_PRIVATE(pk_info)) == 0, STATUS_CERTIFICATE_GENERATION_FAILED); +#endif switch (mbedtls_pk_get_type(pKey)) { case MBEDTLS_PK_RSA: @@ -579,9 +637,16 @@ STATUS copyCertificateAndKey(mbedtls_x509_crt* pCert, mbedtls_pk_context* pKey, case MBEDTLS_PK_ECDSA: pSrcECP = mbedtls_pk_ec(*pKey); pDstECP = mbedtls_pk_ec(pDst->privateKey); +#if MBEDTLS_BEFORE_V3 CHK(mbedtls_ecp_group_copy(&pDstECP->grp, &pSrcECP->grp) == 0 && mbedtls_ecp_copy(&pDstECP->Q, &pSrcECP->Q) == 0 && mbedtls_mpi_copy(&pDstECP->d, &pSrcECP->d) == 0, STATUS_CERTIFICATE_GENERATION_FAILED); +#else + CHK(mbedtls_ecp_group_copy(&pDstECP->MBEDTLS_PRIVATE(grp), &pSrcECP->MBEDTLS_PRIVATE(grp)) == 0 && + mbedtls_ecp_copy(&pDstECP->MBEDTLS_PRIVATE(Q), &pSrcECP->MBEDTLS_PRIVATE(Q)) == 0 && + mbedtls_mpi_copy(&pDstECP->MBEDTLS_PRIVATE(d), &pSrcECP->MBEDTLS_PRIVATE(d)) == 0, + STATUS_CERTIFICATE_GENERATION_FAILED); +#endif break; default: CHK(FALSE, STATUS_CERTIFICATE_GENERATION_FAILED); @@ -598,6 +663,29 @@ STATUS copyCertificateAndKey(mbedtls_x509_crt* pCert, mbedtls_pk_context* pKey, return retStatus; } +#if !(defined(MBEDTLS_BIGNUM_C) && !defined(MBEDTLS_DEPRECATED_REMOVED)) +int mbedtls_x509write_crt_set_serial(mbedtls_x509write_cert* ctx, const mbedtls_mpi* serial) +{ + int ret; + size_t tmp_len; + + /* Ensure that the MPI value fits into the buffer */ + tmp_len = mbedtls_mpi_size(serial); + if (tmp_len > MBEDTLS_X509_RFC5280_MAX_SERIAL_LEN) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + ctx->MBEDTLS_PRIVATE(serial_len) = tmp_len; + + ret = mbedtls_mpi_write_binary(serial, ctx->MBEDTLS_PRIVATE(serial), tmp_len); + if (ret != 0) { + return ret; + } + + return 0; +} +#endif // MBEDTLS_BIGNUM_C && !MBEDTLS_DEPRECATED_REMOVED + /** * createCertificateAndKey generates a new certificate and a key * If generateRSACertificate is true, RSA is going to be used for the key generation. Otherwise, ECDSA is going to be used. @@ -732,7 +820,11 @@ STATUS dtlsCertificateFingerprint(mbedtls_x509_crt* pCert, PCHAR pBuff) pMdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); CHK(pMdInfo != NULL, STATUS_INTERNAL_ERROR); +#if MBEDTLS_BEFORE_V3 sslRet = mbedtls_sha256_ret(pCert->raw.p, pCert->raw.len, fingerprint, 0); +#else + sslRet = mbedtls_sha256(pCert->raw.p, pCert->raw.len, fingerprint, 0); +#endif CHK(sslRet == 0, STATUS_INTERNAL_ERROR); size = mbedtls_md_get_size(pMdInfo); diff --git a/src/source/Crypto/Tls.h b/src/source/Crypto/Tls.h index 68e48593c4..81bdf1ebec 100644 --- a/src/source/Crypto/Tls.h +++ b/src/source/Crypto/Tls.h @@ -109,11 +109,19 @@ STATUS tlsSessionChangeState(PTlsSession, TLS_SESSION_STATE); #ifdef KVS_USE_OPENSSL INT32 tlsSessionCertificateVerifyCallback(INT32, X509_STORE_CTX*); #elif KVS_USE_MBEDTLS +#if defined(MBEDTLS_VERSION_NUMBER) +#define MBEDTLS_BEFORE_V3 (MBEDTLS_VERSION_NUMBER < 0x03000000) +#else +#define MBEDTLS_BEFORE_V3 (1) /* default to before v3 */ +#endif // following are required callbacks for mbedtls // NOTE: const is not a pure C qualifier, they're here because there's no way to type cast // a callback signature. INT32 tlsSessionSendCallback(PVOID, const unsigned char*, ULONG); INT32 tlsSessionReceiveCallback(PVOID, unsigned char*, ULONG); + +// Add hostname parameter for mbedTLS 3.x compatibility +STATUS tlsSessionStartWithHostname(PTlsSession, BOOL, PCHAR); #else #error "A Crypto implementation is required." #endif diff --git a/src/source/Crypto/Tls_mbedtls.c b/src/source/Crypto/Tls_mbedtls.c index ea4c1ea40a..3394a085b7 100644 --- a/src/source/Crypto/Tls_mbedtls.c +++ b/src/source/Crypto/Tls_mbedtls.c @@ -4,6 +4,37 @@ #define LOG_CLASS "TLS_mbedtls" #include "../Include_i.h" +// Read and parse CA certificate +PRIVATE_API STATUS readAndParseCACertificate(PTlsSession pTlsSession) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + UINT64 cert_len = 0; + PBYTE cert_buf = NULL; + CHAR errBuf[128]; + + CHK(pTlsSession != NULL, STATUS_NULL_ARG); + + CHK_STATUS(readFile(KVS_CA_CERT_PATH, FALSE, NULL, &cert_len)); + CHK(cert_len > 0, STATUS_INVALID_CERT_PATH_LENGTH); + cert_buf = (PBYTE) MEMCALLOC(1, cert_len + 1); + CHK(cert_buf != NULL, STATUS_NOT_ENOUGH_MEMORY); + CHK_STATUS(readFile(KVS_CA_CERT_PATH, FALSE, cert_buf, &cert_len)); + int ret = mbedtls_x509_crt_parse(&pTlsSession->cacert, cert_buf, (SIZE_T) (cert_len + 1)); + if (ret != 0) { + mbedtls_strerror(ret, errBuf, SIZEOF(errBuf)); + DLOGE("mbedtls_x509_crt_parse failed: %s", errBuf); + } + CHK(ret == 0, STATUS_INVALID_CA_CERT_PATH); + +CleanUp: + CHK_LOG_ERR(retStatus); + SAFE_MEMFREE(cert_buf); + + LEAVES(); + return retStatus; +} + STATUS createTlsSession(PTlsSessionCallbacks pCallbacks, PTlsSession* ppTlsSession) { ENTERS(); @@ -26,9 +57,11 @@ STATUS createTlsSession(PTlsSessionCallbacks pCallbacks, PTlsSession* ppTlsSessi mbedtls_ssl_config_init(&pTlsSession->sslCtxConfig); mbedtls_ssl_init(&pTlsSession->sslCtx); CHK(mbedtls_ctr_drbg_seed(&pTlsSession->ctrDrbg, mbedtls_entropy_func, &pTlsSession->entropy, NULL, 0) == 0, STATUS_CREATE_SSL_FAILED); - CHK(mbedtls_x509_crt_parse_file(&pTlsSession->cacert, KVS_CA_CERT_PATH) == 0, STATUS_INVALID_CA_CERT_PATH); + + CHK_STATUS(readAndParseCACertificate(pTlsSession)); CleanUp: + if (STATUS_FAILED(retStatus) && pTlsSession != NULL) { freeTlsSession(&pTlsSession); } @@ -100,7 +133,7 @@ INT32 tlsSessionReceiveCallback(PVOID customData, unsigned char* buf, ULONG len) return STATUS_FAILED(retStatus) ? -retStatus : readBytes; } -STATUS tlsSessionStart(PTlsSession pTlsSession, BOOL isServer) +STATUS tlsSessionStartWithHostname(PTlsSession pTlsSession, BOOL isServer, PCHAR hostname) { ENTERS(); STATUS retStatus = STATUS_SUCCESS; @@ -114,9 +147,25 @@ STATUS tlsSessionStart(PTlsSession pTlsSession, BOOL isServer) STATUS_CREATE_SSL_FAILED); mbedtls_ssl_conf_ca_chain(&pTlsSession->sslCtxConfig, &pTlsSession->cacert, NULL); - mbedtls_ssl_conf_authmode(&pTlsSession->sslCtxConfig, MBEDTLS_SSL_VERIFY_REQUIRED); + + // For mbedTLS 3.x, use appropriate verification mode + if (hostname != NULL) { + // If a hostname is provided, use strict verification + mbedtls_ssl_conf_authmode(&pTlsSession->sslCtxConfig, MBEDTLS_SSL_VERIFY_REQUIRED); + } else { + // Otherwise, use optional verification for IP-based connections + mbedtls_ssl_conf_authmode(&pTlsSession->sslCtxConfig, MBEDTLS_SSL_VERIFY_OPTIONAL); + } + mbedtls_ssl_conf_rng(&pTlsSession->sslCtxConfig, mbedtls_ctr_drbg_random, &pTlsSession->ctrDrbg); CHK(mbedtls_ssl_setup(&pTlsSession->sslCtx, &pTlsSession->sslCtxConfig) == 0, STATUS_SSL_CTX_CREATION_FAILED); + + // Set the hostname for certificate verification (for mbedTLS 3.x compatibility) + if (!isServer && hostname != NULL) { + // Use the provided hostname for certificate verification + CHK(mbedtls_ssl_set_hostname(&pTlsSession->sslCtx, hostname) == 0, STATUS_SSL_CTX_CREATION_FAILED); + } + mbedtls_ssl_set_mtu(&pTlsSession->sslCtx, DEFAULT_MTU_SIZE_BYTES); mbedtls_ssl_set_bio(&pTlsSession->sslCtx, pTlsSession, tlsSessionSendCallback, tlsSessionReceiveCallback, NULL); @@ -134,6 +183,11 @@ STATUS tlsSessionStart(PTlsSession pTlsSession, BOOL isServer) return retStatus; } +STATUS tlsSessionStart(PTlsSession pTlsSession, BOOL isServer) +{ + return tlsSessionStartWithHostname(pTlsSession, isServer, NULL); +} + STATUS tlsSessionProcessPacket(PTlsSession pTlsSession, PBYTE pData, UINT32 bufferLen, PUINT32 pDataLen) { ENTERS(); @@ -171,8 +225,11 @@ STATUS tlsSessionProcessPacket(PTlsSession pTlsSession, PBYTE pData, UINT32 buff iterate = FALSE; } } - +#if MBEDTLS_BEFORE_V3 if (pTlsSession->sslCtx.state == MBEDTLS_SSL_HANDSHAKE_OVER) { +#else + if (pTlsSession->sslCtx.MBEDTLS_PRIVATE(state) == MBEDTLS_SSL_HANDSHAKE_OVER) { +#endif tlsSessionChangeState(pTlsSession, TLS_SESSION_STATE_CONNECTED); } diff --git a/src/source/Ice/ConnectionListener.c b/src/source/Ice/ConnectionListener.c index 33eb21a37e..4994def140 100644 --- a/src/source/Ice/ConnectionListener.c +++ b/src/source/Ice/ConnectionListener.c @@ -26,8 +26,8 @@ STATUS createConnectionListener(PConnectionListener* ppConnectionListener) pConnectionListener->pBuffer = (PBYTE) (pConnectionListener + 1); pConnectionListener->bufferLen = MAX_UDP_PACKET_SIZE; - // TODO add support for windows socketpair -#ifndef _WIN32 + // Use socketpair only if available +#if defined(HAVE_SOCKETPAIR) pConnectionListener->kickSocket[CONNECTION_LISTENER_KICK_SOCKET_LISTEN] = -1; pConnectionListener->kickSocket[CONNECTION_LISTENER_KICK_SOCKET_WRITE] = -1; CHK_STATUS(createSocketPair(&(pConnectionListener->kickSocket))); @@ -69,7 +69,7 @@ STATUS freeConnectionListener(PConnectionListener* ppConnectionListener) // TODO add support for windows socketpair // This writes to the socketpair, kicking the POLL() out early, // otherwise wait for the POLL to timeout -#ifndef _WIN32 +#if defined(HAVE_SOCKETPAIR) socketWrite(pConnectionListener->kickSocket[CONNECTION_LISTENER_KICK_SOCKET_WRITE], msg, STRLEN(msg)); #endif @@ -82,7 +82,7 @@ STATUS freeConnectionListener(PConnectionListener* ppConnectionListener) } // TODO add support for windows socketpair -#ifndef _WIN32 +#if defined(HAVE_SOCKETPAIR) if (pConnectionListener->kickSocket[CONNECTION_LISTENER_KICK_SOCKET_LISTEN] != -1) { closeSocket(pConnectionListener->kickSocket[CONNECTION_LISTENER_KICK_SOCKET_LISTEN]); } @@ -283,7 +283,7 @@ PVOID connectionListenerReceiveDataRoutine(PVOID arg) MUTEX_UNLOCK(pSocketConnection->lock); rfds[nfds].fd = localSocket; rfds[nfds].events = POLLIN | POLLPRI; -#ifdef _WIN32 +#if !defined(HAVE_SOCKETPAIR) rfds[nfds].events &= ~POLLPRI; #endif rfds[nfds].revents = 0; @@ -307,7 +307,7 @@ PVOID connectionListenerReceiveDataRoutine(PVOID arg) // TODO add support for socketpair() in windows // This end of the socketpair has been added to the list of sockets polled // in order to have a way to end the poll early from the destructor -#ifndef _WIN32 +#if defined(HAVE_SOCKETPAIR) rfds[nfds].fd = pConnectionListener->kickSocket[CONNECTION_LISTENER_KICK_SOCKET_LISTEN]; rfds[nfds].events = POLLIN; rfds[nfds].revents = 0; diff --git a/src/source/Ice/ConnectionListener.h b/src/source/Ice/ConnectionListener.h index 207465f1ad..6385276b97 100644 --- a/src/source/Ice/ConnectionListener.h +++ b/src/source/Ice/ConnectionListener.h @@ -24,7 +24,7 @@ typedef struct { TID receiveDataRoutine; PBYTE pBuffer; UINT64 bufferLen; -#ifndef _WIN32 +#if defined(HAVE_SOCKETPAIR) INT32 kickSocket[2]; #endif } ConnectionListener, *PConnectionListener; diff --git a/src/source/Ice/IceUtils.c b/src/source/Ice/IceUtils.c index 444d2d025a..a54a9314f6 100644 --- a/src/source/Ice/IceUtils.c +++ b/src/source/Ice/IceUtils.c @@ -172,12 +172,12 @@ STATUS iceUtilsSendStunPacket(PStunPacket pStunPacket, PBYTE password, UINT32 pa CHK(pDest != NULL, STATUS_NULL_ARG); switch (pStunPacket->header.stunMessageType) { case STUN_PACKET_TYPE_BINDING_REQUEST: - DLOGD("Sending BINDING_REQUEST to ip:%u.%u.%u.%u, port:%u", pDest->address[0], pDest->address[1], pDest->address[2], pDest->address[3], - (UINT16) getInt16(pDest->port)); + DLOGD("Sending BINDING_REQUEST on socket id: %d, to ip:%u.%u.%u.%u, port:%u", pSocketConnection->localSocket, pDest->address[0], + pDest->address[1], pDest->address[2], pDest->address[3], (UINT16) getInt16(pDest->port)); break; case STUN_PACKET_TYPE_BINDING_RESPONSE_SUCCESS: - DLOGD("Sending BINDING_RESPONSE_SUCCESS to ip:%u.%u.%u.%u, port:%u", pDest->address[0], pDest->address[1], pDest->address[2], - pDest->address[3], (UINT16) getInt16(pDest->port)); + DLOGD("Sending BINDING_RESPONSE_SUCCESS on socket id: %d to ip:%u.%u.%u.%u, port:%u", pSocketConnection->localSocket, pDest->address[0], + pDest->address[1], pDest->address[2], pDest->address[3], (UINT16) getInt16(pDest->port)); break; default: break; diff --git a/src/source/Ice/IceUtils.h b/src/source/Ice/IceUtils.h index c03b1f1d19..d6c57013f6 100644 --- a/src/source/Ice/IceUtils.h +++ b/src/source/Ice/IceUtils.h @@ -51,12 +51,13 @@ STATUS iceUtilsSendStunPacket(PStunPacket, PBYTE, UINT32, PKvsIpAddress, PSocket STATUS iceUtilsSendData(PBYTE, UINT32, PKvsIpAddress, PSocketConnection, struct __TurnConnection*, BOOL); typedef struct { + // isTurn ? TURN server : STUN server BOOL isTurn; BOOL isSecure; - CHAR url[MAX_ICE_CONFIG_URI_LEN + 1]; + CHAR url[MAX_ICE_CONFIG_URI_BUFFER_LEN]; KvsIpAddress ipAddress; - CHAR username[MAX_ICE_CONFIG_USER_NAME_LEN + 1]; - CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1]; + CHAR username[MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN]; + CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN]; KVS_SOCKET_PROTOCOL transport; IceServerSetIpFunc setIpFn; } IceServer, *PIceServer; diff --git a/src/source/Ice/Network.c b/src/source/Ice/Network.c index b5568bb97b..8a46bf14ff 100644 --- a/src/source/Ice/Network.c +++ b/src/source/Ice/Network.c @@ -150,8 +150,7 @@ STATUS getLocalhostIpAddresses(PKvsIpAddress destIpList, PUINT32 pDestIpListLen, return retStatus; } -// TODO add support for windows socketpair -#ifndef _WIN32 +#if defined(HAVE_SOCKETPAIR) STATUS createSocketPair(INT32 (*pSocketPair)[2]) { STATUS retStatus = STATUS_SUCCESS; @@ -406,25 +405,31 @@ STATUS getIpWithHostName(PCHAR hostname, PKvsIpAddress destIp) struct in_addr inaddr; CHAR addr[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'}; + BOOL isStunServer; CHK(hostname != NULL, STATUS_NULL_ARG); DLOGI("ICE SERVER Hostname received: %s", hostname); hostnameLen = STRLEN(hostname); addrLen = SIZEOF(addr); + isStunServer = STRNCMP(hostname, KINESIS_VIDEO_STUN_URL_PREFIX, KINESIS_VIDEO_STUN_URL_PREFIX_LENGTH) == 0; // Adding this check in case we directly get an IP address. With the current usage pattern, // there is no way this function would receive an address directly, but having this check // in place anyways if (isIpAddr(hostname, hostnameLen)) { MEMCPY(addr, hostname, hostnameLen); - } else { + } else if (!isStunServer) { retStatus = getIpAddrFromDnsHostname(hostname, addr, hostnameLen, addrLen); } // Verify the generated address has the format x.x.x.x if (!isIpAddr(addr, hostnameLen) || retStatus != STATUS_SUCCESS) { - DLOGW("Parsing for address failed for %s, fallback to getaddrinfo", hostname); + // Only print the message for TURN servers since STUN addresses don't have the IP in the URL + if (!isStunServer) { + DLOGW("Parsing for address failed for %s, fallback to getaddrinfo", hostname); + } + errCode = getaddrinfo(hostname, NULL, NULL, &res); if (errCode != 0) { errStr = errCode == EAI_SYSTEM ? (strerror(errno)) : ((PCHAR) gai_strerror(errCode)); diff --git a/src/source/Ice/Network.h b/src/source/Ice/Network.h index 80fb745160..ca5b6e0aad 100644 --- a/src/source/Ice/Network.h +++ b/src/source/Ice/Network.h @@ -63,8 +63,7 @@ typedef enum { */ STATUS getLocalhostIpAddresses(PKvsIpAddress, PUINT32, IceSetInterfaceFilterFunc, UINT64); -// TODO add support for windows socketpair -#ifndef _WIN32 +#if defined(HAVE_SOCKETPAIR) /** * @param - INT32 (*)[2] - OUT - Array for the socket pair fds * diff --git a/src/source/Ice/SocketConnection.c b/src/source/Ice/SocketConnection.c index f464c9c876..af8305ef79 100644 --- a/src/source/Ice/SocketConnection.c +++ b/src/source/Ice/SocketConnection.c @@ -30,6 +30,7 @@ STATUS createSocketConnection(KVS_IP_FAMILY_TYPE familyType, KVS_SOCKET_PROTOCOL pSocketConnection->secureConnection = FALSE; pSocketConnection->protocol = protocol; + pSocketConnection->hostname = NULL; if (protocol == KVS_SOCKET_PROTOCOL_TCP) { pSocketConnection->peerIpAddr = *pPeerIpAddr; CHK_STATUS(socketConnect(pPeerIpAddr, pSocketConnection->localSocket)); @@ -47,9 +48,10 @@ STATUS createSocketConnection(KVS_IP_FAMILY_TYPE familyType, KVS_SOCKET_PROTOCOL if (pBindAddr) { getIpAddrStr(pBindAddr, ipAddr, ARRAY_SIZE(ipAddr)); - DLOGD("create socket with ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pBindAddr->port), pBindAddr->family); + DLOGD("create socket id: %d, with ip: %s:%u. family:%d", pSocketConnection->localSocket, ipAddr, (UINT16) getInt16(pBindAddr->port), + pBindAddr->family); } else { - DLOGD("create socket without the bind address(%d:%d)", familyType, protocol); + DLOGD("create socket id %d, without the bind address(%d:%d)", pSocketConnection->localSocket, familyType, protocol); } if (protocol == KVS_SOCKET_PROTOCOL_TCP) { getIpAddrStr(pPeerIpAddr, ipAddr, ARRAY_SIZE(ipAddr)); @@ -105,6 +107,8 @@ STATUS freeSocketConnection(PSocketConnection* ppSocketConnection) freeTlsSession(&pSocketConnection->pTlsSession); } + SAFE_MEMFREE(pSocketConnection->hostname); + getIpAddrStr(&pSocketConnection->hostIpAddr, ipAddr, ARRAY_SIZE(ipAddr)); DLOGD("close socket with ip: %s:%u. family:%d", ipAddr, (UINT16) getInt16(pSocketConnection->hostIpAddr.port), pSocketConnection->hostIpAddr.family); @@ -186,7 +190,14 @@ STATUS socketConnectionInitSecureConnection(PSocketConnection pSocketConnection, callbacks.stateChangeFn = socketConnectionTlsSessionOnStateChange; CHK_STATUS(createTlsSession(&callbacks, &pSocketConnection->pTlsSession)); + +#if KVS_USE_MBEDTLS + // Setting the hostname is recommended by mbedTLS and is default in mbedTLS 3.0 and above + // https://mbed-tls.readthedocs.io/en/latest/security-advisories/mbedtls-security-advisory-2025-03-1/ + CHK_STATUS(tlsSessionStartWithHostname(pSocketConnection->pTlsSession, isServer, pSocketConnection->hostname)); +#else CHK_STATUS(tlsSessionStart(pSocketConnection->pTlsSession, isServer)); +#endif pSocketConnection->secureConnection = TRUE; CleanUp: @@ -408,7 +419,7 @@ STATUS socketSendDataWithRetry(PSocketConnection pSocketConnection, PBYTE buf, U /* nothing need to be done, just retry */ } else { /* fatal error from send() */ - DLOGE("sendto() failed with errno %s(%d)", getErrorString(errorNum), errorNum); + DLOGE("sendto() socket %d failed with errno %s(%d)", pSocketConnection->localSocket, getErrorString(errorNum), errorNum); CHAR ipAddr[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; if (pDestIp != NULL) { diff --git a/src/source/Ice/SocketConnection.h b/src/source/Ice/SocketConnection.h index 3184c19004..3a82f736e0 100644 --- a/src/source/Ice/SocketConnection.h +++ b/src/source/Ice/SocketConnection.h @@ -44,6 +44,9 @@ struct __SocketConnection { ConnectionDataAvailableFunc dataAvailableCallbackFn; UINT64 dataAvailableCallbackCustomData; UINT64 tlsHandshakeStartTime; + + /* Hostname for TLS verification */ + PCHAR hostname; }; typedef struct __SocketConnection* PSocketConnection; diff --git a/src/source/Ice/TurnConnection.c b/src/source/Ice/TurnConnection.c index 86dc365747..e3e799a616 100644 --- a/src/source/Ice/TurnConnection.c +++ b/src/source/Ice/TurnConnection.c @@ -7,6 +7,52 @@ extern StateMachineState TURN_CONNECTION_STATE_MACHINE_STATES[]; extern UINT32 TURN_CONNECTION_STATE_MACHINE_STATE_COUNT; +// On success, ppHostname is allocated and must be freed by the caller +STATUS getHostnameFromUrl(PCHAR url, PCHAR* ppHostname) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PCHAR urlStart = NULL; + PCHAR urlEnd = NULL; + UINT32 hostnameLen = 0; + + CHK(url != NULL && ppHostname != NULL, STATUS_NULL_ARG); + + // Skip protocol prefix (turn: or turns:) if present + urlStart = STRSTR(url, "://"); + if (urlStart != NULL) { + urlStart += 3; // Skip "://" + } else { + // If no protocol prefix, use the entire URL + urlStart = url; + } + + // Find port separator or query parameter if they exist + urlEnd = STRCHR(urlStart, ':'); + if (urlEnd == NULL) { + urlEnd = STRCHR(urlStart, '?'); + } + + if (urlEnd != NULL) { + hostnameLen = urlEnd - urlStart; + } else { + // If no port or query parameter is specified, use the entire remaining string + hostnameLen = STRLEN(urlStart); + } + + CHK(hostnameLen > 0 && hostnameLen <= MAX_ICE_CONFIG_URI_LEN, STATUS_INVALID_ARG); + *ppHostname = MEMCALLOC(1, hostnameLen + 1); + CHK(*ppHostname != NULL, STATUS_NOT_ENOUGH_MEMORY); + STRNCPY(*ppHostname, urlStart, hostnameLen); + +CleanUp: + + CHK_LOG_ERR(retStatus); + LEAVES(); + + return retStatus; +} + STATUS createTurnConnection(PIceServer pTurnServer, TIMER_QUEUE_HANDLE timerQueueHandle, TURN_CONNECTION_DATA_TRANSFER_MODE dataTransferMode, KVS_SOCKET_PROTOCOL protocol, PTurnConnectionCallbacks pTurnConnectionCallbacks, PSocketConnection pTurnSocket, PConnectionListener pConnectionListener, PTurnConnection* ppTurnConnection) @@ -23,6 +69,11 @@ STATUS createTurnConnection(PIceServer pTurnServer, TIMER_QUEUE_HANDLE timerQueu !IS_EMPTY_STRING(pTurnServer->username), STATUS_INVALID_ARG); + // Set the TURN server hostname in the socket connection for TLS hostname verification + PCHAR hostname = NULL; + CHK_STATUS(getHostnameFromUrl(pTurnServer->url, &hostname)); + pTurnSocket->hostname = hostname; + pTurnConnection = (PTurnConnection) MEMCALLOC( 1, SIZEOF(TurnConnection) + DEFAULT_TURN_MESSAGE_RECV_CHANNEL_DATA_BUFFER_LEN * 2 + DEFAULT_TURN_MESSAGE_SEND_CHANNEL_DATA_BUFFER_LEN); CHK(pTurnConnection != NULL, STATUS_NOT_ENOUGH_MEMORY); @@ -70,8 +121,13 @@ STATUS createTurnConnection(PIceServer pTurnServer, TIMER_QUEUE_HANDLE timerQueu CHK_LOG_ERR(retStatus); - if (STATUS_FAILED(retStatus) && pTurnConnection != NULL) { - freeTurnConnection(&pTurnConnection); + if (STATUS_FAILED(retStatus)) { + if (pTurnConnection != NULL) { + freeTurnConnection(&pTurnConnection); + } + if (pTurnSocket != NULL) { + SAFE_MEMFREE(pTurnSocket->hostname); + } } if (ppTurnConnection != NULL) { diff --git a/src/source/Include_i.h b/src/source/Include_i.h index b606165d8c..a3b897a9a3 100644 --- a/src/source/Include_i.h +++ b/src/source/Include_i.h @@ -39,7 +39,9 @@ extern "C" { #include #include #include +#if MBEDTLS_VERSION_NUMBER < 0x03000000 #include +#endif #include #include #endif @@ -52,12 +54,12 @@ extern "C" { #define INET6 1 #include -#include - #if !defined __WINDOWS_BUILD__ #include #include +#ifdef HAVE_IFADDRS_H #include +#endif #include #include #include @@ -65,6 +67,9 @@ extern "C" { #include #include #include +#ifdef HAVE_POLL_H +#include +#endif #endif // Max uFrag and uPwd length as documented in https://tools.ietf.org/html/rfc5245#section-15.4 diff --git a/src/source/PeerConnection/Retransmitter.c b/src/source/PeerConnection/Retransmitter.c index b8b66a483b..fd87bda1ae 100644 --- a/src/source/PeerConnection/Retransmitter.c +++ b/src/source/PeerConnection/Retransmitter.c @@ -121,13 +121,17 @@ STATUS resendPacketOnNack(PRtcpPacket pRtcpPacket, PKvsPeerConnection pKvsPeerCo pRtpPacket = NULL; } } -CleanUp: - MUTEX_LOCK(pSenderTranceiver->statsLock); - pSenderTranceiver->outboundStats.nackCount += nackCount; - pSenderTranceiver->outboundStats.retransmittedPacketsSent += retransmittedPacketsSent; - pSenderTranceiver->outboundStats.retransmittedBytesSent += retransmittedBytesSent; - MUTEX_UNLOCK(pSenderTranceiver->statsLock); +CleanUp: + if (pSenderTranceiver != NULL) { + MUTEX_LOCK(pSenderTranceiver->statsLock); + pSenderTranceiver->outboundStats.nackCount += nackCount; + pSenderTranceiver->outboundStats.retransmittedPacketsSent += retransmittedPacketsSent; + pSenderTranceiver->outboundStats.retransmittedBytesSent += retransmittedBytesSent; + MUTEX_UNLOCK(pSenderTranceiver->statsLock); + } else { + DLOGD("Retransmit pSenderTranceiver is NULL"); + } CHK_LOG_ERR(retStatus); if (pRtpPacket != NULL) { diff --git a/src/source/PeerConnection/SessionDescription.c b/src/source/PeerConnection/SessionDescription.c index eeda6923f3..0960daf305 100644 --- a/src/source/PeerConnection/SessionDescription.c +++ b/src/source/PeerConnection/SessionDescription.c @@ -1,6 +1,30 @@ #define LOG_CLASS "SessionDescription" #include "../Include_i.h" +// Case-insensitive string contains +// Finds the first occurrence of the substring needle in the string haystack. +// The terminating null bytes are not compared. +PCHAR STRCASESTR(PCHAR haystack, PCHAR needle) +{ + if (haystack == NULL || needle == NULL) { + return NULL; + } + + UINT32 needleLen = STRLEN(needle); + if (needleLen == 0) { + return haystack; + } + + PCHAR p = haystack; + while (*p != '\0') { + if (STRNCMPI(p, needle, needleLen) == 0) { + return p; + } + p++; + } + return NULL; +} + STATUS serializeSessionDescriptionInit(PRtcSessionDescriptionInit pSessionDescriptionInit, PCHAR sessionDescriptionJSON, PUINT32 sessionDescriptionJSONLen) { @@ -9,6 +33,8 @@ STATUS serializeSessionDescriptionInit(PRtcSessionDescriptionInit pSessionDescri PCHAR curr, tail, next; UINT32 lineLen, inputSize = 0, amountWritten; + // NOTE: sessionDescriptionJSON can be NULL. In this case, no writing is actually done, + // but the size that it would have written is returned. CHK(pSessionDescriptionInit != NULL && sessionDescriptionJSONLen != NULL, STATUS_NULL_ARG); inputSize = *sessionDescriptionJSONLen; @@ -30,21 +56,29 @@ STATUS serializeSessionDescriptionInit(PRtcSessionDescriptionInit pSessionDescri lineLen--; } - amountWritten = - SNPRINTF(sessionDescriptionJSON + *sessionDescriptionJSONLen, sessionDescriptionJSON == NULL ? 0 : inputSize - *sessionDescriptionJSONLen, - "%*.*s%s", lineLen, lineLen, curr, SESSION_DESCRIPTION_INIT_LINE_ENDING); + if (sessionDescriptionJSON == NULL) { + amountWritten = SNPRINTF(NULL, 0, "%*.*s%s", lineLen, lineLen, curr, SESSION_DESCRIPTION_INIT_LINE_ENDING); + } else { + amountWritten = SNPRINTF(sessionDescriptionJSON + *sessionDescriptionJSONLen, inputSize - *sessionDescriptionJSONLen, "%*.*s%s", lineLen, + lineLen, curr, SESSION_DESCRIPTION_INIT_LINE_ENDING); + } CHK(sessionDescriptionJSON == NULL || ((inputSize - *sessionDescriptionJSONLen) >= amountWritten), STATUS_BUFFER_TOO_SMALL); *sessionDescriptionJSONLen += amountWritten; curr = next + 1; } - amountWritten = SNPRINTF(sessionDescriptionJSON + *sessionDescriptionJSONLen, - sessionDescriptionJSON == NULL ? 0 : inputSize - *sessionDescriptionJSONLen, SESSION_DESCRIPTION_INIT_TEMPLATE_TAIL); + if (sessionDescriptionJSON == NULL) { + amountWritten = SNPRINTF(NULL, 0, SESSION_DESCRIPTION_INIT_TEMPLATE_TAIL); + } else { + amountWritten = SNPRINTF(sessionDescriptionJSON + *sessionDescriptionJSONLen, inputSize - *sessionDescriptionJSONLen, + SESSION_DESCRIPTION_INIT_TEMPLATE_TAIL); + } CHK(sessionDescriptionJSON == NULL || ((inputSize - *sessionDescriptionJSONLen) >= amountWritten), STATUS_BUFFER_TOO_SMALL); *sessionDescriptionJSONLen += (amountWritten + 1); // NULL terminator CleanUp: + CHK_LOG_ERR(retStatus); LEAVES(); return retStatus; @@ -206,7 +240,7 @@ STATUS setPayloadTypesFromOffer(PHashTable codecTable, PHashTable rtxTable, PSes // When there's no match, the last fmtp will be chosen. This will allow us to not break existing customers who might be using // flexible decoders which can infer the video profile from the SPS header. if (fmtpScore >= bestFmtpScore) { - DLOGV("Found H264 payload type %" PRId64 " with score %lu: %s", parsedPayloadType, fmtpScore, fmtp); + DLOGV("Found H264 payload type %" PRId64 " with score %lu: %s", parsedPayloadType, fmtpScore, fmtp ? fmtp : "NULL"); CHK_STATUS( hashTableUpsert(codecTable, RTC_CODEC_H264_PROFILE_42E01F_LEVEL_ASYMMETRY_ALLOWED_PACKETIZATION_MODE, parsedPayloadType)); bestFmtpScore = fmtpScore; @@ -1301,7 +1335,7 @@ STATUS findTransceiversByRemoteDescription(PKvsPeerConnection pKvsPeerConnection if (STRSTR(attributeValue, H264_VALUE) != NULL) { supportCodec = TRUE; rtcCodec = RTC_CODEC_H264_PROFILE_42E01F_LEVEL_ASYMMETRY_ALLOWED_PACKETIZATION_MODE; - } else if (STRSTR(attributeValue, OPUS_VALUE) != NULL) { + } else if (STRCASESTR(attributeValue, OPUS_VALUE) != NULL) { supportCodec = TRUE; rtcCodec = RTC_CODEC_OPUS; } else if (STRSTR(attributeValue, MULAW_VALUE) != NULL) { diff --git a/src/source/PeerConnection/SessionDescription.h b/src/source/PeerConnection/SessionDescription.h index a7e5ac4903..bbd0d4f83e 100644 --- a/src/source/PeerConnection/SessionDescription.h +++ b/src/source/PeerConnection/SessionDescription.h @@ -51,10 +51,8 @@ extern "C" { #define DEFAULT_PAYLOAD_MULAW_STR (PCHAR) "0" #define DEFAULT_PAYLOAD_ALAW_STR (PCHAR) "8" -#define DEFAULT_H264_FMTP (PCHAR) "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f" -#define DEFAULT_H265_FMTP \ - (PCHAR) "profile-space=0;profile-id=0;tier-flag=0;level-id=0;interop-constraints=000000000000;sprop-vps=QAEMAf//" \ - "AIAAAAMAAAMAAAMAAAMAALUCQA==;sprop-sps=QgEBAIAAAAMAAAMAAAMAAAMAAKACgIAtH+W1kkbQzkkktySqSfKSyA==;sprop-pps=RAHBpVgeSA==" +#define DEFAULT_H264_FMTP (PCHAR) "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f" +#define DEFAULT_H265_FMTP (PCHAR) "level-id=93;profile-id=1" #define DEFAULT_OPUS_FMTP (PCHAR) "minptime=10;useinbandfec=1" #define H264_PROFILE_42E01F 0x42e01f // profile-level-id: diff --git a/src/source/Sctp/Sctp.c b/src/source/Sctp/Sctp.c index cd49fa6ab4..2385eafaae 100644 --- a/src/source/Sctp/Sctp.c +++ b/src/source/Sctp/Sctp.c @@ -210,11 +210,11 @@ STATUS sctpSessionWriteMessage(PSctpSession pSctpSession, UINT32 streamId, BOOL // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // \ / // | Label | -// / \ +// / / // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // \ / // | Protocol | -// / \ +// / / // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ STATUS sctpSessionWriteDcep(PSctpSession pSctpSession, UINT32 streamId, PCHAR pChannelName, UINT32 pChannelNameLen, PRtcDataChannelInit pRtcDataChannelInit) diff --git a/src/source/Signaling/LwsApiCalls.c b/src/source/Signaling/LwsApiCalls.c index 1b6721b374..1f20118be8 100644 --- a/src/source/Signaling/LwsApiCalls.c +++ b/src/source/Signaling/LwsApiCalls.c @@ -3,6 +3,11 @@ */ #define LOG_CLASS "LwsApiCalls" #include "../Include_i.h" + +#include +#include "Signaling.h" +#include "LwsApiCalls.h" + #define WEBRTC_SCHEME_NAME "webrtc" static BOOL gInterruptedFlagBySignalHandler; @@ -12,7 +17,7 @@ VOID lwsSignalHandler(INT32 signal) gInterruptedFlagBySignalHandler = TRUE; } -INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, PVOID user, PVOID pDataIn, size_t dataSize) +INT32 lwsHttpCallbackRoutine(PVOID wsi, INT32 reason, PVOID user, PVOID pDataIn, size_t dataSize) { UNUSED_PARAM(user); STATUS retStatus = STATUS_SUCCESS; @@ -53,12 +58,12 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, CHK(FALSE, retStatus); } - customData = lws_get_opaque_user_data(wsi); + customData = lws_get_opaque_user_data((struct lws*) wsi); pLwsCallInfo = (PLwsCallInfo) customData; CHK_STATUS(configureLwsLogging(loggerGetLogLevel())); - CHK(pLwsCallInfo != NULL && pLwsCallInfo->pSignalingClient != NULL && pLwsCallInfo->pSignalingClient->pLwsContext != NULL && + CHK(pLwsCallInfo != NULL && pLwsCallInfo->pSignalingClient != NULL && pLwsCallInfo->pSignalingClient->pWebsocketContext != NULL && pLwsCallInfo->callInfo.pRequestInfo != NULL && pLwsCallInfo->protocolIndex == PROTOCOL_INDEX_HTTPS, retStatus); @@ -98,13 +103,13 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, break; case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - status = lws_http_client_http_response(wsi); + status = lws_http_client_http_response((struct lws*) wsi); getStateMachineCurrentState(pSignalingClient->pStateMachine, &pStateMachineState); DLOGD("Connected with server response: %d", status); pLwsCallInfo->callInfo.callResult = getServiceCallResultFromHttpStatus((UINT32) status); - len = (SIZE_T) lws_hdr_copy(wsi, &dateHdrBuffer[0], MAX_DATE_HEADER_BUFFER_LENGTH, WSI_TOKEN_HTTP_DATE); + len = (SIZE_T) lws_hdr_copy((struct lws*) wsi, &dateHdrBuffer[0], MAX_DATE_HEADER_BUFFER_LENGTH, WSI_TOKEN_HTTP_DATE); time(&td); @@ -141,7 +146,7 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, } // Store the Request ID header - if ((size = lws_hdr_custom_copy(wsi, pBuffer, LWS_SCRATCH_BUFFER_SIZE, SIGNALING_REQUEST_ID_HEADER_NAME, + if ((size = lws_hdr_custom_copy((struct lws*) wsi, pBuffer, LWS_SCRATCH_BUFFER_SIZE, SIGNALING_REQUEST_ID_HEADER_NAME, (SIZEOF(SIGNALING_REQUEST_ID_HEADER_NAME) - 1) * SIZEOF(CHAR))) > 0) { pBuffer[size] = '\0'; DLOGI("Request ID: %s", pBuffer); @@ -182,7 +187,7 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, DLOGD("Received client http"); size = LWS_SCRATCH_BUFFER_SIZE; - if (lws_http_client_read(wsi, &pBuffer, &size) < 0) { + if (lws_http_client_read((struct lws*) wsi, &pBuffer, &size) < 0) { retValue = -1; } @@ -217,8 +222,8 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, DLOGV("Appending header - %s %s", pRequestHeader->pName, pRequestHeader->pValue); - status = lws_add_http_header_by_name(wsi, (PBYTE) pRequestHeader->pName, (PBYTE) pRequestHeader->pValue, pRequestHeader->valueLen, - ppStartPtr, pEndPtr); + status = lws_add_http_header_by_name((struct lws*) wsi, (PBYTE) pRequestHeader->pName, (PBYTE) pRequestHeader->pValue, + pRequestHeader->valueLen, ppStartPtr, pEndPtr); if (status != 0) { retValue = 1; CHK(FALSE, retStatus); @@ -232,8 +237,8 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, headerCount--; } - lws_client_http_body_pending(wsi, 1); - lws_callback_on_writable(wsi); + lws_client_http_body_pending((struct lws*) wsi, 1); + lws_callback_on_writable((struct lws*) wsi); break; @@ -241,20 +246,20 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, DLOGD("Sending the body %.*s, size %d", pRequestInfo->bodySize, pRequestInfo->body, pRequestInfo->bodySize); MEMCPY(pBuffer, pRequestInfo->body, pRequestInfo->bodySize); - size = lws_write(wsi, (PBYTE) pBuffer, (SIZE_T) pRequestInfo->bodySize, LWS_WRITE_TEXT); + size = lws_write((struct lws*) wsi, (PBYTE) pBuffer, (SIZE_T) pRequestInfo->bodySize, LWS_WRITE_TEXT); if (size != (INT32) pRequestInfo->bodySize) { DLOGW("Failed to write out the body of POST request entirely. Expected to write %d, wrote %d", pRequestInfo->bodySize, size); if (size > 0) { // Schedule again - lws_client_http_body_pending(wsi, 1); - lws_callback_on_writable(wsi); + lws_client_http_body_pending((struct lws*) wsi, 1); + lws_callback_on_writable((struct lws*) wsi); } else { // Quit retValue = 1; } } else { - lws_client_http_body_pending(wsi, 0); + lws_client_http_body_pending((struct lws*) wsi, 0); } break; @@ -271,7 +276,7 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, ATOMIC_STORE_BOOL(&pRequestInfo->terminating, TRUE); } - lws_cancel_service(lws_get_context(wsi)); + lws_cancel_service(lws_get_context((struct lws*) wsi)); retValue = -1; } @@ -283,7 +288,7 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, return retValue; } -INT32 lwsWssCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, PVOID user, PVOID pDataIn, size_t dataSize) +INT32 lwsWssCallbackRoutine(PVOID wsi, INT32 reason, PVOID user, PVOID pDataIn, size_t dataSize) { UNUSED_PARAM(user); STATUS retStatus = STATUS_SUCCESS; @@ -312,14 +317,14 @@ INT32 lwsWssCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, P CHK(FALSE, retStatus); } - customData = lws_get_opaque_user_data(wsi); + customData = lws_get_opaque_user_data((struct lws*) wsi); pLwsCallInfo = (PLwsCallInfo) customData; CHK_STATUS(configureLwsLogging(loggerGetLogLevel())); CHK(pLwsCallInfo != NULL && pLwsCallInfo->pSignalingClient != NULL && pLwsCallInfo->pSignalingClient->pOngoingCallInfo != NULL && - pLwsCallInfo->pSignalingClient->pLwsContext != NULL && pLwsCallInfo->pSignalingClient->pOngoingCallInfo->callInfo.pRequestInfo != NULL && - pLwsCallInfo->protocolIndex == PROTOCOL_INDEX_WSS, + pLwsCallInfo->pSignalingClient->pWebsocketContext != NULL && + pLwsCallInfo->pSignalingClient->pOngoingCallInfo->callInfo.pRequestInfo != NULL && pLwsCallInfo->protocolIndex == PROTOCOL_INDEX_WSS, retStatus); pSignalingClient = pLwsCallInfo->pSignalingClient; pLwsCallInfo = pSignalingClient->pOngoingCallInfo; @@ -564,7 +569,7 @@ STATUS lwsCompleteSync(PLwsCallInfo pCallInfo) CHK_STATUS(removeRequestHeader(pCallInfo->callInfo.pRequestInfo, AWS_SIG_V4_HEADER_HOST)); } - pContext = pCallInfo->pSignalingClient->pLwsContext; + pContext = (struct lws_context*) pCallInfo->pSignalingClient->pWebsocketContext; // Execute the LWS REST call MEMSET(&connectInfo, 0x00, SIZEOF(struct lws_client_connect_info)); @@ -596,7 +601,7 @@ STATUS lwsCompleteSync(PLwsCallInfo pCallInfo) connectInfo.path = path; connectInfo.host = connectInfo.address; connectInfo.method = pVerb; - connectInfo.protocol = pCallInfo->pSignalingClient->signalingProtocols[pCallInfo->protocolIndex].name; + connectInfo.protocol = ((struct lws_protocols*) pCallInfo->pSignalingClient->signalingProtocols[pCallInfo->protocolIndex])->name; connectInfo.pwsi = &clientLws; connectInfo.opaque_user_data = pCallInfo; @@ -1135,17 +1140,10 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time) UNUSED_PARAM(time); PRequestInfo pRequestInfo = NULL; - CHAR url[MAX_URI_CHAR_LEN + 1]; - CHAR paramsJson[MAX_JSON_PARAMETER_STRING_LEN]; + CHAR url[MAX_URI_CHAR_LEN + 1], paramsJson[MAX_JSON_PARAMETER_STRING_LEN]; PLwsCallInfo pLwsCallInfo = NULL; PCHAR pResponseStr; - jsmn_parser parser; - jsmntok_t tokens[MAX_JSON_TOKEN_COUNT]; - jsmntok_t* pToken; - UINT32 i, strLen, resultLen, configCount = 0, tokenCount; - INT32 j; - UINT64 ttl; - BOOL jsonInIceServerList = FALSE; + UINT32 resultLen; CHK(pSignalingClient != NULL, STATUS_NULL_ARG); CHK(pSignalingClient->channelEndpointHttps[0] != '\0', STATUS_INTERNAL_ERROR); @@ -1188,14 +1186,47 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time) CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, STATUS_SIGNALING_LWS_CALL_FAILED); - // Parse the response + CHK_STATUS( + parseIceConfigResponse(pResponseStr, resultLen, MAX_ICE_CONFIG_COUNT, pSignalingClient->iceConfigs, &pSignalingClient->iceConfigCount)); + + // Perform some validation on the ice configuration + CHK_STATUS(validateIceConfiguration(pSignalingClient)); + +CleanUp: + + if (STATUS_FAILED(retStatus)) { + DLOGE("Call Failed with Status: 0x%08x", retStatus); + } + + freeLwsCallInfo(&pLwsCallInfo); + + LEAVES(); + return retStatus; +} + +STATUS parseIceConfigResponse(PCHAR pResponseStr, UINT32 responseLen, UINT8 maxIceConfigs, PIceConfigInfo pIceConfigs, PUINT32 pIceConfigCount) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + jsmn_parser parser; + jsmntok_t tokens[MAX_JSON_TOKEN_COUNT]; + jsmntok_t* pToken; + UINT32 i, configCount = 0, strLen; // Note: strLen excludes the NULL terminator + INT32 j, tokenCount; + UINT64 ttl; + BOOL jsonInIceServerList = FALSE; + + CHK(pIceConfigs != NULL && pIceConfigCount != NULL && pResponseStr != NULL, STATUS_NULL_ARG); + CHK(maxIceConfigs > 0, STATUS_INVALID_ARG); + CHK(!IS_EMPTY_STRING(pResponseStr), STATUS_INVALID_API_CALL_RETURN_JSON); + jsmn_init(&parser); - tokenCount = jsmn_parse(&parser, pResponseStr, resultLen, tokens, SIZEOF(tokens) / SIZEOF(jsmntok_t)); + tokenCount = jsmn_parse(&parser, pResponseStr, responseLen, tokens, SIZEOF(tokens) / SIZEOF(jsmntok_t)); CHK(tokenCount > 1, STATUS_INVALID_API_CALL_RETURN_JSON); CHK(tokens[0].type == JSMN_OBJECT, STATUS_INVALID_API_CALL_RETURN_JSON); - MEMSET(&pSignalingClient->iceConfigs, 0x00, MAX_ICE_CONFIG_COUNT * SIZEOF(IceConfigInfo)); - pSignalingClient->iceConfigCount = 0; + MEMSET(pIceConfigs, 0x00, maxIceConfigs * SIZEOF(IceConfigInfo)); + *pIceConfigCount = 0; // Loop through the tokens and extract the ice configuration for (i = 0; i < tokenCount; i++) { @@ -1204,58 +1235,64 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time) jsonInIceServerList = TRUE; CHK(tokens[i + 1].type == JSMN_ARRAY, STATUS_INVALID_API_CALL_RETURN_JSON); - CHK(tokens[i + 1].size <= MAX_ICE_CONFIG_COUNT, STATUS_SIGNALING_MAX_ICE_CONFIG_COUNT); + if (tokens[i + 1].size > maxIceConfigs) { + DLOGW("Received more ice configs (%d) than supported (%d). Will ignore the rest.", tokens[i + 1].size, maxIceConfigs); + } } } else { pToken = &tokens[i]; - if (pToken->type == JSMN_OBJECT) { + if (pToken->type == JSMN_UNDEFINED) { + DLOGW("Encountered unexpected item in the JSON! %*.s", pResponseStr, responseLen); + // Skip this token and continue parsing + // Don't return error, just move to next token + if (i + 1 < tokenCount && tokens[i + 1].type != JSMN_OBJECT) { + i++; // Skip the value associated with this field + } + continue; + } else if (pToken->type == JSMN_OBJECT) { + if (configCount + 1 > maxIceConfigs) { + // That's all we have room to parse + break; + } configCount++; } else if (compareJsonString(pResponseStr, pToken, JSMN_STRING, (PCHAR) "Username")) { strLen = (UINT32) (pToken[1].end - pToken[1].start); CHK(strLen <= MAX_ICE_CONFIG_USER_NAME_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - STRNCPY(pSignalingClient->iceConfigs[configCount - 1].userName, pResponseStr + pToken[1].start, strLen); - pSignalingClient->iceConfigs[configCount - 1].userName[MAX_ICE_CONFIG_USER_NAME_LEN] = '\0'; + SNPRINTF(pIceConfigs[configCount - 1].userName, MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN, "%.*s", strLen, pResponseStr + pToken[1].start); i++; } else if (compareJsonString(pResponseStr, pToken, JSMN_STRING, (PCHAR) "Password")) { strLen = (UINT32) (pToken[1].end - pToken[1].start); CHK(strLen <= MAX_ICE_CONFIG_CREDENTIAL_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - STRNCPY(pSignalingClient->iceConfigs[configCount - 1].password, pResponseStr + pToken[1].start, strLen); - pSignalingClient->iceConfigs[configCount - 1].userName[MAX_ICE_CONFIG_CREDENTIAL_LEN] = '\0'; + SNPRINTF(pIceConfigs[configCount - 1].password, MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN, "%.*s", strLen, pResponseStr + pToken[1].start); i++; } else if (compareJsonString(pResponseStr, pToken, JSMN_STRING, (PCHAR) "Ttl")) { - CHK_STATUS(STRTOUI64(pResponseStr + pToken[1].start, pResponseStr + pToken[1].end, 10, &ttl)); - - // NOTE: Ttl value is in seconds - pSignalingClient->iceConfigs[configCount - 1].ttl = ttl * HUNDREDS_OF_NANOS_IN_A_SECOND; + retStatus = STRTOUI64((PCHAR) pResponseStr + pToken[1].start, (PCHAR) pResponseStr + pToken[1].end, 10, &ttl); + if (STATUS_FAILED(retStatus)) { + strLen = (UINT32) (pToken[1].end - pToken[1].start); + DLOGE("Unable to convert TTL: %.*s to a number", strLen, (PCHAR) pResponseStr + pToken[1].start); + retStatus = STATUS_INVALID_API_CALL_RETURN_JSON; + CHK(FALSE, retStatus); + } + pIceConfigs[configCount - 1].ttl = ttl * HUNDREDS_OF_NANOS_IN_A_SECOND; i++; } else if (compareJsonString(pResponseStr, pToken, JSMN_STRING, (PCHAR) "Uris")) { - // Expect an array of elements CHK(pToken[1].type == JSMN_ARRAY, STATUS_INVALID_API_CALL_RETURN_JSON); CHK(pToken[1].size <= MAX_ICE_CONFIG_URI_COUNT, STATUS_SIGNALING_MAX_ICE_URI_COUNT); for (j = 0; j < pToken[1].size; j++) { strLen = (UINT32) (pToken[j + 2].end - pToken[j + 2].start); CHK(strLen <= MAX_ICE_CONFIG_URI_LEN, STATUS_SIGNALING_MAX_ICE_URI_LEN); - STRNCPY(pSignalingClient->iceConfigs[configCount - 1].uris[j], pResponseStr + pToken[j + 2].start, strLen); - pSignalingClient->iceConfigs[configCount - 1].uris[j][MAX_ICE_CONFIG_URI_LEN] = '\0'; - pSignalingClient->iceConfigs[configCount - 1].uriCount++; + SNPRINTF(pIceConfigs[configCount - 1].uris[j], MAX_ICE_CONFIG_URI_BUFFER_LEN, "%.*s", strLen, pResponseStr + pToken[j + 2].start); + pIceConfigs[configCount - 1].uriCount++; } - i += pToken[1].size + 1; } } } - // Perform some validation on the ice configuration - pSignalingClient->iceConfigCount = configCount; - CHK_STATUS(validateIceConfiguration(pSignalingClient)); + *pIceConfigCount = configCount; CleanUp: - - if (STATUS_FAILED(retStatus)) { - DLOGE("Call Failed with Status: 0x%08x", retStatus); - } - - freeLwsCallInfo(&pLwsCallInfo); + CHK_LOG_ERR(retStatus); LEAVES(); return retStatus; @@ -1974,76 +2011,52 @@ STATUS writeLwsData(PSignalingClient pSignalingClient, BOOL awaitForResponse) return retStatus; } -STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT32 messageLen) +STATUS parseSignalingMessage(PCHAR pMessage, UINT32 messageLen, PReceivedSignalingMessage pReceivedSignalingMessage) { ENTERS(); STATUS retStatus = STATUS_SUCCESS; jsmn_parser parser; jsmntok_t tokens[MAX_JSON_TOKEN_COUNT]; jsmntok_t* pToken; - UINT32 i, strLen, outLen = MAX_SIGNALING_MESSAGE_LEN; - UINT32 tokenCount; - INT32 j; - PSignalingMessageWrapper pSignalingMessageWrapper = NULL; - TID receivedTid = INVALID_TID_VALUE; - BOOL parsedMessageType = FALSE, parsedStatusResponse = FALSE, jsonInIceServerList = FALSE; - PSignalingMessage pOngoingMessage; - UINT64 ttl; - - CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + INT32 tokenCount; + UINT32 i, strLen, outLen, printResult; + BOOL parsedMessageType = FALSE, parsedStatusResponse = FALSE; - // If we have a signalingMessage and if there is a correlation id specified then the response should be non-empty - if (pMessage == NULL || messageLen == 0) { - if (BLOCK_ON_CORRELATION_ID) { - // Get empty correlation id message from the ongoing if exists - CHK_STATUS(signalingGetOngoingMessage(pSignalingClient, EMPTY_STRING, EMPTY_STRING, &pOngoingMessage)); - if (pOngoingMessage == NULL) { - DLOGW("Received an empty body for a message with no correlation id which has been already removed from the queue. Warning 0x%08x", - STATUS_SIGNALING_RECEIVE_EMPTY_DATA_NOT_SUPPORTED); - } else { - CHK_STATUS(signalingRemoveOngoingMessage(pSignalingClient, EMPTY_STRING)); - } - } + CHK(pMessage != NULL && pReceivedSignalingMessage != NULL, STATUS_NULL_ARG); + CHK(messageLen <= MAX_SIGNALING_MESSAGE_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - // Check if anything needs to be done - CHK_WARN(pMessage != NULL && messageLen != 0, retStatus, "Signaling received an empty message"); - } + MEMSET(pReceivedSignalingMessage, 0x00, SIZEOF(ReceivedSignalingMessage)); + pReceivedSignalingMessage->signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_UNKNOWN; - // Parse the response jsmn_init(&parser); tokenCount = jsmn_parse(&parser, pMessage, messageLen, tokens, SIZEOF(tokens) / SIZEOF(jsmntok_t)); CHK(tokenCount > 1, STATUS_INVALID_API_CALL_RETURN_JSON); CHK(tokens[0].type == JSMN_OBJECT, STATUS_INVALID_API_CALL_RETURN_JSON); - CHK(NULL != (pSignalingMessageWrapper = (PSignalingMessageWrapper) MEMCALLOC(1, SIZEOF(SignalingMessageWrapper))), STATUS_NOT_ENOUGH_MEMORY); - - pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; - - // Loop through the tokens and extract the stream description for (i = 1; i < tokenCount; i++) { if (compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "senderClientId")) { strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); CHK(strLen <= MAX_SIGNALING_CLIENT_ID_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - STRNCPY(pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.peerClientId, pMessage + tokens[i + 1].start, strLen); - pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.peerClientId[MAX_SIGNALING_CLIENT_ID_LEN] = '\0'; + printResult = SNPRINTF(pReceivedSignalingMessage->signalingMessage.peerClientId, MAX_SIGNALING_CLIENT_ID_LEN + 1, "%.*s", strLen, + pMessage + tokens[i + 1].start); + CHK(printResult >= 0 && printResult <= MAX_SIGNALING_CLIENT_ID_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); i++; } else if (compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "messageType")) { strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); CHK(strLen <= MAX_SIGNALING_MESSAGE_TYPE_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - CHK_STATUS(getMessageTypeFromString(pMessage + tokens[i + 1].start, strLen, - &pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.messageType)); - + CHK_STATUS(getMessageTypeFromString(pMessage + tokens[i + 1].start, strLen, &pReceivedSignalingMessage->signalingMessage.messageType)); parsedMessageType = TRUE; i++; } else if (compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "messagePayload")) { strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); CHK(strLen <= MAX_SIGNALING_MESSAGE_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); + outLen = SIZEOF(pReceivedSignalingMessage->signalingMessage.payload); + // Base64 method will set outLen <= original input outLen + CHK_STATUS(base64Decode(pMessage + tokens[i + 1].start, strLen, (PBYTE) (pReceivedSignalingMessage->signalingMessage.payload), &outLen)); - // Base64 decode the message - CHK_STATUS(base64Decode(pMessage + tokens[i + 1].start, strLen, - (PBYTE) (pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.payload), &outLen)); - pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.payload[MAX_SIGNALING_MESSAGE_LEN] = '\0'; - pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.payloadLen = outLen; + // Need to manually null-terminate the output of base64Decode + pReceivedSignalingMessage->signalingMessage.payload[outLen] = '\0'; + pReceivedSignalingMessage->signalingMessage.payloadLen = outLen; i++; } else if (!parsedStatusResponse && compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "statusResponse")) { parsedStatusResponse = TRUE; @@ -2051,85 +2064,74 @@ STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT } else if (parsedStatusResponse && compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "correlationId")) { strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); CHK(strLen <= MAX_CORRELATION_ID_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - STRNCPY(pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.correlationId, pMessage + tokens[i + 1].start, strLen); - pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.correlationId[MAX_CORRELATION_ID_LEN] = '\0'; - + printResult = SNPRINTF(pReceivedSignalingMessage->signalingMessage.correlationId, MAX_CORRELATION_ID_LEN + 1, "%.*s", strLen, + pMessage + tokens[i + 1].start); + CHK(printResult >= 0 && printResult <= MAX_CORRELATION_ID_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); i++; } else if (parsedStatusResponse && compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "errorType")) { strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); CHK(strLen <= MAX_ERROR_TYPE_STRING_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - STRNCPY(pSignalingMessageWrapper->receivedSignalingMessage.errorType, pMessage + tokens[i + 1].start, strLen); - pSignalingMessageWrapper->receivedSignalingMessage.errorType[MAX_ERROR_TYPE_STRING_LEN] = '\0'; - + printResult = + SNPRINTF(pReceivedSignalingMessage->errorType, MAX_ERROR_TYPE_STRING_LEN + 1, "%.*s", strLen, pMessage + tokens[i + 1].start); + CHK(printResult >= 0 && printResult <= MAX_ERROR_TYPE_STRING_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); i++; } else if (parsedStatusResponse && compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "statusCode")) { strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); CHK(strLen <= MAX_STATUS_CODE_STRING_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - - // Parse the status code - CHK_STATUS(STRTOUI32(pMessage + tokens[i + 1].start, pMessage + tokens[i + 1].end, 10, - &pSignalingMessageWrapper->receivedSignalingMessage.statusCode)); - + CHK_STATUS(STRTOUI32(pMessage + tokens[i + 1].start, pMessage + tokens[i + 1].end, 10, &pReceivedSignalingMessage->statusCode)); i++; } else if (parsedStatusResponse && compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "description")) { strLen = (UINT32) (tokens[i + 1].end - tokens[i + 1].start); CHK(strLen <= MAX_MESSAGE_DESCRIPTION_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - STRNCPY(pSignalingMessageWrapper->receivedSignalingMessage.description, pMessage + tokens[i + 1].start, strLen); - pSignalingMessageWrapper->receivedSignalingMessage.description[MAX_MESSAGE_DESCRIPTION_LEN] = '\0'; - + printResult = + SNPRINTF(pReceivedSignalingMessage->description, MAX_MESSAGE_DESCRIPTION_LEN + 1, "%.*s", strLen, pMessage + tokens[i + 1].start); + CHK(printResult >= 0 && printResult <= MAX_MESSAGE_DESCRIPTION_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); i++; - } else if (!jsonInIceServerList && - pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.messageType == SIGNALING_MESSAGE_TYPE_OFFER && - compareJsonString(pMessage, &tokens[i], JSMN_STRING, (PCHAR) "IceServerList")) { - jsonInIceServerList = TRUE; - - CHK(tokens[i + 1].type == JSMN_ARRAY, STATUS_INVALID_API_CALL_RETURN_JSON); - CHK(tokens[i + 1].size <= MAX_ICE_CONFIG_COUNT, STATUS_SIGNALING_MAX_ICE_CONFIG_COUNT); - - // Zero the ice configs - MEMSET(&pSignalingClient->iceConfigs, 0x00, MAX_ICE_CONFIG_COUNT * SIZEOF(IceConfigInfo)); - pSignalingClient->iceConfigCount = 0; - } else if (jsonInIceServerList) { - pToken = &tokens[i]; - if (pToken->type == JSMN_OBJECT) { - pSignalingClient->iceConfigCount++; - } else if (compareJsonString(pMessage, pToken, JSMN_STRING, (PCHAR) "Username")) { - strLen = (UINT32) (pToken[1].end - pToken[1].start); - CHK(strLen <= MAX_ICE_CONFIG_USER_NAME_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - STRNCPY(pSignalingClient->iceConfigs[pSignalingClient->iceConfigCount - 1].userName, pMessage + pToken[1].start, strLen); - pSignalingClient->iceConfigs[pSignalingClient->iceConfigCount - 1].userName[MAX_ICE_CONFIG_USER_NAME_LEN] = '\0'; - i++; - } else if (compareJsonString(pMessage, pToken, JSMN_STRING, (PCHAR) "Password")) { - strLen = (UINT32) (pToken[1].end - pToken[1].start); - CHK(strLen <= MAX_ICE_CONFIG_CREDENTIAL_LEN, STATUS_INVALID_API_CALL_RETURN_JSON); - STRNCPY(pSignalingClient->iceConfigs[pSignalingClient->iceConfigCount - 1].password, pMessage + pToken[1].start, strLen); - pSignalingClient->iceConfigs[pSignalingClient->iceConfigCount - 1].userName[MAX_ICE_CONFIG_CREDENTIAL_LEN] = '\0'; - i++; - } else if (compareJsonString(pMessage, pToken, JSMN_STRING, (PCHAR) "Ttl")) { - CHK_STATUS(STRTOUI64(pMessage + pToken[1].start, pMessage + pToken[1].end, 10, &ttl)); + } + } - // NOTE: Ttl value is in seconds - pSignalingClient->iceConfigs[pSignalingClient->iceConfigCount - 1].ttl = ttl * HUNDREDS_OF_NANOS_IN_A_SECOND; - i++; - } else if (compareJsonString(pMessage, pToken, JSMN_STRING, (PCHAR) "Uris")) { - // Expect an array of elements - CHK(pToken[1].type == JSMN_ARRAY, STATUS_INVALID_API_CALL_RETURN_JSON); - CHK(pToken[1].size <= MAX_ICE_CONFIG_URI_COUNT, STATUS_SIGNALING_MAX_ICE_URI_COUNT); - for (j = 0; j < pToken[1].size; j++) { - strLen = (UINT32) (pToken[j + 2].end - pToken[j + 2].start); - CHK(strLen <= MAX_ICE_CONFIG_URI_LEN, STATUS_SIGNALING_MAX_ICE_URI_LEN); - STRNCPY(pSignalingClient->iceConfigs[pSignalingClient->iceConfigCount - 1].uris[j], pMessage + pToken[j + 2].start, strLen); - pSignalingClient->iceConfigs[pSignalingClient->iceConfigCount - 1].uris[j][MAX_ICE_CONFIG_URI_LEN] = '\0'; - pSignalingClient->iceConfigs[pSignalingClient->iceConfigCount - 1].uriCount++; - } + CHK(parsedMessageType, STATUS_SIGNALING_INVALID_MESSAGE_TYPE); - i += pToken[1].size + 1; +CleanUp: + CHK_LOG_ERR(retStatus); + + LEAVES(); + return retStatus; +} + +STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT32 messageLen) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + UINT32 i, strLen; + PSignalingMessageWrapper pSignalingMessageWrapper = NULL; + TID receivedTid = INVALID_TID_VALUE; + PSignalingMessage pOngoingMessage; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + // If we have a signalingMessage and if there is a correlation id specified then the response should be non-empty + if (pMessage == NULL || messageLen == 0) { + if (BLOCK_ON_CORRELATION_ID) { + // Get empty correlation id message from the ongoing if exists + CHK_STATUS(signalingGetOngoingMessage(pSignalingClient, EMPTY_STRING, EMPTY_STRING, &pOngoingMessage)); + if (pOngoingMessage == NULL) { + DLOGW("Received an empty body for a message with no correlation id which has been already removed from the queue. Warning 0x%08x", + STATUS_SIGNALING_RECEIVE_EMPTY_DATA_NOT_SUPPORTED); + } else { + CHK_STATUS(signalingRemoveOngoingMessage(pSignalingClient, EMPTY_STRING)); } } + + // Check if anything needs to be done + CHK_WARN(pMessage != NULL && messageLen != 0, retStatus, "Signaling received an empty message"); } - // Message type is a mandatory field. - CHK(parsedMessageType, STATUS_SIGNALING_INVALID_MESSAGE_TYPE); + // Parse the response + CHK(NULL != (pSignalingMessageWrapper = (PSignalingMessageWrapper) MEMCALLOC(1, SIZEOF(SignalingMessageWrapper))), STATUS_NOT_ENOUGH_MEMORY); + pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; + CHK_STATUS(parseSignalingMessage(pMessage, messageLen, &pSignalingMessageWrapper->receivedSignalingMessage)); + pSignalingMessageWrapper->pSignalingClient = pSignalingClient; switch (pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.messageType) { @@ -2206,11 +2208,6 @@ STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT DLOGD("Client received message of type: %s", getMessageTypeInString(pSignalingMessageWrapper->receivedSignalingMessage.signalingMessage.messageType)); - // Validate and process the ice config - if (jsonInIceServerList && STATUS_FAILED(validateIceConfiguration(pSignalingClient))) { - DLOGW("Failed to validate the ICE server configuration received with an Offer"); - } - #ifdef ENABLE_KVS_THREADPOOL // This would fail if threadpool was not created CHK_STATUS(threadpoolContextPush(receiveLwsMessageWrapper, pSignalingMessageWrapper)); @@ -2413,7 +2410,7 @@ STATUS wakeLwsServiceEventLoop(PSignalingClient pSignalingClient, UINT32 protoco STATUS retStatus = STATUS_SUCCESS; // Early exit in case we don't need to do anything - CHK(pSignalingClient != NULL && pSignalingClient->pLwsContext != NULL, retStatus); + CHK(pSignalingClient != NULL && pSignalingClient->pWebsocketContext != NULL, retStatus); if (pSignalingClient->currentWsi[protocolIndex] != NULL) { lws_callback_on_writable(pSignalingClient->currentWsi[protocolIndex]); @@ -2447,7 +2444,6 @@ STATUS configureLwsLogging(UINT32 kvsLogLevel) lws_set_log_level(lws_levels, NULL); -CleanUp: CHK_LOG_ERR(retStatus); LEAVES(); diff --git a/src/source/Signaling/LwsApiCalls.h b/src/source/Signaling/LwsApiCalls.h index 4749b4b7c1..0f0a88cc09 100644 --- a/src/source/Signaling/LwsApiCalls.h +++ b/src/source/Signaling/LwsApiCalls.h @@ -167,11 +167,14 @@ extern "C" { // Encoded max ice server infos string len #define MAX_ENCODED_ICE_SERVER_INFOS_STR_LEN (MAX_ICE_SERVER_INFOS_STR_LEN + ICE_SERVER_INFO_TEMPLATE_BLOAT_SIZE) +// Alignment bytes for libwebsockets (Internally, it might end up using lesser than 16) +#define LWS_ALIGN_BYTES 16 + // Scratch buffer size -#define LWS_SCRATCH_BUFFER_SIZE (MAX_JSON_PARAMETER_STRING_LEN + LWS_PRE) +#define LWS_SCRATCH_BUFFER_SIZE (MAX_JSON_PARAMETER_STRING_LEN + LWS_ALIGN_BYTES) // Send and receive buffer size -#define LWS_MESSAGE_BUFFER_SIZE (SIZEOF(CHAR) * (MAX_SIGNALING_MESSAGE_LEN + LWS_PRE)) +#define LWS_MESSAGE_BUFFER_SIZE (SIZEOF(CHAR) * (MAX_SIGNALING_MESSAGE_LEN + LWS_ALIGN_BYTES)) #define AWS_SIG_V4_HEADER_HOST (PCHAR) "host" @@ -251,8 +254,8 @@ PVOID lwsListenerHandler(PVOID); PVOID reconnectHandler(PVOID); // LWS callback routine -INT32 lwsHttpCallbackRoutine(struct lws*, enum lws_callback_reasons, PVOID, PVOID, size_t); -INT32 lwsWssCallbackRoutine(struct lws*, enum lws_callback_reasons, PVOID, PVOID, size_t); +INT32 lwsHttpCallbackRoutine(PVOID, INT32, PVOID, PVOID, size_t); +INT32 lwsWssCallbackRoutine(PVOID, INT32, PVOID, PVOID, size_t); BOOL isCallResultSignatureExpired(PCallInfo); BOOL isCallResultSignatureNotYetCurrent(PCallInfo); @@ -281,6 +284,43 @@ STATUS wakeLwsServiceEventLoop(PSignalingClient, UINT32); STATUS terminateConnectionWithStatus(PSignalingClient, SERVICE_CALL_RESULT); STATUS configureLwsLogging(UINT32 kvsLogLevel); +/** + * Parses ICE configuration from a JSON response string. + * If there are more ICE configurations in the string than maxIceConfigs, we will only + * parse up until maxIceConfigs. + * + * @param[in] pResponseStr JSON string containing ICE server configuration. + * @param[in] responseLen Length of the JSON string (excluding null-terminator). + * @param[in] maxIceConfigs Maximum number of ICE configurations the array can hold. + * @param[out] pIceConfigs Pointer to array of IceConfigInfo structures to be populated. + * @param[out] pIceConfigCount Pointer to receive the number of ICE configurations parsed. + * + * @return STATUS code of the execution: + * - STATUS_SUCCESS: Successfully parsed ICE configuration. + * - STATUS_NULL_ARG: Invalid NULL argument provided. + * - STATUS_INVALID_API_CALL_RETURN_JSON: Malformed JSON or missing required fields. + * - STATUS_SIGNALING_MAX_ICE_URI_COUNT: Too many URIs in configuration (more than MAX_ICE_CONFIG_URI_COUNT). + */ +STATUS parseIceConfigResponse(PCHAR, UINT32, UINT8, PIceConfigInfo, PUINT32); + +/** + * Parses the signaling message from a JSON response string. + * The payload is base64-decoded in the output. + * See Asynchronous message + * reception. + * + * @param[in] pMessage JSON of the signaling message. + * @param[in] messageLen Length of the JSON string (excluding null-terminator). + * @param[out] pReceivedSignalingMessage Pointer to receive the parsed signaling message. + * + * @return STATUS code of the execution: + * - STATUS_SUCCESS: Successfully parsed ICE configuration. + * - STATUS_NULL_ARG: Invalid NULL argument provided. + * - STATUS_SIGNALING_INVALID_MESSAGE_TYPE: If the required field 'messageType' is missing for non error response messages. + * - STATUS_INVALID_API_CALL_RETURN_JSON: Malformed JSON or missing other required fields. + */ +STATUS parseSignalingMessage(PCHAR, UINT32, PReceivedSignalingMessage); + #ifdef __cplusplus } #endif diff --git a/src/source/Signaling/Signaling.c b/src/source/Signaling/Signaling.c index 1b001d4d7f..6c3938d7e5 100644 --- a/src/source/Signaling/Signaling.c +++ b/src/source/Signaling/Signaling.c @@ -1,9 +1,42 @@ #define LOG_CLASS "Signaling" #include "../Include_i.h" +#include +#include "LwsApiCalls.h" extern StateMachineState SIGNALING_STATE_MACHINE_STATES[]; extern UINT32 SIGNALING_STATE_MACHINE_STATE_COUNT; +// Allocate memory and read the CA certificate from the path +PRIVATE_API STATUS readCACertificate(PCHAR pCaCertPath, PBYTE* ppCaCertBuf, PUINT32 pCaCertBufLen) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + UINT64 cert_len = 0; + PBYTE cert_buf = NULL; + + CHK(pCaCertPath != NULL && ppCaCertBuf != NULL && pCaCertBufLen != NULL, STATUS_NULL_ARG); + + *ppCaCertBuf = NULL; + *pCaCertBufLen = 0; + + CHK_STATUS(readFile(pCaCertPath, FALSE, NULL, &cert_len)); + CHK(cert_len > 0, STATUS_INVALID_CERT_PATH_LENGTH); + cert_buf = (PBYTE) MEMCALLOC(1, cert_len + 1); // +1 for the null terminator + CHK(cert_buf != NULL, STATUS_NOT_ENOUGH_MEMORY); + CHK_STATUS(readFile(pCaCertPath, FALSE, cert_buf, &cert_len)); + + *ppCaCertBuf = cert_buf; + *pCaCertBufLen = (UINT32) cert_len; + cert_buf = NULL; // So that it is not freed by SAFE_MEMFREE + +CleanUp: + CHK_LOG_ERR(retStatus); + SAFE_MEMFREE(cert_buf); + + LEAVES(); + return retStatus; +} + STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInfo pChannelInfo, PSignalingClientCallbacks pCallbacks, PAwsCredentialProvider pCredentialProvider, PSignalingClient* ppSignalingClient) { @@ -12,6 +45,8 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf PSignalingClient pSignalingClient = NULL; PCHAR userLogLevelStr = NULL; UINT32 userLogLevel; + PBYTE caCertBuf = NULL; + UINT32 caCertBufLen = 0; struct lws_context_creation_info creationInfo; const lws_retry_bo_t retryPolicy = { .secs_since_valid_ping = SIGNALING_SERVICE_WSS_PING_PONG_INTERVAL_IN_SECONDS, @@ -21,6 +56,8 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf BOOL cacheFound = FALSE; PSignalingFileCacheEntry pFileCacheEntry = NULL; + struct lws_protocols* pProtocols = NULL; + CHK(pClientInfo != NULL && pChannelInfo != NULL && pCallbacks != NULL && pCredentialProvider != NULL && ppSignalingClient != NULL, STATUS_NULL_ARG); CHK(pChannelInfo->version <= CHANNEL_INFO_CURRENT_VERSION, STATUS_SIGNALING_INVALID_CHANNEL_INFO_VERSION); @@ -33,6 +70,14 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf CHK_STATUS(initializeThreadTracker(&pSignalingClient->listenerTracker)); CHK_STATUS(initializeThreadTracker(&pSignalingClient->reconnecterTracker)); + // Allocate memory for the protocols array (+ 1 for the null terminator protocol) + pProtocols = (struct lws_protocols*) MEMCALLOC(LWS_PROTOCOL_COUNT + 1, SIZEOF(struct lws_protocols)); + CHK(pProtocols != NULL, STATUS_NOT_ENOUGH_MEMORY); + + // Store the protocols pointer in the signalingProtocols array + pSignalingClient->signalingProtocols[PROTOCOL_INDEX_HTTPS] = pProtocols; + pSignalingClient->signalingProtocols[PROTOCOL_INDEX_WSS] = &pProtocols[PROTOCOL_INDEX_WSS]; + // Validate and store the input CHK_STATUS(createValidateChannelInfo(pChannelInfo, &pSignalingClient->pChannelInfo)); CHK_STATUS(validateSignalingCallbacks(pSignalingClient, pCallbacks)); @@ -93,10 +138,11 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf &pSignalingClient->pStateMachine)); // Prepare the signaling channel protocols array - pSignalingClient->signalingProtocols[PROTOCOL_INDEX_HTTPS].name = HTTPS_SCHEME_NAME; - pSignalingClient->signalingProtocols[PROTOCOL_INDEX_HTTPS].callback = lwsHttpCallbackRoutine; - pSignalingClient->signalingProtocols[PROTOCOL_INDEX_WSS].name = WSS_SCHEME_NAME; - pSignalingClient->signalingProtocols[PROTOCOL_INDEX_WSS].callback = lwsWssCallbackRoutine; + pProtocols = (struct lws_protocols*) pSignalingClient->signalingProtocols[PROTOCOL_INDEX_HTTPS]; + pProtocols[PROTOCOL_INDEX_HTTPS].name = HTTPS_SCHEME_NAME; + pProtocols[PROTOCOL_INDEX_HTTPS].callback = (lws_callback_function*) lwsHttpCallbackRoutine; + pProtocols[PROTOCOL_INDEX_WSS].name = WSS_SCHEME_NAME; + pProtocols[PROTOCOL_INDEX_WSS].callback = (lws_callback_function*) lwsWssCallbackRoutine; pSignalingClient->currentWsi[PROTOCOL_INDEX_HTTPS] = NULL; pSignalingClient->currentWsi[PROTOCOL_INDEX_WSS] = NULL; @@ -104,11 +150,13 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf MEMSET(&creationInfo, 0x00, SIZEOF(struct lws_context_creation_info)); creationInfo.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; creationInfo.port = CONTEXT_PORT_NO_LISTEN; - creationInfo.protocols = pSignalingClient->signalingProtocols; + creationInfo.protocols = pProtocols; creationInfo.timeout_secs = SIGNALING_SERVICE_API_CALL_TIMEOUT_IN_SECONDS; creationInfo.gid = -1; creationInfo.uid = -1; - creationInfo.client_ssl_ca_filepath = pChannelInfo->pCertPath; + CHK_STATUS(readCACertificate(pChannelInfo->pCertPath, &caCertBuf, &caCertBufLen)); + creationInfo.client_ssl_ca_mem = caCertBuf; + creationInfo.client_ssl_ca_mem_len = caCertBufLen; creationInfo.client_ssl_cipher_list = "HIGH:!PSK:!RSP:!eNULL:!aNULL:!RC4:!MD5:!DES:!3DES:!aDH:!kDH:!DSS"; creationInfo.ka_time = SIGNALING_SERVICE_TCP_KEEPALIVE_IN_SECONDS; creationInfo.ka_probes = SIGNALING_SERVICE_TCP_KEEPALIVE_PROBE_COUNT; @@ -165,8 +213,8 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf CHK_STATUS(configureLwsLogging(loggerGetLogLevel())); - pSignalingClient->pLwsContext = lws_create_context(&creationInfo); - CHK(pSignalingClient->pLwsContext != NULL, STATUS_SIGNALING_LWS_CREATE_CONTEXT_FAILED); + pSignalingClient->pWebsocketContext = lws_create_context(&creationInfo); + CHK(pSignalingClient->pWebsocketContext != NULL, STATUS_SIGNALING_LWS_CREATE_CONTEXT_FAILED); // Initializing the diagnostics mostly is taken care of by zero-mem in MEMCALLOC pSignalingClient->diagnostics.createTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); @@ -191,6 +239,7 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf SIGNALING_STATE_GET_TOKEN)); CleanUp: + SAFE_MEMFREE(caCertBuf); if (pClientInfo != NULL && pSignalingClient != NULL) { pClientInfo->signalingClientInfo.stateMachineRetryCountReadOnly = pSignalingClient->diagnostics.stateMachineRetryCount; } @@ -223,13 +272,16 @@ STATUS freeSignaling(PSignalingClient* ppSignalingClient) terminateOngoingOperations(pSignalingClient); - if (pSignalingClient->pLwsContext != NULL) { + if (pSignalingClient->pWebsocketContext != NULL) { MUTEX_LOCK(pSignalingClient->lwsServiceLock); - lws_context_destroy(pSignalingClient->pLwsContext); - pSignalingClient->pLwsContext = NULL; + lws_context_destroy((struct lws_context*) pSignalingClient->pWebsocketContext); + pSignalingClient->pWebsocketContext = NULL; MUTEX_UNLOCK(pSignalingClient->lwsServiceLock); } + // Frees up the entire structure., both HTTPS and WSS protocols (it was allocated as single block) + SAFE_MEMFREE(pSignalingClient->signalingProtocols[PROTOCOL_INDEX_HTTPS]); + freeStateMachine(pSignalingClient->pStateMachine); freeClientRetryStrategy(pSignalingClient); diff --git a/src/source/Signaling/Signaling.h b/src/source/Signaling/Signaling.h index 7f8bf52b89..fa3a0dcd61 100644 --- a/src/source/Signaling/Signaling.h +++ b/src/source/Signaling/Signaling.h @@ -337,14 +337,15 @@ typedef struct { // Restarted thread handler ThreadTracker reconnecterTracker; - // LWS context to use for Restful API - struct lws_context* pLwsContext; + // Generic websocket context - can be used by any implementation + PVOID pWebsocketContext; - // Signaling protocols - one more for the NULL terminator protocol - struct lws_protocols signalingProtocols[LWS_PROTOCOL_COUNT + 1]; + // Generic websocket protocols array - can be used by any implementation + // + 1 for the null terminator protocol + PVOID signalingProtocols[LWS_PROTOCOL_COUNT + 1]; - // Stored wsi objects - struct lws* currentWsi[LWS_PROTOCOL_COUNT]; + // Generic websocket connection objects - can be used by any implementation + PVOID currentWsi[LWS_PROTOCOL_COUNT]; // List of the ongoing messages PStackQueue pMessageQueue; diff --git a/src/source/Signaling/StateMachine.c b/src/source/Signaling/StateMachine.c index 435a25ac1d..9f543b670a 100644 --- a/src/source/Signaling/StateMachine.c +++ b/src/source/Signaling/StateMachine.c @@ -101,7 +101,7 @@ STATUS defaultSignalingStateTransitionHook(UINT64 customData /* customData shoul pSignalingStateMachineRetryStrategy, &pSignalingClient->diagnostics.stateMachineRetryCount)) != STATUS_SUCCESS) { DLOGW("Failed to get retry count. Error code: %08x", countStatus); } else { - DLOGD("Retry count: %llu", pSignalingClient->diagnostics.stateMachineRetryCount); + DLOGD("Retry count: %" PRIu32, pSignalingClient->diagnostics.stateMachineRetryCount); } } DLOGV("Signaling Client base result is [%u]. Executing KVS retry handler of retry strategy type [%u]", pSignalingClient->result, diff --git a/src/source/Signaling/StateMachine.h b/src/source/Signaling/StateMachine.h index 5dda7bcf30..e0a7ec8cbc 100644 --- a/src/source/Signaling/StateMachine.h +++ b/src/source/Signaling/StateMachine.h @@ -10,6 +10,8 @@ Signaling State Machine internal include file extern "C" { #endif +#include + /** * Signaling states definitions */ diff --git a/tst/DtlsApiTest.cpp b/tst/DtlsApiTest.cpp index 161e7608a0..e3b68d5a76 100644 --- a/tst/DtlsApiTest.cpp +++ b/tst/DtlsApiTest.cpp @@ -6,8 +6,7 @@ namespace kinesis { namespace video { namespace webrtcclient { -class DtlsApiTest : public WebRtcClientTestBase { -}; +class DtlsApiTest : public WebRtcClientTestBase {}; #ifdef KVS_USE_OPENSSL TEST_F(DtlsApiTest, createCertificateAndKey_Returns_Success) @@ -38,7 +37,6 @@ TEST_F(DtlsApiTest, dtlsSessionIsInitFinished_Null_Check) freeDtlsSession(&pClient); EXPECT_EQ(NULL, pClient); timerQueueFree(&timerQueueHandle); - } TEST_F(DtlsApiTest, dtlsSessionCreated_RefCount) @@ -78,14 +76,24 @@ TEST_F(DtlsApiTest, createCertificateAndKey_Returns_Success) EXPECT_EQ(createCertificateAndKey(GENERATED_CERTIFICATE_BITS, FALSE, &cert, &key), STATUS_SUCCESS); EXPECT_NE(cert.raw.p, nullptr); EXPECT_NE(cert.raw.len, 0); +#if MBEDTLS_BEFORE_V3 EXPECT_NE(key.pk_ctx, nullptr); EXPECT_NE(key.pk_info, nullptr); +#else + EXPECT_NE(key.MBEDTLS_PRIVATE(pk_ctx), nullptr); + EXPECT_NE(key.MBEDTLS_PRIVATE(pk_info), nullptr); +#endif EXPECT_EQ(freeCertificateAndKey(&cert, &key), STATUS_SUCCESS); EXPECT_EQ(cert.raw.p, nullptr); EXPECT_EQ(cert.raw.len, 0); +#if MBEDTLS_BEFORE_V3 EXPECT_EQ(key.pk_ctx, nullptr); EXPECT_EQ(key.pk_info, nullptr); +#else + EXPECT_EQ(key.MBEDTLS_PRIVATE(pk_ctx), nullptr); + EXPECT_EQ(key.MBEDTLS_PRIVATE(pk_info), nullptr); +#endif } #endif diff --git a/tst/IceConfigParsingTest.cpp b/tst/IceConfigParsingTest.cpp new file mode 100644 index 0000000000..716e7a7827 --- /dev/null +++ b/tst/IceConfigParsingTest.cpp @@ -0,0 +1,361 @@ + +#include "WebRTCClientTestFixture.h" + +namespace com { +namespace amazonaws { +namespace kinesis { +namespace video { +namespace webrtcclient { + +class IceConfigParsingTest : public WebRtcClientTestBase {}; + +TEST_F(IceConfigParsingTest, ParseSuccess) +{ + IceConfigInfo iceConfigs[MAX_ICE_CONFIG_COUNT]; + UINT32 iceConfigCount; + + const std::string mockResponse = R"({ + "IceServerList": [ + { + "Username": "testUser1", + "Password": "testPass1", + "Ttl": 300, + "Uris": [ + "turn:example1.com:443?transport=udp", + "turn:example1.com:443?transport=tcp" + ] + }, + { + "Username": "testUser2", + "Password": "testPass2", + "Ttl": 300, + "Uris": [ + "turn:example2.com:443?transport=udp" + ] + }, + { + "Username": "testUser3", + "Password": "testPass3", + "Ttl": 300, + "Uris": [ + "turn:example3.com:443?transport=udp" + ] + } + ] + })"; + UINT32 mockResponseLen = mockResponse.length(); + + EXPECT_EQ(STATUS_SUCCESS, + parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); + + EXPECT_EQ(3, iceConfigCount); + + // First config + EXPECT_STREQ("testUser1", iceConfigs[0].userName); + EXPECT_STREQ("testPass1", iceConfigs[0].password); + EXPECT_EQ(300 * HUNDREDS_OF_NANOS_IN_A_SECOND, iceConfigs[0].ttl); + EXPECT_EQ(2, iceConfigs[0].uriCount); + EXPECT_STREQ("turn:example1.com:443?transport=udp", iceConfigs[0].uris[0]); + EXPECT_STREQ("turn:example1.com:443?transport=tcp", iceConfigs[0].uris[1]); + + // Second config + EXPECT_STREQ("testUser2", iceConfigs[1].userName); + EXPECT_STREQ("testPass2", iceConfigs[1].password); + EXPECT_EQ(300 * HUNDREDS_OF_NANOS_IN_A_SECOND, iceConfigs[1].ttl); + EXPECT_EQ(1, iceConfigs[1].uriCount); + EXPECT_STREQ("turn:example2.com:443?transport=udp", iceConfigs[1].uris[0]); + + // Third config + EXPECT_STREQ("testUser3", iceConfigs[2].userName); + EXPECT_STREQ("testPass3", iceConfigs[2].password); + EXPECT_EQ(300 * HUNDREDS_OF_NANOS_IN_A_SECOND, iceConfigs[2].ttl); + EXPECT_EQ(1, iceConfigs[2].uriCount); + EXPECT_STREQ("turn:example3.com:443?transport=udp", iceConfigs[2].uris[0]); +} + +TEST_F(IceConfigParsingTest, InvalidArgs) +{ + IceConfigInfo iceConfigs[MAX_ICE_CONFIG_COUNT]; + UINT32 iceConfigCount; + + const std::string emptyResponse = ""; + UINT32 emptyResponseLen = emptyResponse.length(); + + const std::string mockResponse = R"({ + "IceServerList": [ + { + "Username": "testUser1", + "Password": "testPass1", + "Ttl": 300, + "Uris": [ + "turn:example1.com:443?transport=udp" + ] + } + ] + })"; + UINT32 mockResponseLen = mockResponse.length(); + + // NULL in a parameter + EXPECT_EQ(STATUS_NULL_ARG, parseIceConfigResponse(NULL, mockResponseLen, MAX_ICE_CONFIG_COUNT, iceConfigs, &iceConfigCount)); + EXPECT_EQ(STATUS_NULL_ARG, parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, MAX_ICE_CONFIG_COUNT, NULL, &iceConfigCount)); + EXPECT_EQ(STATUS_NULL_ARG, parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, MAX_ICE_CONFIG_COUNT, iceConfigs, NULL)); + + // Empty input string + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, + parseIceConfigResponse((PCHAR) emptyResponse.c_str(), emptyResponseLen, MAX_ICE_CONFIG_COUNT, iceConfigs, &iceConfigCount)); + + // 0 output parameter length + EXPECT_EQ(STATUS_INVALID_ARG, parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, 0, iceConfigs, &iceConfigCount)); +} + +TEST_F(IceConfigParsingTest, TooManyIceConfigsReturned) +{ + // Array size = 1 + IceConfigInfo iceConfigs[1]; + UINT32 iceConfigCount; + + // But there are 2 ICE configurations returned in the response + const std::string mockResponse = R"({ + "IceServerList": [ + { + "Username": "testUser1", + "Password": "testPass1", + "Ttl": 300, + "Uris": [ + "turn:example1.com:443?transport=udp", + "turn:example1.com:443?transport=tcp" + ] + }, + { + "Username": "testUser2", + "Password": "testPass2", + "Ttl": 300, + "Uris": [ + "turn:example2.com:443?transport=udp" + ] + } + ] + })"; + UINT32 mockResponseLen = mockResponse.length(); + + EXPECT_EQ(STATUS_SUCCESS, + parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); + + EXPECT_EQ(1, iceConfigCount); + EXPECT_STREQ("testUser1", iceConfigs[0].userName); + EXPECT_STREQ("testPass1", iceConfigs[0].password); + EXPECT_EQ(300 * HUNDREDS_OF_NANOS_IN_A_SECOND, iceConfigs[0].ttl); + EXPECT_EQ(2, iceConfigs[0].uriCount); + EXPECT_STREQ("turn:example1.com:443?transport=udp", iceConfigs[0].uris[0]); + EXPECT_STREQ("turn:example1.com:443?transport=tcp", iceConfigs[0].uris[1]); + +} + +TEST_F(IceConfigParsingTest, UsernameIsTooLong) +{ + IceConfigInfo iceConfigs[MAX_ICE_CONFIG_COUNT]; + UINT32 iceConfigCount; + + // Username is one character too long + const std::string longUsername(MAX_ICE_CONFIG_USER_NAME_LEN + 1, 'M'); + + const std::string mockResponse = R"({ + "IceServerList": [ + { + "Username": ")" + + longUsername + R"(", + "Password": "testPass1", + "Ttl": 300, + "Uris": [ + "turn:example2.com:443?transport=udp" + ] + } + ] + })"; + UINT32 mockResponseLen = mockResponse.length(); + + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, + parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); +} + +TEST_F(IceConfigParsingTest, PasswordIsTooLong) +{ + IceConfigInfo iceConfigs[MAX_ICE_CONFIG_COUNT]; + UINT32 iceConfigCount; + + // Password is one character too long + const std::string longPassword(MAX_ICE_CONFIG_CREDENTIAL_LEN + 1, 'V'); + + const std::string mockResponse = R"({ + "IceServerList": [ + { + "Username": "testUser1" + "Password": ")" + + longPassword + R"(", + "Ttl": 300, + "Uris": [ + "turn:example2.com:443?transport=udp" + ] + } + ] + })"; + UINT32 mockResponseLen = mockResponse.length(); + + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, + parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); +} + +TEST_F(IceConfigParsingTest, UriIsTooLong) +{ + IceConfigInfo iceConfigs[MAX_ICE_CONFIG_COUNT]; + UINT32 iceConfigCount; + + // URI is one character too long + const std::string longUri(MAX_ICE_CONFIG_URI_LEN + 1, 'N'); + + const std::string mockResponse = R"({ + "IceServerList": [ + { + "Username": "testUser1", + "Password": "testPass1", + "Ttl": 300, + "Uris": [ + ")" + + longUri + R"(" + ] + } + ] + })"; + UINT32 mockResponseLen = mockResponse.length(); + + EXPECT_EQ(STATUS_SIGNALING_MAX_ICE_URI_LEN, + parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); +} + +TEST_F(IceConfigParsingTest, TooManyUrisReturned) +{ + IceConfigInfo iceConfigs[MAX_ICE_CONFIG_COUNT]; + UINT32 iceConfigCount; + + // Create exactly MAX_ICE_CONFIG_URI_COUNT + 1 URIs to test the boundary + std::string uris; + for (UINT32 i = 0; i <= MAX_ICE_CONFIG_URI_COUNT; i++) { + if (i > 0) { + uris += ",\n "; + } + uris += "\"turn:example" + std::to_string(i) + ".com:443?transport=udp\""; + } + + const std::string mockResponse = R"({ + "IceServerList": [ + { + "Username": "testUser1", + "Password": "testPass1", + "Ttl": 300, + "Uris": [ + )" + + uris + R"( + ] + } + ] + })"; + + // Verify the error is returned + EXPECT_EQ(STATUS_SIGNALING_MAX_ICE_URI_COUNT, + parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponse.length(), ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); + + // Parsing failure, expecting nothing returned + EXPECT_EQ(0, iceConfigCount); +} + +TEST_F(IceConfigParsingTest, MalformedJson) +{ + IceConfigInfo iceConfigs[MAX_ICE_CONFIG_COUNT]; + UINT32 iceConfigCount = 0; + + // Missing closing brace + const std::string malformedJson1 = R"({ + "IceServerList": [ + { + "Username": "testUser1", + "Password": "testPass1", + "Ttl": 300, + "Uris": [ + "turn:example1.com:443?transport=udp" + ] + })"; + + // Invalid JSON structure + const std::string malformedJson2 = R"({ + "IceServerList": [ + { + "Username": "testUser1", + "Password": "testPass1", + "Ttl": "not_a_number", + "Uris": "not_an_array" + } + ] + })"; + + // Invalid array structure + const std::string malformedJson3 = R"({ + "IceServerList": { + "not": "an array" + } + })"; + + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, + parseIceConfigResponse((PCHAR) malformedJson1.c_str(), malformedJson1.length(), ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); + + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, + parseIceConfigResponse((PCHAR) malformedJson2.c_str(), malformedJson2.length(), ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); + + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, + parseIceConfigResponse((PCHAR) malformedJson3.c_str(), malformedJson3.length(), ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); + + // Parsing failure, expecting nothing returned + EXPECT_EQ(0, iceConfigCount); +} + +TEST_F(IceConfigParsingTest, IgnoreExtraFieldsInTheJson) +{ + IceConfigInfo iceConfigs[MAX_ICE_CONFIG_COUNT]; + UINT32 iceConfigCount; + + const std::string mockResponse = R"({ + "IceServerList": [ + { + "Username": "testUser1", + "Password": "testPass1", + "Ttl": 250, + "Extra": "field", + "Uris": [ + "turn:example1.com:443?transport=udp", + "turn:example1.com:443?transport=tcp" + ], + "SecondExtra": [ + "field" + ] + } + ] + })"; + UINT32 mockResponseLen = mockResponse.length(); + + EXPECT_EQ(STATUS_SUCCESS, + parseIceConfigResponse((PCHAR) mockResponse.c_str(), mockResponseLen, ARRAY_SIZE(iceConfigs), iceConfigs, &iceConfigCount)); + + EXPECT_EQ(1, iceConfigCount); + + EXPECT_STREQ("testUser1", iceConfigs[0].userName); + EXPECT_STREQ("testPass1", iceConfigs[0].password); + EXPECT_EQ(250 * HUNDREDS_OF_NANOS_IN_A_SECOND, iceConfigs[0].ttl); + EXPECT_EQ(2, iceConfigs[0].uriCount); + EXPECT_STREQ("turn:example1.com:443?transport=udp", iceConfigs[0].uris[0]); + EXPECT_STREQ("turn:example1.com:443?transport=tcp", iceConfigs[0].uris[1]); +} + +} // namespace webrtcclient +} // namespace video +} // namespace kinesis +} // namespace amazonaws +} // namespace com diff --git a/tst/NetworkApiTest.cpp b/tst/NetworkApiTest.cpp index c5702a61c6..54dc6ad9db 100644 --- a/tst/NetworkApiTest.cpp +++ b/tst/NetworkApiTest.cpp @@ -30,6 +30,89 @@ TEST_F(NetworkApiTest, ipIpAddrTest) EXPECT_EQ(FALSE, isIpAddr((PCHAR) "2001:85a3:0000:0000:8a2e:0370:7334", STRLEN("2001:85a3:0000:0000:8a2e:0370:7334"))); } +// ------------------------------- getIpAddrStr ---------------------- + +STATUS initTestKvsIpv4Address(PKvsIpAddress pKvsIpAddress) +{ + STATUS retStatus = STATUS_SUCCESS; + UINT8 addr[] = {192, 168, 1, 1}; + + CHK(pKvsIpAddress != NULL, STATUS_NULL_ARG); + + MEMSET(pKvsIpAddress, 0, SIZEOF(KvsIpAddress)); + pKvsIpAddress->family = KVS_IP_FAMILY_TYPE_IPV4; + + MEMCPY(pKvsIpAddress->address, addr, IPV4_ADDRESS_LENGTH); + +CleanUp: + return retStatus; +} + +TEST_F(NetworkApiTest, GetIpAddrStrNullIpAddress) +{ + CHAR buffer[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; + + EXPECT_EQ(STATUS_NULL_ARG, getIpAddrStr(NULL, buffer, SIZEOF(buffer))); +} + +TEST_F(NetworkApiTest, GetIpAddrStrInvalidBuffer) +{ + CHAR buffer[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; + + KvsIpAddress ipAddress; + EXPECT_EQ(STATUS_SUCCESS, initTestKvsIpv4Address(&ipAddress)); + + EXPECT_EQ(STATUS_INVALID_ARG, getIpAddrStr(&ipAddress, NULL, SIZEOF(buffer))); + EXPECT_EQ(STATUS_INVALID_ARG, getIpAddrStr(&ipAddress, buffer, 0)); +} + +TEST_F(NetworkApiTest, GetIpAddrStrBufferTooSmall) +{ + KvsIpAddress ipAddress; + EXPECT_EQ(STATUS_SUCCESS, initTestKvsIpv4Address(&ipAddress)); + + // Test with increasingly small buffers + CHAR tinyBuffer[1]; + EXPECT_EQ(STATUS_BUFFER_TOO_SMALL, getIpAddrStr(&ipAddress, tinyBuffer, SIZEOF(tinyBuffer))); + + CHAR smallBuffer[5]; + EXPECT_EQ(STATUS_BUFFER_TOO_SMALL, getIpAddrStr(&ipAddress, smallBuffer, SIZEOF(smallBuffer))); +} + +TEST_F(NetworkApiTest, GetIpAddrStrIpv4Addr) +{ + CHAR buffer[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; + + KvsIpAddress ipAddress; + EXPECT_EQ(STATUS_SUCCESS, initTestKvsIpv4Address(&ipAddress)); + + EXPECT_EQ(STATUS_SUCCESS, getIpAddrStr(&ipAddress, buffer, SIZEOF(buffer))); + EXPECT_STREQ("192.168.1.1", buffer); +} + +TEST_F(NetworkApiTest, GetIpAddrStrIpv6Addr) +{ + CHAR buffer[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; + + KvsIpAddress ipAddress; + MEMSET(&ipAddress, 0, SIZEOF(KvsIpAddress)); + ipAddress.family = KVS_IP_FAMILY_TYPE_IPV6; + + // rfc3849 - 2001:db8::/32 as a documentation-only prefix in the IPv6 + // address registry. No end party is to be assigned this address. + UINT8 addr[] = {0x20, 0x01, + 0x0d, 0xb8, + 0x12, 0x34, + 0x56, 0x78, + 0x9a, 0xbc, + 0xde, 0xf0, + 0x12, 0x34, + 0x56, 0x78}; + MEMCPY(ipAddress.address, addr, IPV6_ADDRESS_LENGTH); + + EXPECT_EQ(STATUS_SUCCESS, getIpAddrStr(&ipAddress, buffer, SIZEOF(buffer))); + EXPECT_STREQ("2001:0db8:1234:5678:9abc:def0:1234:5678", buffer); +} } // namespace webrtcclient } // namespace video diff --git a/tst/SdpApiTest.cpp b/tst/SdpApiTest.cpp index 774511bf9f..66e2765a79 100644 --- a/tst/SdpApiTest.cpp +++ b/tst/SdpApiTest.cpp @@ -2689,6 +2689,81 @@ INSTANTIATE_TEST_SUITE_P(SdpApiTest_SdpMatch_Chromium, SdpApiTest_SdpMatch, ::te INSTANTIATE_TEST_SUITE_P(SdpApiTest_SdpMatch_Safari, SdpApiTest_SdpMatch, ::testing::Values(offer_1v1a1d_Safari_Mac)); +// Test case-insensitive codec matching for OPUS +TEST_F(SdpApiTest, caseInsensitiveCodecMatching_OPUS) +{ + RtcConfiguration configuration; + PRtcPeerConnection pRtcPeerConnection = nullptr; + PRtcRtpTransceiver transceiver = nullptr; + RtcMediaStreamTrack track; + RtcSessionDescriptionInit offerSdp; + + MEMSET(&configuration, 0x00, SIZEOF(RtcConfiguration)); + MEMSET(&offerSdp, 0x00, SIZEOF(RtcSessionDescriptionInit)); + + // SDP offer with capitalized OPUS (instead of lowercase opus) + auto sdpOffer = R"(v=0 +o=- 3236679289542986524 0 IN IP4 0.0.0.0 +s=- +t=0 0 +a=ice-options:trickle +a=group:BUNDLE audio0 +m=audio 9 UDP/TLS/RTP/SAVPF 111 +c=IN IP4 0.0.0.0 +a=setup:actpass +a=ice-ufrag:raliw7w40wB2zDu880kYOHG3m0ftNyys +a=ice-pwd:KWXR7ud6QBxjXQfW4QhKNO2HVPez9DPP +a=rtcp-mux +a=rtcp-rsize +a=sendrecv +a=rtpmap:111 OPUS/48000/2 +a=rtcp-fb:111 transport-cc +a=ssrc:47552272 msid:user1379891550@host-400f95df webrtctransceiver2 +a=ssrc:47552272 cname:user1379891550@host-400f95df +a=mid:audio0 +a=fingerprint:sha-256 E8:35:78:98:04:A5:22:B7:47:65:5F:E1:A0:BD:C6:1B:2B:4E:14:41:E1:C8:1A:99:46:3C:FD:06:6F:94:A2:0C +a=rtcp-mux-only +)"; + + configuration.iceTransportPolicy = ICE_TRANSPORT_POLICY_ALL; + configuration.kvsRtcConfiguration.generatedCertificateBits = GENERATED_CERTIFICATE_BITS; + SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION, TEST_DEFAULT_STUN_URL_POSTFIX); + + track.kind = MEDIA_STREAM_TRACK_KIND_AUDIO; + track.codec = RTC_CODEC_OPUS; + STRNCPY(track.streamId, "audioStream1", MAX_MEDIA_STREAM_ID_LEN); + STRNCPY(track.trackId, "audioTrack1", MAX_MEDIA_STREAM_TRACK_ID_LEN); + + offerSdp.type = SDP_TYPE_OFFER; + STRNCPY(offerSdp.sdp, sdpOffer, MAX_SESSION_DESCRIPTION_INIT_SDP_LEN); + + EXPECT_EQ(STATUS_SUCCESS, createPeerConnection(&configuration, &pRtcPeerConnection)); + EXPECT_EQ(STATUS_SUCCESS, addSupportedCodec(pRtcPeerConnection, RTC_CODEC_OPUS)); + EXPECT_EQ(STATUS_SUCCESS, addTransceiver(pRtcPeerConnection, &track, nullptr, &transceiver)); + + RtcSessionDescriptionInit answerSdp; + MEMSET(&answerSdp, 0x00, SIZEOF(RtcSessionDescriptionInit)); + + EXPECT_EQ(STATUS_SUCCESS, setRemoteDescription(pRtcPeerConnection, &offerSdp)); + EXPECT_EQ(STATUS_SUCCESS, createAnswer(pRtcPeerConnection, &answerSdp)); + + std::string answer(answerSdp.sdp); + + // Verify that the answer contains sendrecv (not inactive) for audio + EXPECT_NE(std::string::npos, answer.find("a=sendrecv")); + EXPECT_EQ(std::string::npos, answer.find("a=inactive")); + + // Verify that the answer contains the correct OPUS rtpmap with payload type 111 + EXPECT_NE(std::string::npos, answer.find("a=rtpmap:111 opus/48000/2")); + + // Verify that it doesn't contain fake stream/track + EXPECT_EQ(std::string::npos, answer.find("fakeStream")); + EXPECT_EQ(std::string::npos, answer.find("fakeTrack")); + + EXPECT_EQ(STATUS_SUCCESS, closePeerConnection(pRtcPeerConnection)); + EXPECT_EQ(STATUS_SUCCESS, freePeerConnection(&pRtcPeerConnection)); +} + } // namespace webrtcclient } // namespace video } // namespace kinesis diff --git a/tst/SignalingApiFunctionalityTest.cpp b/tst/SignalingApiFunctionalityTest.cpp index 8fab9f668d..d70e01847c 100644 --- a/tst/SignalingApiFunctionalityTest.cpp +++ b/tst/SignalingApiFunctionalityTest.cpp @@ -3500,788 +3500,267 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingUpdateFullMultiChannelCache) FREMOVE(DEFAULT_CACHE_FILE_PATH); } +// ----------------- parseSignalingMessage ----------------- -TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer) -{ - ASSERT_EQ(TRUE, mAccessKeyIdSet); - - ChannelInfo channelInfo; - SignalingClientCallbacks signalingClientCallbacks; - SignalingClientInfoInternal clientInfoInternal; - PSignalingClient pSignalingClient; - SIGNALING_CLIENT_HANDLE signalingHandle; - UINT32 i, iceCount; - PIceConfigInfo pIceConfigInfo; - - signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; - signalingClientCallbacks.customData = (UINT64) this; - signalingClientCallbacks.messageReceivedFn = NULL; - signalingClientCallbacks.errorReportFn = signalingClientError; - signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; - signalingClientCallbacks.getCurrentTimeFn = NULL; - - MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_ParseOfferSuccess) { + ReceivedSignalingMessage receivedMessage; - clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; - clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; - STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); - setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); + // "Hello" -> "SGVsbG8=" (base64) + const std::string jsonMessage = R"({ + "messageType": "SDP_OFFER", + "senderClientId": "ClientA", + "messagePayload": "SGVsbG8=" + })"; - MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); - channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; - channelInfo.pChannelName = mChannelName; - channelInfo.pKmsKeyId = NULL; - channelInfo.tagCount = 0; - channelInfo.pTags = NULL; - channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; - channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; - channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; - channelInfo.retry = TRUE; - channelInfo.reconnect = TRUE; - channelInfo.pCertPath = mCaCertPath; - channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; - - EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); - signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); - EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); - - // Fetch credentials, describe, get endpoint, get ice config - EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); - - // Connect to the channel - EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); - - pActiveClient = pSignalingClient; - - // Connect to the signaling client - EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); - - // Should have an exiting ICE configuration - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); - EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); - - - // Ensure the ICE is not refreshed as we already have a current non-expired set - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - EXPECT_NE(0, iceCount); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - - // Trigger the ICE refresh immediately on any of the ICE accessor calls - pSignalingClient->iceConfigCount = 0; - - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - EXPECT_NE(0, iceCount); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - - // Set to invalid again and trigger an update via offer message - pSignalingClient->iceConfigCount = 0; - - // Inject a reconnect ice server message - CHAR message[] = "{\n" - " \"messageType\": \"SDP_OFFER\",\n" - " \"senderClientId\": \"ProducerMaster\",\n" - " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" - " \"IceServerList\": [{\n" - " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " },\n" - " {\n" - " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " }\n" - " ],\n" - " \"statusResponse\": {\n" - " \"correlationId\": \"CorrelationID\",\n" - " \"errorType\": \"Unknown message\",\n" - " \"statusCode\": \"200\",\n" - " \"description\": \"Test attempt to send an unknown message\"\n" - " }\n" - "}"; - - EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message, ARRAY_SIZE(message))); - - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - - // ICE should not have been called again as we updated it via a message - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - - // Validate the retrieved info - EXPECT_EQ(2, iceCount); - - for (i = 0; i < iceCount; i++) { - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, i, &pIceConfigInfo)); - EXPECT_NE(0, pIceConfigInfo->uriCount); - EXPECT_EQ(298 * HUNDREDS_OF_NANOS_IN_A_SECOND, pIceConfigInfo->ttl); - EXPECT_EQ(SIGNALING_ICE_CONFIG_INFO_CURRENT_VERSION, pIceConfigInfo->version); - EXPECT_EQ(0, STRCMP("1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==", pIceConfigInfo->userName)); - } - - // - // Set to invalid again to trigger an update. - // The message will not update as the type is not an offer - // - pSignalingClient->iceConfigCount = 0; - - // Inject a reconnect ice server message - CHAR message2[] = "{\n" - " \"messageType\": \"SDP_ANSWER\",\n" - " \"senderClientId\": \"ProducerMaster\",\n" - " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" - " \"IceServerList\": [{\n" - " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " },\n" - " {\n" - " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " }\n" - " ],\n" - " \"statusResponse\": {\n" - " \"correlationId\": \"CorrelationID\",\n" - " \"errorType\": \"Unknown message\",\n" - " \"statusCode\": \"200\",\n" - " \"description\": \"Test attempt to send an unknown message\"\n" - " }\n" - "}"; - - EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message2, ARRAY_SIZE(message2))); - - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - - // ICE should have been called again as we couldn't have updated via the message - EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - // Check that we are connected and can send a message - SignalingMessage signalingMessage; - signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; - signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; - STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); - MEMSET(signalingMessage.payload, 'A', 100); - signalingMessage.payload[100] = '\0'; - signalingMessage.payloadLen = 0; - signalingMessage.correlationId[0] = '\0'; - - EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); - - deleteChannelLws(FROM_SIGNALING_CLIENT_HANDLE(signalingHandle), 0); - - EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS, status); + EXPECT_STREQ("ClientA", receivedMessage.signalingMessage.peerClientId); + EXPECT_STREQ("Hello", receivedMessage.signalingMessage.payload); + EXPECT_EQ(5, receivedMessage.signalingMessage.payloadLen); + EXPECT_EQ(SIGNALING_MESSAGE_TYPE_OFFER, receivedMessage.signalingMessage.messageType); } -TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer_SlowClockSkew) -{ - ASSERT_EQ(TRUE, mAccessKeyIdSet); - - ChannelInfo channelInfo; - SignalingClientCallbacks signalingClientCallbacks; - SignalingClientInfoInternal clientInfoInternal; - PSignalingClient pSignalingClient; - SIGNALING_CLIENT_HANDLE signalingHandle; - UINT32 i, iceCount; - PIceConfigInfo pIceConfigInfo; - - signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; - signalingClientCallbacks.customData = (UINT64) this; - signalingClientCallbacks.messageReceivedFn = NULL; - signalingClientCallbacks.errorReportFn = signalingClientError; - signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; - signalingClientCallbacks.getCurrentTimeFn = getCurrentTimeSlowClock; - - MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); - - clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; - clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; - STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); - - MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); - channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; - channelInfo.pChannelName = mChannelName; - channelInfo.pKmsKeyId = NULL; - channelInfo.tagCount = 0; - channelInfo.pTags = NULL; - channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; - channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; - channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; - channelInfo.retry = TRUE; - channelInfo.reconnect = TRUE; - channelInfo.pCertPath = mCaCertPath; - channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; - - EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); - signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); - EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); - EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); - - // Connect to the channel - EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); - - pActiveClient = pSignalingClient; - - // Connect to the signaling client - EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); - - // Should have an exiting ICE configuration - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); - // Describe is 3 because the first one requests in expired signature, second - // one results in 404 (not found because it doesn't exist) - // Then we call create which also fails once due to expired signature then succeeds - // the second time, then we call describe a 3rd time which succeeds - // At this point we have both fixed the clock skew offset in the code - // as well as created the channel we are calling describe on. - EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); - EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); - - - // Ensure the ICE is not refreshed as we already have a current non-expired set - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - EXPECT_NE(0, iceCount); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - - // Trigger the ICE refresh immediately on any of the ICE accessor calls - pSignalingClient->iceConfigCount = 0; - - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - EXPECT_NE(0, iceCount); - EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - - // Set to invalid again and trigger an update via offer message - pSignalingClient->iceConfigCount = 0; - - // Inject a reconnect ice server message - CHAR message[] = "{\n" - " \"messageType\": \"SDP_OFFER\",\n" - " \"senderClientId\": \"ProducerMaster\",\n" - " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" - " \"IceServerList\": [{\n" - " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " },\n" - " {\n" - " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " }\n" - " ],\n" - " \"statusResponse\": {\n" - " \"correlationId\": \"CorrelationID\",\n" - " \"errorType\": \"Unknown message\",\n" - " \"statusCode\": \"200\",\n" - " \"description\": \"Test attempt to send an unknown message\"\n" - " }\n" - "}"; - - EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message, ARRAY_SIZE(message))); - - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - - // ICE should not have been called again as we updated it via a message - EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - - // Validate the retrieved info - EXPECT_EQ(2, iceCount); - - for (i = 0; i < iceCount; i++) { - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, i, &pIceConfigInfo)); - EXPECT_NE(0, pIceConfigInfo->uriCount); - EXPECT_EQ(298 * HUNDREDS_OF_NANOS_IN_A_SECOND, pIceConfigInfo->ttl); - EXPECT_EQ(SIGNALING_ICE_CONFIG_INFO_CURRENT_VERSION, pIceConfigInfo->version); - EXPECT_EQ(0, STRCMP("1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==", pIceConfigInfo->userName)); - } - - // - // Set to invalid again to trigger an update. - // The message will not update as the type is not an offer - // - pSignalingClient->iceConfigCount = 0; - - // Inject a reconnect ice server message - CHAR message2[] = "{\n" - " \"messageType\": \"SDP_ANSWER\",\n" - " \"senderClientId\": \"ProducerMaster\",\n" - " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" - " \"IceServerList\": [{\n" - " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " },\n" - " {\n" - " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " }\n" - " ],\n" - " \"statusResponse\": {\n" - " \"correlationId\": \"CorrelationID\",\n" - " \"errorType\": \"Unknown message\",\n" - " \"statusCode\": \"200\",\n" - " \"description\": \"Test attempt to send an unknown message\"\n" - " }\n" - "}"; - - EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message2, ARRAY_SIZE(message2))); - - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_ParseStatusResponseSuccess) { + ReceivedSignalingMessage receivedMessage; - // ICE should have been called again as we couldn't have updated via the message - EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - - // Check that we are connected and can send a message - SignalingMessage signalingMessage; - signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; - signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; - STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); - MEMSET(signalingMessage.payload, 'A', 100); - signalingMessage.payload[100] = '\0'; - signalingMessage.payloadLen = 0; - signalingMessage.correlationId[0] = '\0'; + const std::string jsonMessage = R"({ + "messageType": "STATUS_RESPONSE", + "statusResponse": { + "correlationId": "1700186220273", + "errorType": "InvalidArgumentException", + "statusCode": "400", + "success": false + } + })"; - EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - deleteChannelLws(FROM_SIGNALING_CLIENT_HANDLE(signalingHandle), 0); + EXPECT_EQ(STATUS_SUCCESS, status); + EXPECT_EQ(SIGNALING_MESSAGE_TYPE_STATUS_RESPONSE, receivedMessage.signalingMessage.messageType); + EXPECT_EQ(400, receivedMessage.statusCode); + EXPECT_STREQ("InvalidArgumentException", receivedMessage.errorType); - EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); + // Check not-present fields are correctly zeroed + EXPECT_STREQ("", receivedMessage.description); + EXPECT_STREQ("", receivedMessage.signalingMessage.peerClientId); + EXPECT_STREQ("", receivedMessage.signalingMessage.payload); + EXPECT_EQ(0, receivedMessage.signalingMessage.payloadLen); } +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_InvalidArgs) { + ReceivedSignalingMessage receivedMessage; -TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer_FastClockSkew) -{ - ASSERT_EQ(TRUE, mAccessKeyIdSet); + // "Hello" -> "SGVsbG8=" (base64) + const std::string jsonMessage = R"({ + "messageType": "SDP_OFFER", + "senderClientId": "ClientA", + "messagePayload": "SGVsbG8=" + })"; - ChannelInfo channelInfo; - SignalingClientCallbacks signalingClientCallbacks; - SignalingClientInfoInternal clientInfoInternal; - PSignalingClient pSignalingClient; - SIGNALING_CLIENT_HANDLE signalingHandle; - UINT32 i, iceCount; - PIceConfigInfo pIceConfigInfo; + // Validate that the jsonMessage is OK + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); + EXPECT_EQ(STATUS_SUCCESS, status); - signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; - signalingClientCallbacks.customData = (UINT64) this; - signalingClientCallbacks.messageReceivedFn = NULL; - signalingClientCallbacks.errorReportFn = signalingClientError; - signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; - signalingClientCallbacks.getCurrentTimeFn = getCurrentTimeFastClock; + // NULL parameter + EXPECT_EQ(STATUS_NULL_ARG, parseSignalingMessage(NULL, jsonMessage.length(), &receivedMessage)); + EXPECT_EQ(STATUS_NULL_ARG, parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), NULL)); +} - MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_messageTooLong) { + ReceivedSignalingMessage receivedMessage; - clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; - clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; - STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + const std::string jsonMessage(MAX_SIGNALING_MESSAGE_LEN + 1, 'Q'); - MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); - channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; - channelInfo.pChannelName = mChannelName; - channelInfo.pKmsKeyId = NULL; - channelInfo.tagCount = 0; - channelInfo.pTags = NULL; - channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; - channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; - channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; - channelInfo.retry = TRUE; - channelInfo.reconnect = TRUE; - channelInfo.pCertPath = mCaCertPath; - channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; - - EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); - signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); - EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); - EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); - - // Connect to the channel - EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - pActiveClient = pSignalingClient; + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); +} - // Connect to the signaling client - EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_senderClientIdIsTooLong) { + ReceivedSignalingMessage receivedMessage; - // Should have an exiting ICE configuration - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); - // Describe is 3 because the first one requests in expired signature, second - // one results in 404 (not found because it doesn't exist) - // Then we call create which also fails once due to expired signature then succeeds - // the second time, then we call describe a 3rd time which succeeds - // At this point we have both fixed the clock skew offset in the code - // as well as created the channel we are calling describe on. - EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); - EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + const std::string longClientId(MAX_SIGNALING_CLIENT_ID_LEN + 1, 'K'); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - EXPECT_NE(0, iceCount); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - - // Trigger the ICE refresh immediately on any of the ICE accessor calls - pSignalingClient->iceConfigCount = 0; - - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - EXPECT_NE(0, iceCount); - EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + const std::string jsonMessage = R"({ + "messageType": "SDP_OFFER", + "senderClientId": ")" + + longClientId + R"(", + "messagePayload": "SGVsbG8=" + })"; - // Set to invalid again and trigger an update via offer message - pSignalingClient->iceConfigCount = 0; + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - // Inject a reconnect ice server message - CHAR message[] = "{\n" - " \"messageType\": \"SDP_OFFER\",\n" - " \"senderClientId\": \"ProducerMaster\",\n" - " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" - " \"IceServerList\": [{\n" - " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " },\n" - " {\n" - " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " }\n" - " ],\n" - " \"statusResponse\": {\n" - " \"correlationId\": \"CorrelationID\",\n" - " \"errorType\": \"Unknown message\",\n" - " \"statusCode\": \"200\",\n" - " \"description\": \"Test attempt to send an unknown message\"\n" - " }\n" - "}"; + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); +} - EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message, ARRAY_SIZE(message))); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_messageTypeIsTooLong) { + ReceivedSignalingMessage receivedMessage; - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + const std::string longMessageType(MAX_SIGNALING_MESSAGE_TYPE_LEN + 1, 'J'); - // ICE should not have been called again as we updated it via a message - EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + const std::string jsonMessage = R"({ + "messageType": ")" + + longMessageType + R"(", + "senderClientId": "Client1", + "messagePayload": "SGVsbG8=" + })"; - // Validate the retrieved info - EXPECT_EQ(2, iceCount); + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - for (i = 0; i < iceCount; i++) { - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, i, &pIceConfigInfo)); - EXPECT_NE(0, pIceConfigInfo->uriCount); - EXPECT_EQ(298 * HUNDREDS_OF_NANOS_IN_A_SECOND, pIceConfigInfo->ttl); - EXPECT_EQ(SIGNALING_ICE_CONFIG_INFO_CURRENT_VERSION, pIceConfigInfo->version); - EXPECT_EQ(0, STRCMP("1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==", pIceConfigInfo->userName)); - } + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); +} - // - // Set to invalid again to trigger an update. - // The message will not update as the type is not an offer - // - pSignalingClient->iceConfigCount = 0; +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_messagePayloadIsTooLong) { + ReceivedSignalingMessage receivedMessage; - // Inject a reconnect ice server message - CHAR message2[] = "{\n" - " \"messageType\": \"SDP_ANSWER\",\n" - " \"senderClientId\": \"ProducerMaster\",\n" - " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" - " \"IceServerList\": [{\n" - " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " },\n" - " {\n" - " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " }\n" - " ],\n" - " \"statusResponse\": {\n" - " \"correlationId\": \"CorrelationID\",\n" - " \"errorType\": \"Unknown message\",\n" - " \"statusCode\": \"200\",\n" - " \"description\": \"Test attempt to send an unknown message\"\n" - " }\n" - "}"; - - EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message2, ARRAY_SIZE(message2))); + const std::string longMessagePayload(MAX_SIGNALING_MESSAGE_LEN + 1, 'K'); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + const std::string jsonMessage = R"({ + "messageType": "SDP_OFFER", + "senderClientId": Client1", + "messagePayload": ")" + + longMessagePayload + R"(" + })"; - // ICE should have been called again as we couldn't have updated via the message - EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - // Check that we are connected and can send a message - SignalingMessage signalingMessage; - signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; - signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; - STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); - MEMSET(signalingMessage.payload, 'A', 100); - signalingMessage.payload[100] = '\0'; - signalingMessage.payloadLen = 0; - signalingMessage.correlationId[0] = '\0'; - - EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); - - deleteChannelLws(FROM_SIGNALING_CLIENT_HANDLE(signalingHandle), 0); - - EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); } -TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer_FastClockSkew_VerifyOffsetRemovedWhenClockFixed) -{ - if (!mAccessKeyIdSet) { - return; - } +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_correlationIdIsTooLong) { + ReceivedSignalingMessage receivedMessage; - ChannelInfo channelInfo; - SignalingClientCallbacks signalingClientCallbacks; - SignalingClientInfoInternal clientInfoInternal; - PSignalingClient pSignalingClient; - SIGNALING_CLIENT_HANDLE signalingHandle; - UINT32 i, iceCount; - PIceConfigInfo pIceConfigInfo; - STATUS retStatus = STATUS_SUCCESS; - - signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; - signalingClientCallbacks.customData = (UINT64) this; - signalingClientCallbacks.messageReceivedFn = NULL; - signalingClientCallbacks.errorReportFn = signalingClientError; - signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; - signalingClientCallbacks.getCurrentTimeFn = getCurrentTimeFastClock; + const std::string longCorrelationId(MAX_CORRELATION_ID_LEN + 1, 'L'); - MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); - - clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; - clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; - STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + const std::string jsonMessage = R"({ + "messageType": "STATUS_RESPONSE", + "statusResponse": { + "correlationId": ")" + + longCorrelationId + R"(", + "errorType": "InvalidArgumentException", + "statusCode": "400", + "success": false + } + })"; - MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); - channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; - channelInfo.pChannelName = mChannelName; - channelInfo.pKmsKeyId = NULL; - channelInfo.tagCount = 0; - channelInfo.pTags = NULL; - channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; - channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; - channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; - channelInfo.retry = TRUE; - channelInfo.reconnect = TRUE; - channelInfo.pCertPath = mCaCertPath; - channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); - signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); - EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); - EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); +} - // Connect to the channel - EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_errorTypeIsTooLong) { + ReceivedSignalingMessage receivedMessage; - pActiveClient = pSignalingClient; + const std::string longErrorType(MAX_ERROR_TYPE_STRING_LEN + 1, 'M'); - // Connect to the signaling client - EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + const std::string jsonMessage = R"({ + "messageType": "STATUS_RESPONSE", + "statusResponse": { + "correlationId": "1700186220273", + "errorType": ")" + + longErrorType + R"(", + "statusCode": "400", + "success": false + } + })"; - // Should have an exiting ICE configuration - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); - // Describe is 3 because the first one requests in expired signature, second - // one results in 404 (not found because it doesn't exist) - // Then we call create which also fails once due to expired signature then succeeds - // the second time, then we call describe a 3rd time which succeeds - // At this point we have both fixed the clock skew offset in the code - // as well as created the channel we are calling describe on. - EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); - EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - // allow for timeouts, as the forced 403 from clock skew can result in a 1 time timeout. - // however it must succeed after that 1 failure. - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - EXPECT_NE(0, iceCount); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); +} - // reset the current time callback to "fix" the clock - MUTEX_LOCK(pSignalingClient->diagnosticsLock); - pSignalingClient->signalingClientCallbacks.getCurrentTimeFn = NULL; - MUTEX_UNLOCK(pSignalingClient->diagnosticsLock); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_statusCodeIsTooLong) { + ReceivedSignalingMessage receivedMessage; - // Trigger the ICE refresh immediately on any of the ICE accessor calls - pSignalingClient->iceConfigCount = 0; + const std::string longStatusCode(MAX_STATUS_CODE_STRING_LEN + 1, '1'); - for(int i = 0; i < 2; i++) - { - retStatus = signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount); - if(retStatus == STATUS_SUCCESS) - { - break; + const std::string jsonMessage = R"({ + "messageType": "STATUS_RESPONSE", + "statusResponse": { + "correlationId": "1700186220273", + "errorType": "InvalidArgumentException", + "statusCode": ")" + + longStatusCode + R"(", + "success": false } - } - EXPECT_EQ(STATUS_SUCCESS, retStatus); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); - EXPECT_NE(0, iceCount); - // Called an extra time because first time will fail with 403 - // Due to application of clock skew offset but after failure - // We will remove the offset correction from the map and retry - EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + })"; - // Set to invalid again and trigger an update via offer message - pSignalingClient->iceConfigCount = 0; + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - // Inject a reconnect ice server message - CHAR message[] = "{\n" - " \"messageType\": \"SDP_OFFER\",\n" - " \"senderClientId\": \"ProducerMaster\",\n" - " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" - " \"IceServerList\": [{\n" - " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " },\n" - " {\n" - " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " }\n" - " ],\n" - " \"statusResponse\": {\n" - " \"correlationId\": \"CorrelationID\",\n" - " \"errorType\": \"Unknown message\",\n" - " \"statusCode\": \"200\",\n" - " \"description\": \"Test attempt to send an unknown message\"\n" - " }\n" - "}"; + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); +} - EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message, ARRAY_SIZE(message))); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_descriptionIsTooLong) { + ReceivedSignalingMessage receivedMessage; - for(int i = 0; i < 2; i++) - { - retStatus = signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount); - if(retStatus == STATUS_SUCCESS) - { - break; - } - } - EXPECT_EQ(STATUS_SUCCESS, retStatus); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + const std::string longDescription(MAX_MESSAGE_DESCRIPTION_LEN + 1, 'Z'); - // ICE should not have been called again as we updated it via a message - EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + const std::string jsonMessage = R"({ + "messageType": "STATUS_RESPONSE", + "statusResponse": { + "correlationId": "1700186220273", + "errorType": "InvalidArgumentException", + "statusCode": "400", + "success": false, + "description": ")" + longDescription + R"(" + } + })"; - // Validate the retrieved info - EXPECT_EQ(2, iceCount); + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - for (i = 0; i < iceCount; i++) { - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, i, &pIceConfigInfo)); - EXPECT_NE(0, pIceConfigInfo->uriCount); - EXPECT_EQ(298 * HUNDREDS_OF_NANOS_IN_A_SECOND, pIceConfigInfo->ttl); - EXPECT_EQ(SIGNALING_ICE_CONFIG_INFO_CURRENT_VERSION, pIceConfigInfo->version); - EXPECT_EQ(0, STRCMP("1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==", pIceConfigInfo->userName)); - } + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); +} - // - // Set to invalid again to trigger an update. - // The message will not update as the type is not an offer - // - pSignalingClient->iceConfigCount = 0; - // Inject a reconnect ice server message - CHAR message2[] = "{\n" - " \"messageType\": \"SDP_ANSWER\",\n" - " \"senderClientId\": \"ProducerMaster\",\n" - " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" - " \"IceServerList\": [{\n" - " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " },\n" - " {\n" - " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" - " \"Ttl\": 298,\n" - " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" - " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" - " }\n" - " ],\n" - " \"statusResponse\": {\n" - " \"correlationId\": \"CorrelationID\",\n" - " \"errorType\": \"Unknown message\",\n" - " \"statusCode\": \"200\",\n" - " \"description\": \"Test attempt to send an unknown message\"\n" - " }\n" - "}"; - - EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message2, ARRAY_SIZE(message2))); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_emptyJson) { + ReceivedSignalingMessage receivedMessage; + std::string emptyJsonMessage = "{}"; - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); - EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + STATUS status = parseSignalingMessage((PCHAR) emptyJsonMessage.c_str(), emptyJsonMessage.length(), &receivedMessage); + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); +} - // ICE should have been called again as we couldn't have updated via the message - EXPECT_EQ(5, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_ignoreExtraFields) { + ReceivedSignalingMessage receivedMessage; + + const std::string jsonMessage = R"({ + "messageType": "SDP_OFFER", + "senderClientId": "ClientA", + "extra": "field", + "messagePayload": "SGVsbG8=", + "extrafield": [ + "again" + ], + "more": "fields" + })"; + + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); + + EXPECT_EQ(STATUS_SUCCESS, status); + EXPECT_STREQ("ClientA", receivedMessage.signalingMessage.peerClientId); + EXPECT_STREQ("Hello", receivedMessage.signalingMessage.payload); + EXPECT_EQ(5, receivedMessage.signalingMessage.payloadLen); + EXPECT_EQ(SIGNALING_MESSAGE_TYPE_OFFER, receivedMessage.signalingMessage.messageType); +} - // Check that we are connected and can send a message - SignalingMessage signalingMessage; - signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; - signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; - STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); - MEMSET(signalingMessage.payload, 'A', 100); - signalingMessage.payload[100] = '\0'; - signalingMessage.payloadLen = 0; - signalingMessage.correlationId[0] = '\0'; +TEST_F(SignalingApiFunctionalityTest, SignalingMessageParsing_InvalidJson) { + ReceivedSignalingMessage receivedMessage; - EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); + // Missing closing '}' + const std::string jsonMessage = R"({ + "messageType": "SDP_OFFER", + "senderClientId": "ClientA", + "messagePayload": "SGVsbG8=" + )"; - deleteChannelLws(FROM_SIGNALING_CLIENT_HANDLE(signalingHandle), 0); + STATUS status = parseSignalingMessage((PCHAR) jsonMessage.c_str(), jsonMessage.length(), &receivedMessage); - EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); + EXPECT_EQ(STATUS_INVALID_API_CALL_RETURN_JSON, status); + EXPECT_STREQ("", receivedMessage.signalingMessage.peerClientId); + EXPECT_STREQ("", receivedMessage.signalingMessage.payload); + EXPECT_EQ(0, receivedMessage.signalingMessage.payloadLen); + EXPECT_EQ(SIGNALING_MESSAGE_TYPE_UNKNOWN, receivedMessage.signalingMessage.messageType); } } // namespace webrtcclient