Skip to content

Commit

Permalink
STUN DNS resolution and webrtc client singleton
Browse files Browse the repository at this point in the history
  • Loading branch information
disa6302 committed Sep 21, 2023
1 parent 3ad6f81 commit 04008a6
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ extern "C" {
#define STATUS_PEERCONNECTION_CREATE_ANSWER_WITHOUT_REMOTE_DESCRIPTION STATUS_PEERCONNECTION_BASE + 0x00000001
#define STATUS_PEERCONNECTION_CODEC_INVALID STATUS_PEERCONNECTION_BASE + 0x00000002
#define STATUS_PEERCONNECTION_CODEC_MAX_EXCEEDED STATUS_PEERCONNECTION_BASE + 0x00000003
#define STATUS_PEERCONNECTION_UNSUPPORTED_HOSTNAME STATUS_PEERCONNECTION_BASE + 0x00000004
/*!@} */

/////////////////////////////////////////////////////
Expand Down Expand Up @@ -696,9 +697,10 @@ 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_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"

/**
* Default signaling SSL port
Expand Down
5 changes: 5 additions & 0 deletions src/source/Ice/IceAgent.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ STATUS createIceAgent(PCHAR username, PCHAR password, PIceAgentCallbacks pIceAge
pIceAgent->iceServersCount = 0;
for (i = 0; i < MAX_ICE_SERVERS_COUNT; i++) {
if (pRtcConfiguration->iceServers[i].urls[0] != '\0') {
if (STRSTR(pRtcConfiguration->iceServers[i].urls, "stun")) {
pIceAgent->iceServers[pIceAgent->iceServersCount].setIpFn = pIceAgent->iceAgentCallbacks.setStunServerIpFn;
} else {
pIceAgent->iceServers[pIceAgent->iceServersCount].setIpFn = NULL;
}
PROFILE_CALL_WITH_T_OBJ(
retStatus = parseIceServer(&pIceAgent->iceServers[pIceAgent->iceServersCount], (PCHAR) pRtcConfiguration->iceServers[i].urls,
(PCHAR) pRtcConfiguration->iceServers[i].username, (PCHAR) pRtcConfiguration->iceServers[i].credential),
Expand Down
1 change: 1 addition & 0 deletions src/source/Ice/IceAgent.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ typedef struct {
IceInboundPacketFunc inboundPacketFn;
IceConnectionStateChangedFunc connectionStateChangedFn;
IceNewLocalCandidateFunc newLocalCandidateFn;
IceServerSetIpFunc setStunServerIpFn;
} IceAgentCallbacks, *PIceAgentCallbacks;

typedef struct {
Expand Down
15 changes: 14 additions & 1 deletion src/source/Ice/IceUtils.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ STATUS parseIceServer(PIceServer pIceServer, PCHAR url, PCHAR username, PCHAR cr
STATUS retStatus = STATUS_SUCCESS;
PCHAR separator = NULL, urlNoPrefix = NULL, paramStart = NULL;
UINT32 port = ICE_STUN_DEFAULT_PORT;
CHAR addressResolved[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'};

// username and credential is only mandatory for turn server
CHK(url != NULL && pIceServer != NULL, STATUS_NULL_ARG);
Expand Down Expand Up @@ -247,8 +248,20 @@ STATUS parseIceServer(PIceServer pIceServer, PCHAR url, PCHAR username, PCHAR cr
STRNCPY(pIceServer->url, urlNoPrefix, MAX_ICE_CONFIG_URI_LEN);
}

CHK_STATUS(getIpWithHostName(pIceServer->url, &pIceServer->ipAddress));
if (pIceServer->setIpFn != NULL) {
retStatus = pIceServer->setIpFn(0, pIceServer->url, &pIceServer->ipAddress);
}

if (retStatus == STATUS_NULL_ARG || pIceServer->setIpFn == NULL) {
// Reset the retStatus to ensure the appropriate status code is returned from
// getIpWithHostName
retStatus = STATUS_SUCCESS;
CHK_STATUS(getIpWithHostName(pIceServer->url, &pIceServer->ipAddress));
}

pIceServer->ipAddress.port = (UINT16) getInt16((INT16) port);
getIpAddrStr(&pIceServer->ipAddress, addressResolved, ARRAY_SIZE(addressResolved));
DLOGP("ICE Server address for %s: %s", pIceServer->url, addressResolved);

CleanUp:

Expand Down
1 change: 1 addition & 0 deletions src/source/Ice/IceUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ typedef struct {
CHAR username[MAX_ICE_CONFIG_USER_NAME_LEN + 1];
CHAR credential[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1];
KVS_SOCKET_PROTOCOL transport;
IceServerSetIpFunc setIpFn;
} IceServer, *PIceServer;

STATUS parseIceServer(PIceServer, PCHAR, PCHAR, PCHAR);
Expand Down
4 changes: 0 additions & 4 deletions src/source/Ice/Network.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,6 @@ STATUS getIpWithHostName(PCHAR hostname, PKvsIpAddress destIp)
struct in_addr inaddr;

CHAR addr[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'};
CHAR addressResolved[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'};

CHK(hostname != NULL, STATUS_NULL_ARG);
DLOGI("ICE SERVER Hostname received: %s", hostname);
Expand Down Expand Up @@ -442,12 +441,9 @@ STATUS getIpWithHostName(PCHAR hostname, PKvsIpAddress destIp)
}
freeaddrinfo(res);
CHK_ERR(resolved, STATUS_HOSTNAME_NOT_FOUND, "Could not find network address of %s", hostname);
getIpAddrStr(destIp, addressResolved, ARRAY_SIZE(addressResolved));
DLOGP("ICE Server address for %s with getaddrinfo: %s", hostname, addressResolved);
}

else {
DLOGP("ICE Server address for %s: %s", hostname, addr);
inet_pton(AF_INET, addr, &inaddr);
destIp->family = KVS_IP_FAMILY_TYPE_IPV4;
MEMCPY(destIp->address, &inaddr, IPV4_ADDRESS_LENGTH);
Expand Down
3 changes: 3 additions & 0 deletions src/source/Include_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ typedef struct {
// Used for ensuring alignment
#define ALIGN_UP_TO_MACHINE_WORD(x) ROUND_UP((x), SIZEOF(SIZE_T))

typedef STATUS (*IceServerSetIpFunc)(UINT64, PCHAR, PKvsIpAddress);
STATUS getIpAddrStr(PKvsIpAddress pKvsIpAddress, PCHAR pBuffer, UINT32 bufferLen);

////////////////////////////////////////////////////
// Project forward declarations
////////////////////////////////////////////////////
Expand Down
164 changes: 163 additions & 1 deletion src/source/PeerConnection/PeerConnection.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@

static volatile ATOMIC_BOOL gKvsWebRtcInitialized = (SIZE_T) FALSE;

// Function to get access to the Singleton instance
PWebRtcClientContext getWebRtcClientInstance()
{
static WebRtcClientContext w = {.pStunIpAddrCtx = NULL};
return &w;
}

PStunIpAddrContext getStunIpContext()
{
PWebRtcClientContext pWebRtcClientContext = getWebRtcClientInstance();
return pWebRtcClientContext->pStunIpAddrCtx;
}

STATUS createWebRtcClientInstance()
{
PWebRtcClientContext pWebRtcClientContext = getWebRtcClientInstance();
STATUS retStatus = STATUS_SUCCESS;

CHK_WARN(pWebRtcClientContext->pStunIpAddrCtx == NULL, STATUS_INVALID_OPERATION, "STUN object already allocated");
pWebRtcClientContext->pStunIpAddrCtx = (PStunIpAddrContext) MEMCALLOC(1, SIZEOF(StunIpAddrContext));
CHK_ERR(pWebRtcClientContext->pStunIpAddrCtx != NULL, STATUS_NULL_ARG, "Memory allocation for WebRtc client object failed");
pWebRtcClientContext->pStunIpAddrCtx->lock = MUTEX_CREATE(FALSE);
pWebRtcClientContext->pStunIpAddrCtx->expirationDuration = 2 * HUNDREDS_OF_NANOS_IN_AN_HOUR;

CleanUp:
return retStatus;
}

STATUS allocateSrtp(PKvsPeerConnection pKvsPeerConnection)
{
DtlsKeyingMaterial dtlsKeyingMaterial;
Expand Down Expand Up @@ -687,6 +715,108 @@ STATUS rtcpReportsCallback(UINT32 timerId, UINT64 currentTime, UINT64 customData
return retStatus;
}

// Not thread safe
STATUS getAddrAsync(PStunIpAddrContext pStunIpAddrCtx)
{
INT32 errCode;
STATUS retStatus = STATUS_SUCCESS;
struct addrinfo *rp, *res;
struct sockaddr_in* ipv4Addr;
BOOL resolved = FALSE;

errCode = getaddrinfo(pStunIpAddrCtx->hostname, NULL, NULL, &res);
if (errCode != 0) {
DLOGI("Failed to resolve hostname with errcode: %d", errCode);
retStatus = STATUS_RESOLVE_HOSTNAME_FAILED;
} else {
for (rp = res; rp != NULL && !resolved; rp = rp->ai_next) {
if (rp->ai_family == AF_INET) {
ipv4Addr = (struct sockaddr_in*) rp->ai_addr;
pStunIpAddrCtx->kvsIpAddr.family = KVS_IP_FAMILY_TYPE_IPV4;
pStunIpAddrCtx->kvsIpAddr.port = 0;
MEMCPY(pStunIpAddrCtx->kvsIpAddr.address, &ipv4Addr->sin_addr, IPV4_ADDRESS_LENGTH);
resolved = TRUE;
}
}
freeaddrinfo(res);
}
if (!resolved) {
retStatus = STATUS_RESOLVE_HOSTNAME_FAILED;
}
CleanUp:
return retStatus;
}

STATUS onSetStunServerIp(UINT64 customData, PCHAR url, PKvsIpAddress pIpAddr)
{
UNUSED_PARAM(customData);
STATUS retStatus = STATUS_SUCCESS;
CHAR addressResolved[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'};
PStunIpAddrContext pStunIpAddrCtx = getStunIpContext();
CHK_ERR(pStunIpAddrCtx != NULL, STATUS_NULL_ARG, "WebRTC Client object could not be created");
UINT64 currentTime = GETTIME();

MUTEX_LOCK(pStunIpAddrCtx->lock);
CHK(STRCMP(url, pStunIpAddrCtx->hostname) == 0, STATUS_PEERCONNECTION_UNSUPPORTED_HOSTNAME);

if (pStunIpAddrCtx->isIpInitialized) {
DLOGI("Initialized successfully");
if (currentTime > (pStunIpAddrCtx->startTime + pStunIpAddrCtx->expirationDuration)) {
DLOGI("Expired...need to refresh STUN address");
// Reset start time
pStunIpAddrCtx->startTime = 0;
CHK_ERR(getAddrAsync(pStunIpAddrCtx) == STATUS_SUCCESS, retStatus, "Failed to resolve after cache expiry");
}
MEMCPY(pIpAddr, &pStunIpAddrCtx->kvsIpAddr, SIZEOF(pStunIpAddrCtx->kvsIpAddr));
} else {
DLOGE("Initialization failed");
}
CleanUp:
MUTEX_UNLOCK(pStunIpAddrCtx->lock);
return retStatus;
}

PVOID resolveStunIceServerIp(PVOID args)
{
UNUSED_PARAM(args);
BOOL locked = FALSE;
CHAR addressResolved[KVS_IP_ADDRESS_STRING_BUFFER_LEN + 1] = {'\0'};
PStunIpAddrContext pStunIpAddrCtx = getStunIpContext();

PCHAR pRegion;
PCHAR pHostnamePostfix;
if (pStunIpAddrCtx == NULL) {
DLOGE("Failed to resolve STUN IP address because webrtc client instance was not created");
return NULL;
}

if ((pRegion = GETENV(DEFAULT_REGION_ENV_VAR)) == NULL) {
pRegion = DEFAULT_AWS_REGION;
}

pHostnamePostfix = KINESIS_VIDEO_STUN_URL_POSTFIX;
// If region is in CN, add CN region uri postfix
if (STRSTR(pRegion, "cn-")) {
pHostnamePostfix = KINESIS_VIDEO_STUN_URL_POSTFIX_CN;
}

MUTEX_LOCK(pStunIpAddrCtx->lock);
locked = TRUE;
SNPRINTF(pStunIpAddrCtx->hostname, SIZEOF(pStunIpAddrCtx->hostname), KINESIS_VIDEO_STUN_URL_WITHOUT_PORT, pRegion, pHostnamePostfix);
if (getAddrAsync(pStunIpAddrCtx) == STATUS_SUCCESS) {
getIpAddrStr(&pStunIpAddrCtx->kvsIpAddr, addressResolved, ARRAY_SIZE(addressResolved));
DLOGI("ICE Server address for %s with getaddrinfo: %s", pStunIpAddrCtx->hostname, addressResolved);
pStunIpAddrCtx->isIpInitialized = TRUE;
} else {
DLOGE("Failed to resolve %s", pStunIpAddrCtx->hostname);
}
pStunIpAddrCtx->startTime = GETTIME();
if (locked) {
MUTEX_UNLOCK(pStunIpAddrCtx->lock);
}
return NULL;
}

STATUS createPeerConnection(PRtcConfiguration pConfiguration, PRtcPeerConnection* ppPeerConnection)
{
ENTERS();
Expand Down Expand Up @@ -738,6 +868,8 @@ STATUS createPeerConnection(PRtcConfiguration pConfiguration, PRtcPeerConnection
iceAgentCallbacks.inboundPacketFn = onInboundPacket;
iceAgentCallbacks.connectionStateChangedFn = onIceConnectionStateChange;
iceAgentCallbacks.newLocalCandidateFn = onNewIceLocalCandidate;
iceAgentCallbacks.setStunServerIpFn = onSetStunServerIp;

CHK_STATUS(createConnectionListener(&pConnectionListener));
// IceAgent will own the lifecycle of pConnectionListener;
CHK_STATUS(createIceAgent(pKvsPeerConnection->localIceUfrag, pKvsPeerConnection->localIcePwd, &iceAgentCallbacks, pConfiguration,
Expand Down Expand Up @@ -1430,6 +1562,8 @@ STATUS initKvsWebRtc(VOID)
#ifdef ENABLE_KVS_THREADPOOL
DLOGI("KVS WebRtc library using thread pool");
CHK_STATUS(createThreadPoolContext());
createWebRtcClientInstance();
CHK_STATUS(threadpoolContextPush(resolveStunIceServerIp, NULL));
#endif
ATOMIC_STORE_BOOL(&gKvsWebRtcInitialized, TRUE);

Expand All @@ -1439,6 +1573,33 @@ STATUS initKvsWebRtc(VOID)
return retStatus;
}

STATUS cleanupWebRtcClientContext()
{
STATUS retStatus = STATUS_SUCCESS;
// Stun object cleanup
PWebRtcClientContext pWebRtcClientContext = getWebRtcClientInstance();

CHK_WARN(pWebRtcClientContext->pStunIpAddrCtx != NULL, STATUS_NULL_ARG, "Destroying STUN object without setting up");

/* Start of handling STUN object */
// Need this check to ensure we do not clean up the object in the next
// step while the resolve thread is ongoing

MUTEX_LOCK(pWebRtcClientContext->pStunIpAddrCtx->lock);
pWebRtcClientContext->pStunIpAddrCtx->isIpInitialized = FALSE;
MUTEX_UNLOCK(pWebRtcClientContext->pStunIpAddrCtx->lock);
if (IS_VALID_MUTEX_VALUE(pWebRtcClientContext->pStunIpAddrCtx->lock)) {
MUTEX_FREE(pWebRtcClientContext->pStunIpAddrCtx->lock);
}
SAFE_MEMFREE(pWebRtcClientContext->pStunIpAddrCtx);
DLOGI("Destroyed STUN IP object");
/* End of handling STUN object */

DLOGI("Destroyed WebRtc client context");
CleanUp:
return retStatus;
}

