Skip to content

Commit

Permalink
mark turn peer as failed if its request receive a error response. Fix…
Browse files Browse the repository at this point in the history
… printing extra char when printing stun error message.
  • Loading branch information
chehefen committed Apr 16, 2020
1 parent 909277d commit f7c9706
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ extern "C" {
#define STATUS_TURN_MISSING_CHANNEL_DATA_HEADER STATUS_ICE_BASE + 0x00000024
#define STATUS_ICE_FAILED_TO_RECOVER_FROM_DISCONNECTION STATUS_ICE_BASE + 0x00000025
#define STATUS_ICE_NO_AVAILABLE_ICE_CANDIDATE_PAIR STATUS_ICE_BASE + 0x00000026
#define STATUS_TURN_CONNECTION_STATE_NOT_READY_TO_SEND_DATA STATUS_ICE_BASE + 0x00000027
#define STATUS_TURN_CONNECTION_PEER_NOT_USABLE STATUS_ICE_BASE + 0x00000027
/*!@} */

/*===========================================================================================*/
Expand Down
20 changes: 6 additions & 14 deletions src/source/Ice/IceAgent.c
Original file line number Diff line number Diff line change
Expand Up @@ -950,34 +950,26 @@ STATUS iceAgentSendStunPacket(PStunPacket pStunPacket, PBYTE password, UINT32 pa
STATUS retStatus = STATUS_SUCCESS;
UINT32 stunPacketSize = STUN_PACKET_ALLOCATION_SIZE;
BYTE stunPacketBuffer[STUN_PACKET_ALLOCATION_SIZE];
KvsIpAddress destAddr;
SocketConnection socketConnection;
BOOL isRelay = FALSE;
PIceCandidatePair pIceCandidatePair = NULL;

// Assuming holding pIceAgent->lock

CHK(pStunPacket != NULL && pIceAgent != NULL && pLocalCandidate != NULL && pDestAddr != NULL, STATUS_NULL_ARG);

// Construct context
// Stun Binding Indication seems to not expect any response. Therefore not storing transactionId
CHK_STATUS(iceUtilsPackageStunPacket(pStunPacket, password, passwordLen, stunPacketBuffer, &stunPacketSize));
socketConnection = *pLocalCandidate->pSocketConnection;
destAddr = *pDestAddr;
isRelay = pLocalCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED;

retStatus = iceUtilsSendData((PBYTE) stunPacketBuffer,
stunPacketSize,
&destAddr,
&socketConnection,
pDestAddr,
pLocalCandidate->pSocketConnection,
pIceAgent->turnConnectionTracker.pTurnConnection,
isRelay);
pLocalCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED);

if (STATUS_FAILED(retStatus)) {
DLOGW("iceUtilsSendData failed with 0x%08x", retStatus);
DLOGW("iceUtilsSendData failed with 0x%08x. Mark candidate pair as failed.", retStatus);
retStatus = STATUS_SUCCESS;

// Update iceCandidatePair state to failed. pIceCandidatePair could no longer exist.
/* Update iceCandidatePair state to failed.
* pIceCandidatePair could no longer exist. */
CHK_STATUS(findIceCandidatePairWithLocalSocketConnectionAndRemoteAddr(pIceAgent, pLocalCandidate->pSocketConnection, pDestAddr, TRUE, &pIceCandidatePair));

if (pIceCandidatePair != NULL) {
Expand Down
166 changes: 94 additions & 72 deletions src/source/Ice/TurnConnection.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ STATUS turnConnectionHandleStunError(PTurnConnection pTurnConnection, PBYTE pBuf
PStunAttributeRealm pStunAttributeRealm = NULL;
PStunPacket pStunPacket = NULL;
BOOL locked = FALSE;
PTurnPeer pTurnPeer = NULL;
PDoubleListNode pCurNode = NULL;

CHK(pTurnConnection != NULL, STATUS_NULL_ARG);
CHK(pBuffer != NULL && bufferLen > 0, STATUS_INVALID_ARG);
Expand All @@ -350,49 +352,68 @@ STATUS turnConnectionHandleStunError(PTurnConnection pTurnConnection, PBYTE pBuf
if (pTurnConnection->credentialObtained) {
retStatus = deserializeStunPacket(pBuffer, bufferLen, pTurnConnection->longTermKey, MD5_DIGEST_LENGTH, &pStunPacket);
}
// if deserializing with password didnt work, try deserialize without password again
/* if deserializing with password didnt work, try deserialize without password again */
if (!pTurnConnection->credentialObtained || STATUS_FAILED(retStatus)) {
CHK_STATUS(deserializeStunPacket(pBuffer, bufferLen, NULL, 0, &pStunPacket));
retStatus = STATUS_SUCCESS;
}

CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_ERROR_CODE, &pStunAttr));

CHK_WARN(pStunAttr != NULL, retStatus, "No error code attribute found in Stun Error response. Dropping Packet");
pStunAttributeErrorCode = (PStunAttributeErrorCode) pStunAttr;
DLOGW("Received STUN error response. Error type: 0x%02x, Error Code: %u. Error detail: %s.",
stunPacketType, pStunAttributeErrorCode->errorCode, pStunAttributeErrorCode->errorPhrase);

if (pStunAttributeErrorCode->errorCode == STUN_ERROR_UNAUTHORIZED) {
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_NONCE, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No Nonce attribute found in Allocate Error response. Dropping Packet");
pStunAttributeNonce = (PStunAttributeNonce) pStunAttr;
CHK_WARN(pStunAttributeNonce->attribute.length <= STUN_MAX_NONCE_LEN, retStatus, "Invalid Nonce found in Allocate Error response. Dropping Packet");
pTurnConnection->nonceLen = pStunAttributeNonce->attribute.length;
MEMCPY(pTurnConnection->turnNonce, pStunAttributeNonce->nonce, pTurnConnection->nonceLen);

CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_REALM, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No Realm attribute found in Allocate Error response. Dropping Packet");
pStunAttributeRealm = (PStunAttributeRealm) pStunAttr;
CHK_WARN(pStunAttributeRealm->attribute.length <= STUN_MAX_REALM_LEN, retStatus, "Invalid Realm found in Allocate Error response. Dropping Packet");
// pStunAttributeRealm->attribute.length does not include null terminator and pStunAttributeRealm->realm is not null terminated
STRNCPY(pTurnConnection->turnRealm, pStunAttributeRealm->realm, pStunAttributeRealm->attribute.length);
pTurnConnection->turnRealm[pStunAttributeRealm->attribute.length] = '\0';

pTurnConnection->credentialObtained = TRUE;

CHK_STATUS(turnConnectionUpdateNonce(pTurnConnection));

} else if (pStunAttributeErrorCode->errorCode == STUN_ERROR_STALE_NONCE) {
DLOGD("Updating stale nonce");
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_NONCE, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No Nonce attribute found in Refresh Error response. Dropping Packet");
pStunAttributeNonce = (PStunAttributeNonce) pStunAttr;
CHK_WARN(pStunAttributeNonce->attribute.length <= STUN_MAX_NONCE_LEN, retStatus, "Invalid Nonce found in Refresh Error response. Dropping Packet");
pTurnConnection->nonceLen = pStunAttributeNonce->attribute.length;
MEMCPY(pTurnConnection->turnNonce, pStunAttributeNonce->nonce, pTurnConnection->nonceLen);

CHK_STATUS(turnConnectionUpdateNonce(pTurnConnection));

switch (pStunAttributeErrorCode->errorCode) {
case STUN_ERROR_UNAUTHORIZED:
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_NONCE, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No Nonce attribute found in Allocate Error response. Dropping Packet");
pStunAttributeNonce = (PStunAttributeNonce) pStunAttr;
CHK_WARN(pStunAttributeNonce->attribute.length <= STUN_MAX_NONCE_LEN, retStatus, "Invalid Nonce found in Allocate Error response. Dropping Packet");
pTurnConnection->nonceLen = pStunAttributeNonce->attribute.length;
MEMCPY(pTurnConnection->turnNonce, pStunAttributeNonce->nonce, pTurnConnection->nonceLen);

CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_REALM, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No Realm attribute found in Allocate Error response. Dropping Packet");
pStunAttributeRealm = (PStunAttributeRealm) pStunAttr;
CHK_WARN(pStunAttributeRealm->attribute.length <= STUN_MAX_REALM_LEN, retStatus, "Invalid Realm found in Allocate Error response. Dropping Packet");
// pStunAttributeRealm->attribute.length does not include null terminator and pStunAttributeRealm->realm is not null terminated
STRNCPY(pTurnConnection->turnRealm, pStunAttributeRealm->realm, pStunAttributeRealm->attribute.length);
pTurnConnection->turnRealm[pStunAttributeRealm->attribute.length] = '\0';

pTurnConnection->credentialObtained = TRUE;

CHK_STATUS(turnConnectionUpdateNonce(pTurnConnection));
break;

case STUN_ERROR_STALE_NONCE:
DLOGD("Updating stale nonce");
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_NONCE, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No Nonce attribute found in Refresh Error response. Dropping Packet");
pStunAttributeNonce = (PStunAttributeNonce) pStunAttr;
CHK_WARN(pStunAttributeNonce->attribute.length <= STUN_MAX_NONCE_LEN, retStatus, "Invalid Nonce found in Refresh Error response. Dropping Packet");
pTurnConnection->nonceLen = pStunAttributeNonce->attribute.length;
MEMCPY(pTurnConnection->turnNonce, pStunAttributeNonce->nonce, pTurnConnection->nonceLen);

CHK_STATUS(turnConnectionUpdateNonce(pTurnConnection));
break;

default:
/* Remove peer for any other error */
DLOGW("Received STUN error response. Error type: 0x%02x, Error Code: %u. attribute len %u, Error detail: %s.",
stunPacketType, pStunAttributeErrorCode->errorCode, pStunAttributeErrorCode->attribute.length,
pStunAttributeErrorCode->errorPhrase);

/* Find TurnPeer using transaction Id, then mark it as failed */
doubleListGetHeadNode(pTurnConnection->turnPeerList, &pCurNode);
while (pCurNode != NULL) {
pTurnPeer = (PTurnPeer) pCurNode->data;
pCurNode = pCurNode->pNext;
if (transactionIdStoreHasId(pTurnPeer->pTransactionIdStore, pBuffer + STUN_PACKET_TRANSACTION_ID_OFFSET)) {
pTurnPeer->connectionState = TURN_PEER_CONN_STATE_FAILED;
/* break the loop */
pCurNode = NULL;
}
}
break;
}

CleanUp:
Expand Down Expand Up @@ -572,10 +593,8 @@ STATUS turnConnectionAddPeer(PTurnConnection pTurnConnection, PKvsIpAddress pPee
ENTERS();
STATUS retStatus = STATUS_SUCCESS;
PTurnPeer pTurnPeer = NULL;
BOOL locked = FALSE, duplicatedPeer = FALSE;
PDoubleListNode pCurNode = NULL;
UINT64 data;
UINT16 peerCount = 0;
BOOL locked = FALSE;
UINT32 peerCount = 0;

CHK(pTurnConnection != NULL && pPeerAddress != NULL, STATUS_NULL_ARG);
CHK(pTurnConnection->turnServer.ipAddress.family == pPeerAddress->family, STATUS_INVALID_ARG);
Expand All @@ -584,20 +603,11 @@ STATUS turnConnectionAddPeer(PTurnConnection pTurnConnection, PKvsIpAddress pPee
MUTEX_LOCK(pTurnConnection->lock);
locked = TRUE;

// check for duplicate
CHK_STATUS(doubleListGetHeadNode(pTurnConnection->turnPeerList, &pCurNode));
while (pCurNode != NULL) {
CHK_STATUS(doubleListGetNodeData(pCurNode, &data));
pCurNode = pCurNode->pNext;

pTurnPeer = (PTurnPeer) data;
if (isSameIpAddress(&pTurnPeer->address, pPeerAddress, TRUE)) {
duplicatedPeer = TRUE;
break;
}
peerCount++;
}
CHK(!duplicatedPeer, retStatus);
/* check for duplicate */
CHK(turnConnectionGetPeerWithIp(pTurnConnection, pPeerAddress) == NULL, retStatus);
CHK_STATUS(doubleListGetNodeCount(pTurnConnection->turnPeerList, &peerCount));
CHK_WARN(peerCount < DEFAULT_TURN_MAX_PEER_COUNT, STATUS_INVALID_OPERATION,
"Add peer failed. Max peer count reached");

pTurnPeer = (PTurnPeer) MEMCALLOC(1, SIZEOF(TurnPeer));
CHK(pTurnPeer != NULL, STATUS_NOT_ENOUGH_MEMORY);
Expand All @@ -606,11 +616,12 @@ STATUS turnConnectionAddPeer(PTurnConnection pTurnConnection, PKvsIpAddress pPee
pTurnPeer->connectionState = TURN_PEER_CONN_STATE_CREATE_PERMISSION;
pTurnPeer->address = *pPeerAddress;
pTurnPeer->xorAddress = *pPeerAddress;
pTurnPeer->channelNumber = peerCount + TURN_CHANNEL_BIND_CHANNEL_NUMBER_BASE;
/* safe to down cast because DEFAULT_TURN_MAX_PEER_COUNT is enforced */
pTurnPeer->channelNumber = (UINT16) peerCount + TURN_CHANNEL_BIND_CHANNEL_NUMBER_BASE;
pTurnPeer->permissionExpirationTime = INVALID_TIMESTAMP_VALUE;
pTurnPeer->ready = FALSE;

CHK_STATUS(xorIpAddress(&pTurnPeer->xorAddress, NULL)); // only work for IPv4 for now
CHK_STATUS(xorIpAddress(&pTurnPeer->xorAddress, NULL)); /* only work for IPv4 for now */
CHK_STATUS(createTransactionIdStore(DEFAULT_MAX_STORED_TRANSACTION_ID_COUNT, &pTurnPeer->pTransactionIdStore));

CHK_STATUS(doubleListInsertItemTail(pTurnConnection->turnPeerList, (UINT64) pTurnPeer));
Expand All @@ -634,9 +645,7 @@ STATUS turnConnectionAddPeer(PTurnConnection pTurnConnection, PKvsIpAddress pPee
STATUS turnConnectionSendData(PTurnConnection pTurnConnection, PBYTE pBuf, UINT32 bufLen, PKvsIpAddress pDestIp)
{
STATUS retStatus = STATUS_SUCCESS;
PDoubleListNode pCurNode = NULL;
UINT64 data;
PTurnPeer pTurnPeer = NULL, pSendPeer = NULL;
PTurnPeer pSendPeer = NULL;
UINT32 paddedDataLen = 0;
CHAR ipAddrStr[KVS_IP_ADDRESS_STRING_BUFFER_LEN];
BOOL locked = FALSE;
Expand All @@ -656,18 +665,7 @@ STATUS turnConnectionSendData(PTurnConnection pTurnConnection, PBYTE pBuf, UINT3
MUTEX_LOCK(pTurnConnection->lock);
locked = TRUE;

// find TurnPeer with pDestIp
CHK_STATUS(doubleListGetHeadNode(pTurnConnection->turnPeerList, &pCurNode));
while (pCurNode != NULL) {
CHK_STATUS(doubleListGetNodeData(pCurNode, &data));
pCurNode = pCurNode->pNext;

pTurnPeer = (PTurnPeer) data;
if (isSameIpAddress(&pTurnPeer->address, pDestIp, TRUE)) {
pSendPeer = pTurnPeer;
break;
}
}
pSendPeer = turnConnectionGetPeerWithIp(pTurnConnection, pDestIp);

MUTEX_UNLOCK(pTurnConnection->lock);
locked = FALSE;
Expand All @@ -677,21 +675,23 @@ STATUS turnConnectionSendData(PTurnConnection pTurnConnection, PBYTE pBuf, UINT3
DLOGV("Unable to send data through turn because peer with address %s:%u is not found",
ipAddrStr, KVS_GET_IP_ADDRESS_PORT(pDestIp));
CHK(FALSE, retStatus);
} else if (pSendPeer->connectionState == TURN_PEER_CONN_STATE_FAILED) {
CHK(FALSE, STATUS_TURN_CONNECTION_PEER_NOT_USABLE);
} else if (!pSendPeer->ready) {
DLOGV("Unable to send data through turn because turn channel is not established with peer with address %s:%u",
ipAddrStr, KVS_GET_IP_ADDRESS_PORT(pDestIp));
CHK(FALSE, retStatus);
}

// need to serialize send because every send load data into the same buffer pTurnConnection->sendDataBuffer
/* need to serialize send because every send load data into the same buffer pTurnConnection->sendDataBuffer */
MUTEX_LOCK(pTurnConnection->sendLock);
sendLocked = TRUE;

CHK(pTurnConnection->dataBufferSize - TURN_DATA_CHANNEL_SEND_OVERHEAD >= bufLen, STATUS_BUFFER_TOO_SMALL);

paddedDataLen = (UINT32) ROUND_UP(TURN_DATA_CHANNEL_SEND_OVERHEAD + bufLen, 4);

// generate data channel TURN message
/* generate data channel TURN message */
putInt16((PINT16) (pTurnConnection->sendDataBuffer), pSendPeer->channelNumber);
putInt16((PINT16) (pTurnConnection->sendDataBuffer + 2), (UINT16) bufLen);
MEMCPY(pTurnConnection->sendDataBuffer + TURN_DATA_CHANNEL_SEND_OVERHEAD, pBuf, bufLen);
Expand Down Expand Up @@ -1421,3 +1421,25 @@ PTurnPeer turnConnectionGetPeerWithChannelNumber(PTurnConnection pTurnConnection

return pTurnPeer;
}

PTurnPeer turnConnectionGetPeerWithIp(PTurnConnection pTurnConnection, PKvsIpAddress pKvsIpAddress)
{
PTurnPeer pTurnPeer = NULL, pCurrTurnPeer = NULL;
PDoubleListNode pCurNode = NULL;
UINT64 data;

doubleListGetHeadNode(pTurnConnection->turnPeerList, &pCurNode);
while (pCurNode != NULL) {
doubleListGetNodeData(pCurNode, &data);
pCurNode = pCurNode->pNext;

pCurrTurnPeer = (PTurnPeer) data;
if (isSameIpAddress(&pCurrTurnPeer->address, pKvsIpAddress, TRUE)) {
pTurnPeer = pCurrTurnPeer;
// Stop the loop iteration
pCurNode = NULL;
}
}

return pTurnPeer;
}
2 changes: 2 additions & 0 deletions src/source/Ice/TurnConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ extern "C" {
#define DEFAULT_TURN_MESSAGE_SEND_CHANNEL_DATA_BUFFER_LEN (10 * 1024)
#define DEFAULT_TURN_MESSAGE_RECV_CHANNEL_DATA_BUFFER_LEN (10 * 1024)
#define DEFAULT_TURN_CHANNEL_DATA_BUFFER_SIZE 128
#define DEFAULT_TURN_MAX_PEER_COUNT 16

// all turn channel numbers must be greater than 0x4000 and less than 0x7FFF
#define TURN_CHANNEL_BIND_CHANNEL_NUMBER_BASE (UINT16) 0x4000
Expand Down Expand Up @@ -206,6 +207,7 @@ STATUS turnConnectionHandleChannelData(PTurnConnection, PBYTE, UINT32, PTurnChan
STATUS turnConnectionHandleChannelDataTcpMode(PTurnConnection, PBYTE, UINT32, PTurnChannelData, PUINT32);

PTurnPeer turnConnectionGetPeerWithChannelNumber(PTurnConnection, UINT16);
PTurnPeer turnConnectionGetPeerWithIp(PTurnConnection, PKvsIpAddress);

#ifdef __cplusplus
}
Expand Down
2 changes: 1 addition & 1 deletion src/source/Stun/Stun.c
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,7 @@ STATUS deserializeStunPacket(PBYTE pStunBuffer, UINT32 bufferSize, PBYTE passwor

// Copy the padded error phrase
MEMCPY(pStunAttributeErrorCode->errorPhrase, ((PBYTE) pStunAttributeHeader + STUN_ATTRIBUTE_HEADER_LEN + STUN_ERROR_CODE_PACKET_ERROR_PHRASE_OFFSET),
pStunAttributeErrorCode->paddedLength);
pStunAttributeErrorCode->paddedLength - STUN_ERROR_CODE_PACKET_ERROR_PHRASE_OFFSET);
attributeSize = SIZEOF(StunAttributeErrorCode) + pStunAttributeErrorCode->paddedLength;

break;
Expand Down
30 changes: 30 additions & 0 deletions tst/StunFunctionalityTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,36 @@ TEST_F(StunFunctionalityTest, serializeDeserializeStunControlAttribute)
EXPECT_EQ(STATUS_SUCCESS, freeStunPacket(&pDeserializedPacket));
}

TEST_F(StunFunctionalityTest, deserializeStunErrorCode)
{
/**
* Error Code is 403. Error phrase is "Forbidden IP" without null terminator
*/
BYTE stunErrorBuffer[] = {0x01, 0x18, 0x00, 0x54, 0x21, 0x12, 0xa4, 0x42, 0xd8, 0x83, 0x1d, 0x97, 0x54, 0x68, 0xf1,
0xbc, 0xf2, 0xe3, 0x40, 0x96, 0x00, 0x09, 0x00, 0x10, 0x00, 0x00, 0x04, 0x03, 0x46, 0x6f,
0x72, 0x62, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x20, 0x49, 0x50, 0x80, 0x22, 0x00, 0x1a, 0x43,
0x6f, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x34, 0x2e, 0x35, 0x2e, 0x31, 0x2e, 0x31, 0x20, 0x27,
0x64, 0x61, 0x6e, 0x20, 0x45, 0x69, 0x64, 0x65, 0x72, 0x27, 0x20, 0x27, 0x00, 0x08, 0x00,
0x14, 0xba, 0xd4, 0xef, 0xe4, 0x0c, 0xa8, 0x6c, 0x6e, 0xc6, 0x10, 0xf1, 0x48, 0xaa, 0xc6,
0x8f, 0xe9, 0xb6, 0x25, 0x58, 0xd6, 0x80, 0x28, 0x00, 0x04, 0x0d, 0xc7, 0xfe, 0x7a};
BYTE turnKey[] = {0x69, 0xa8, 0xc7, 0xf7, 0x79, 0x72, 0x3c, 0x58, 0xae, 0xc4, 0xbd, 0xa3, 0x79, 0x1c, 0x02, 0xbd};
PStunPacket pStunPacket = NULL;
PStunAttributeErrorCode pStunAttributeErrorCode = NULL;
PStunAttributeHeader pStunAttr = NULL;

EXPECT_EQ(STATUS_SUCCESS, deserializeStunPacket(stunErrorBuffer, ARRAY_SIZE(stunErrorBuffer), turnKey,
MD5_DIGEST_LENGTH, &pStunPacket));

EXPECT_EQ(STATUS_SUCCESS, getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_ERROR_CODE, &pStunAttr));
EXPECT_TRUE(pStunAttr != NULL);
pStunAttributeErrorCode = (PStunAttributeErrorCode) pStunAttr;

EXPECT_EQ(pStunAttributeErrorCode->errorCode, 403);
EXPECT_EQ(0, STRCMP(pStunAttributeErrorCode->errorPhrase, "Forbidden IP"));

EXPECT_EQ(STATUS_SUCCESS, freeStunPacket(&pStunPacket));
}

}
}
}
Expand Down

0 comments on commit f7c9706

Please sign in to comment.