Skip to content

Commit 4722375

Browse files
Enhance the ice config parser (#2127)
* Move the ICE config parser to its own method and add tests * Add cases for invalid JSONs * Exit parsing ice servers early if the array if we requested less configs Co-authored-by: Vikram Dattu <vikram.dattu@espressif.com> --------- Co-authored-by: Vikram Dattu <vikram.dattu@espressif.com>
1 parent 7c52408 commit 4722375

File tree

6 files changed

+490
-49
lines changed

6 files changed

+490
-49
lines changed

src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -449,12 +449,27 @@ extern "C" {
449449
*/
450450
#define MAX_ICE_CONFIG_USER_NAME_LEN 256
451451

452+
/**
453+
* Buffer length for ICE configuration user name, including null terminator.
454+
*
455+
* \sa MAX_ICE_CONFIG_USER_NAME_LEN
456+
*/
457+
#define MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN (MAX_ICE_CONFIG_USER_NAME_LEN + 1)
458+
452459
/**
453460
* Maximum allowed ICE configuration password length
454-
* https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_AWSAcuitySignalingService_IceServer.html#KinesisVideo-Type-AWSAcuitySignalingService_IceServer-Password
461+
*
462+
* \sa https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_signaling_IceServer.html#KinesisVideo-Type-signaling_IceServer-Password
455463
*/
456464
#define MAX_ICE_CONFIG_CREDENTIAL_LEN 256
457465

466+
/**
467+
* Buffer length for ICE configuration password, including null terminator.
468+
*
469+
* \sa MAX_ICE_CONFIG_USER_NAME_LEN
470+
*/
471+
#define MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN (MAX_ICE_CONFIG_CREDENTIAL_LEN + 1)
472+
458473
/**
459474
* Maximum allowed signaling URI length
460475
*/
@@ -1139,9 +1154,9 @@ typedef struct {
11391154
* https://www.w3.org/TR/webrtc/#rtciceserver-dictionary
11401155
*/
11411156
typedef struct {
1142-
CHAR urls[MAX_ICE_CONFIG_URI_LEN + 1]; //!< URL of STUN/TURN Server
1143-
CHAR username[MAX_ICE_CONFIG_USER_NAME_LEN + 1]; //!< Username to be used with TURN server
1144-
CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1]; //!< Password to be used with TURN server
1157+
CHAR urls[MAX_ICE_CONFIG_URI_LEN + 1]; //!< URL of STUN/TURN Server
1158+
CHAR username[MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN]; //!< Username to be used with TURN server
1159+
CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN]; //!< Password to be used with TURN server
11451160
} RtcIceServer, *PRtcIceServer;
11461161

11471162
/**
@@ -1390,12 +1405,12 @@ typedef struct {
13901405
* NOTE:TTL is given in default which is 100ns duration
13911406
*/
13921407
typedef struct {
1393-
UINT32 version; //!< Version of the struct
1394-
UINT64 ttl; //!< TTL of the configuration is 100ns
1395-
UINT32 uriCount; //!< Number of Ice URI objects
1396-
CHAR uris[MAX_ICE_CONFIG_URI_COUNT][MAX_ICE_CONFIG_URI_LEN + 1]; //!< List of Ice server URIs
1397-
CHAR userName[MAX_ICE_CONFIG_USER_NAME_LEN + 1]; //!< Username for the server
1398-
CHAR password[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1]; //!< Password for the server
1408+
UINT32 version; //!< Version of the struct
1409+
UINT64 ttl; //!< TTL of the configuration is 100ns
1410+
UINT32 uriCount; //!< Number of Ice URI objects
1411+
CHAR uris[MAX_ICE_CONFIG_URI_COUNT][MAX_ICE_CONFIG_URI_BUFFER_LEN]; //!< List of Ice server URIs
1412+
CHAR userName[MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN]; //!< Username for the server
1413+
CHAR password[MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN]; //!< Password for the server
13991414
} IceConfigInfo, *PIceConfigInfo;
14001415

14011416
typedef struct {

src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ extern "C" {
2929
*/
3030
#define MAX_ICE_CONFIG_URI_LEN 127
3131

32+
/**
33+
* Maximum allowed ICE URI buffer length, including the null terminator.
34+
*
35+
* \sa MAX_ICE_CONFIG_URI_LEN
36+
*/
37+
#define MAX_ICE_CONFIG_URI_BUFFER_LEN (MAX_ICE_CONFIG_URI_LEN + 1)
38+
3239
/**
3340
* Maximum allowed relay protocol length
3441
*/
@@ -244,14 +251,14 @@ typedef struct {
244251
* Reference: https://www.w3.org/TR/webrtc-stats/#ice-server-dict*
245252
*/
246253
typedef struct {
247-
CHAR url[MAX_ICE_CONFIG_URI_LEN + 1]; //!< STUN/TURN server URL
248-
CHAR protocol[MAX_PROTOCOL_LENGTH + 1]; //!< Valid values: UDP, TCP
249-
UINT32 iceServerIndex; //!< Ice server index to get stats from. Not available in spec! Needs to be
250-
//!< populated by the application to get specific server stats
251-
INT32 port; //!< Port number used by client
252-
UINT64 totalRequestsSent; //!< Total amount of requests that have been sent to the server
253-
UINT64 totalResponsesReceived; //!< Total number of responses received from the server
254-
UINT64 totalRoundTripTime; //!< Sum of RTTs of all the requests for which response has been received
254+
CHAR url[MAX_ICE_CONFIG_URI_BUFFER_LEN]; //!< STUN/TURN server URL
255+
CHAR protocol[MAX_PROTOCOL_LENGTH + 1]; //!< Valid values: UDP, TCP
256+
UINT32 iceServerIndex; //!< Ice server index to get stats from. Not available in spec! Needs to be
257+
//!< populated by the application to get specific server stats
258+
INT32 port; //!< Port number used by client
259+
UINT64 totalRequestsSent; //!< Total amount of requests that have been sent to the server
260+
UINT64 totalResponsesReceived; //!< Total number of responses received from the server
261+
UINT64 totalRoundTripTime; //!< Sum of RTTs of all the requests for which response has been received
255262
} RtcIceServerStats, *PRtcIceServerStats;
256263

257264
/**

src/source/Ice/IceUtils.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ typedef struct {
5454
// isTurn ? TURN server : STUN server
5555
BOOL isTurn;
5656
BOOL isSecure;
57-
CHAR url[MAX_ICE_CONFIG_URI_LEN + 1];
57+
CHAR url[MAX_ICE_CONFIG_URI_BUFFER_LEN];
5858
KvsIpAddress ipAddress;
59-
CHAR username[MAX_ICE_CONFIG_USER_NAME_LEN + 1];
60-
CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1];
59+
CHAR username[MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN];
60+
CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN];
6161
KVS_SOCKET_PROTOCOL transport;
6262
IceServerSetIpFunc setIpFn;
6363
} IceServer, *PIceServer;

src/source/Signaling/LwsApiCalls.c

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,14 +1188,47 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time)
11881188
CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL,
11891189
STATUS_SIGNALING_LWS_CALL_FAILED);
11901190

1191-
// Parse the response
1191+
CHK_STATUS(
1192+
parseIceConfigResponse(pResponseStr, resultLen, MAX_ICE_CONFIG_COUNT, pSignalingClient->iceConfigs, &pSignalingClient->iceConfigCount));
1193+
1194+
// Perform some validation on the ice configuration
1195+
CHK_STATUS(validateIceConfiguration(pSignalingClient));
1196+
1197+
CleanUp:
1198+
1199+
if (STATUS_FAILED(retStatus)) {
1200+
DLOGE("Call Failed with Status: 0x%08x", retStatus);
1201+
}
1202+
1203+
freeLwsCallInfo(&pLwsCallInfo);
1204+
1205+
LEAVES();
1206+
return retStatus;
1207+
}
1208+
1209+
STATUS parseIceConfigResponse(PCHAR pResponseStr, UINT32 responseLen, UINT8 maxIceConfigs, PIceConfigInfo pIceConfigs, PUINT32 pIceConfigCount)
1210+
{
1211+
ENTERS();
1212+
STATUS retStatus = STATUS_SUCCESS;
1213+
jsmn_parser parser;
1214+
jsmntok_t tokens[MAX_JSON_TOKEN_COUNT];
1215+
jsmntok_t* pToken;
1216+
UINT32 i, configCount = 0, strLen; // Note: strLen excludes the NULL terminator
1217+
INT32 j, tokenCount;
1218+
UINT64 ttl;
1219+
BOOL jsonInIceServerList = FALSE;
1220+
1221+
CHK(pIceConfigs != NULL && pIceConfigCount != NULL && pResponseStr != NULL, STATUS_NULL_ARG);
1222+
CHK(maxIceConfigs > 0, STATUS_INVALID_ARG);
1223+
CHK(!IS_EMPTY_STRING(pResponseStr), STATUS_INVALID_API_CALL_RETURN_JSON);
1224+
11921225
jsmn_init(&parser);
1193-
tokenCount = jsmn_parse(&parser, pResponseStr, resultLen, tokens, SIZEOF(tokens) / SIZEOF(jsmntok_t));
1226+
tokenCount = jsmn_parse(&parser, pResponseStr, responseLen, tokens, SIZEOF(tokens) / SIZEOF(jsmntok_t));
11941227
CHK(tokenCount > 1, STATUS_INVALID_API_CALL_RETURN_JSON);
11951228
CHK(tokens[0].type == JSMN_OBJECT, STATUS_INVALID_API_CALL_RETURN_JSON);
11961229

1197-
MEMSET(&pSignalingClient->iceConfigs, 0x00, MAX_ICE_CONFIG_COUNT * SIZEOF(IceConfigInfo));
1198-
pSignalingClient->iceConfigCount = 0;
1230+
MEMSET(pIceConfigs, 0x00, maxIceConfigs * SIZEOF(IceConfigInfo));
1231+
*pIceConfigCount = 0;
11991232

12001233
// Loop through the tokens and extract the ice configuration
12011234
for (i = 0; i < tokenCount; i++) {
@@ -1204,58 +1237,64 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time)
12041237
jsonInIceServerList = TRUE;
12051238

12061239
CHK(tokens[i + 1].type == JSMN_ARRAY, STATUS_INVALID_API_CALL_RETURN_JSON);
1207-
CHK(tokens[i + 1].size <= MAX_ICE_CONFIG_COUNT, STATUS_SIGNALING_MAX_ICE_CONFIG_COUNT);
1240+
if (tokens[i + 1].size > maxIceConfigs) {
1241+
DLOGW("Received more ice configs (%d) than supported (%d). Will ignore the rest.", tokens[i + 1].size, maxIceConfigs);
1242+
}
12081243
}
12091244
} else {
12101245
pToken = &tokens[i];
1211-
if (pToken->type == JSMN_OBJECT) {
1246+
if (pToken->type == JSMN_UNDEFINED) {
1247+
DLOGW("Encountered unexpected item in the JSON! %*.s", pResponseStr, responseLen);
1248+
// Skip this token and continue parsing
1249+
// Don't return error, just move to next token
1250+
if (i + 1 < tokenCount && tokens[i + 1].type != JSMN_OBJECT) {
1251+
i++; // Skip the value associated with this field
1252+
}
1253+
continue;
1254+
} else if (pToken->type == JSMN_OBJECT) {
1255+
if (configCount + 1 > maxIceConfigs) {
1256+
// That's all we have room to parse
1257+
break;
1258+
}
12121259
configCount++;
12131260
} else if (compareJsonString(pResponseStr, pToken, JSMN_STRING, (PCHAR) "Username")) {
12141261
strLen = (UINT32) (pToken[1].end - pToken[1].start);
12151262
CHK(strLen <= MAX_ICE_CONFIG_USER_NAME_LEN, STATUS_INVALID_API_CALL_RETURN_JSON);
1216-
STRNCPY(pSignalingClient->iceConfigs[configCount - 1].userName, pResponseStr + pToken[1].start, strLen);
1217-
pSignalingClient->iceConfigs[configCount - 1].userName[MAX_ICE_CONFIG_USER_NAME_LEN] = '\0';
1263+
SNPRINTF(pIceConfigs[configCount - 1].userName, MAX_ICE_CONFIG_USER_NAME_BUFFER_LEN, "%.*s", strLen, pResponseStr + pToken[1].start);
12181264
i++;
12191265
} else if (compareJsonString(pResponseStr, pToken, JSMN_STRING, (PCHAR) "Password")) {
12201266
strLen = (UINT32) (pToken[1].end - pToken[1].start);
12211267
CHK(strLen <= MAX_ICE_CONFIG_CREDENTIAL_LEN, STATUS_INVALID_API_CALL_RETURN_JSON);
1222-
STRNCPY(pSignalingClient->iceConfigs[configCount - 1].password, pResponseStr + pToken[1].start, strLen);
1223-
pSignalingClient->iceConfigs[configCount - 1].userName[MAX_ICE_CONFIG_CREDENTIAL_LEN] = '\0';
1268+
SNPRINTF(pIceConfigs[configCount - 1].password, MAX_ICE_CONFIG_CREDENTIAL_BUFFER_LEN, "%.*s", strLen, pResponseStr + pToken[1].start);
12241269
i++;
12251270
} else if (compareJsonString(pResponseStr, pToken, JSMN_STRING, (PCHAR) "Ttl")) {
1226-
CHK_STATUS(STRTOUI64(pResponseStr + pToken[1].start, pResponseStr + pToken[1].end, 10, &ttl));
1227-
1228-
// NOTE: Ttl value is in seconds
1229-
pSignalingClient->iceConfigs[configCount - 1].ttl = ttl * HUNDREDS_OF_NANOS_IN_A_SECOND;
1271+
retStatus = STRTOUI64((PCHAR) pResponseStr + pToken[1].start, (PCHAR) pResponseStr + pToken[1].end, 10, &ttl);
1272+
if (STATUS_FAILED(retStatus)) {
1273+
strLen = (UINT32) (pToken[1].end - pToken[1].start);
1274+
DLOGE("Unable to convert TTL: %.*s to a number", strLen, (PCHAR) pResponseStr + pToken[1].start);
1275+
retStatus = STATUS_INVALID_API_CALL_RETURN_JSON;
1276+
CHK(FALSE, retStatus);
1277+
}
1278+
pIceConfigs[configCount - 1].ttl = ttl * HUNDREDS_OF_NANOS_IN_A_SECOND;
12301279
i++;
12311280
} else if (compareJsonString(pResponseStr, pToken, JSMN_STRING, (PCHAR) "Uris")) {
1232-
// Expect an array of elements
12331281
CHK(pToken[1].type == JSMN_ARRAY, STATUS_INVALID_API_CALL_RETURN_JSON);
12341282
CHK(pToken[1].size <= MAX_ICE_CONFIG_URI_COUNT, STATUS_SIGNALING_MAX_ICE_URI_COUNT);
12351283
for (j = 0; j < pToken[1].size; j++) {
12361284
strLen = (UINT32) (pToken[j + 2].end - pToken[j + 2].start);
12371285
CHK(strLen <= MAX_ICE_CONFIG_URI_LEN, STATUS_SIGNALING_MAX_ICE_URI_LEN);
1238-
STRNCPY(pSignalingClient->iceConfigs[configCount - 1].uris[j], pResponseStr + pToken[j + 2].start, strLen);
1239-
pSignalingClient->iceConfigs[configCount - 1].uris[j][MAX_ICE_CONFIG_URI_LEN] = '\0';
1240-
pSignalingClient->iceConfigs[configCount - 1].uriCount++;
1286+
SNPRINTF(pIceConfigs[configCount - 1].uris[j], MAX_ICE_CONFIG_URI_BUFFER_LEN, "%.*s", strLen, pResponseStr + pToken[j + 2].start);
1287+
pIceConfigs[configCount - 1].uriCount++;
12411288
}
1242-
12431289
i += pToken[1].size + 1;
12441290
}
12451291
}
12461292
}
12471293

1248-
// Perform some validation on the ice configuration
1249-
pSignalingClient->iceConfigCount = configCount;
1250-
CHK_STATUS(validateIceConfiguration(pSignalingClient));
1294+
*pIceConfigCount = configCount;
12511295

12521296
CleanUp:
1253-
1254-
if (STATUS_FAILED(retStatus)) {
1255-
DLOGE("Call Failed with Status: 0x%08x", retStatus);
1256-
}
1257-
1258-
freeLwsCallInfo(&pLwsCallInfo);
1297+
CHK_LOG_ERR(retStatus);
12591298

12601299
LEAVES();
12611300
return retStatus;

src/source/Signaling/LwsApiCalls.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,25 @@ STATUS wakeLwsServiceEventLoop(PSignalingClient, UINT32);
281281
STATUS terminateConnectionWithStatus(PSignalingClient, SERVICE_CALL_RESULT);
282282
STATUS configureLwsLogging(UINT32 kvsLogLevel);
283283

284+
/**
285+
* Parses ICE configuration from a JSON response string.
286+
* If there are more ICE configurations in the string than maxIceConfigs, we will only
287+
* parse up until maxIceConfigs.
288+
*
289+
* @param[in] pResponseStr JSON string containing ICE server configuration.
290+
* @param[in] responseLen Length of the JSON string (excluding null-terminator).
291+
* @param[in] maxIceConfigs Maximum number of ICE configurations the array can hold.
292+
* @param[out] pIceConfigs Pointer to array of IceConfigInfo structures to be populated.
293+
* @param[out] pIceConfigCount Pointer to receive the number of ICE configurations parsed.
294+
*
295+
* @return STATUS code of the execution:
296+
* - STATUS_SUCCESS: Successfully parsed ICE configuration.
297+
* - STATUS_NULL_ARG: Invalid NULL argument provided.
298+
* - STATUS_INVALID_API_CALL_RETURN_JSON: Malformed JSON or missing required fields.
299+
* - STATUS_SIGNALING_MAX_ICE_URI_COUNT: Too many URIs in configuration (more than MAX_ICE_CONFIG_URI_COUNT).
300+
*/
301+
STATUS parseIceConfigResponse(PCHAR, UINT32, UINT8, PIceConfigInfo, PUINT32);
302+
284303
#ifdef __cplusplus
285304
}
286305
#endif

0 commit comments

Comments
 (0)