STATUS deinitKvsWebRtc(VOID)
{
ENTERS();
Expand All @@ -1453,8 +1614,9 @@ STATUS deinitKvsWebRtc(VOID)

ATOMIC_STORE_BOOL(&gKvsWebRtcInitialized, FALSE);
#ifdef ENABLE_KVS_THREADPOOL
DLOGI("Destroying KVS Webrtc library threadpool");
destroyThreadPoolContext();
DLOGI("Destroyed threadpool");
cleanupWebRtcClientContext();
#endif

CleanUp:
Expand Down
16 changes: 16 additions & 0 deletions src/source/PeerConnection/PeerConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,22 @@ typedef struct {
PHashTable unkeyedDataChannels;
} AllocateSctpSortDataChannelsData, *PAllocateSctpSortDataChannelsData;

typedef struct {
CHAR hostname[MAX_ICE_CONFIG_URI_LEN + 1];
KvsIpAddress kvsIpAddr;
BOOL isIpInitialized;
UINT64 startTime;
UINT64 expirationDuration;
STATUS status;
MUTEX lock;
} StunIpAddrContext, *PStunIpAddrContext;

// Declare the structure of the Singleton
// Members of the singleton are responsible for their own sync mechanisms.
typedef struct {
PStunIpAddrContext pStunIpAddrCtx;
} WebRtcClientContext, *PWebRtcClientContext;

STATUS onFrameReadyFunc(UINT64, UINT16, UINT16, UINT32);
STATUS onFrameDroppedFunc(UINT64, UINT16, UINT16, UINT32);
VOID onSctpSessionOutboundPacket(UINT64, PBYTE, UINT32);
Expand Down
2 changes: 1 addition & 1 deletion src/source/Threadpool/ThreadPoolContext.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ STATUS createThreadPoolContext()
MUTEX_LOCK(pThreadPoolContext->threadpoolContextLock);
locked = TRUE;
CHK_WARN(!pThreadPoolContext->isInitialized, retStatus, "Threadpool already set up. Nothing to do");
CHK_ERR(pThreadPoolContext->pThreadpool == NULL, STATUS_NULL_ARG, "Threadpool object is to be set up");
CHK_WARN(pThreadPoolContext->pThreadpool == NULL, STATUS_INVALID_OPERATION, "Threadpool object already allocated");
CHK_STATUS(threadpoolCreate(&pThreadPoolContext->pThreadpool, minThreads, maxThreads));
pThreadPoolContext->isInitialized = TRUE;
CleanUp:
Expand Down
1 change: 1 addition & 0 deletions tst/TurnConnectionFunctionalityTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class TurnConnectionFunctionalityTest : public WebRtcClientTestBase {
for (uriCount = 0, i = 0; i < iceConfigCount; i++) {
EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(mSignalingClientHandle, i, &pIceConfigInfo));
for (j = 0; j < pIceConfigInfo->uriCount; j++) {
iceServers[uriCount].setIpFn = NULL;
EXPECT_EQ(STATUS_SUCCESS,
parseIceServer(&iceServers[uriCount++], pIceConfigInfo->uris[j], pIceConfigInfo->userName, pIceConfigInfo->password));
}
Expand Down

0 comments on commit 04008a6

Please sign in to comment.