in src/source/Ice/IceAgent.c [2358:2566]
STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PSocketConnection pSocketConnection, PKvsIpAddress pSrcAddr,
PKvsIpAddress pDestAddr)
{
UNUSED_PARAM(pDestAddr);
STATUS retStatus = STATUS_SUCCESS;
PStunPacket pStunPacket = NULL, pStunResponse = NULL;
PStunAttributeHeader pStunAttr = NULL;
UINT16 stunPacketType = 0;
PIceCandidatePair pIceCandidatePair = NULL;
PStunAttributeAddress pStunAttributeAddress = NULL;
PStunAttributePriority pStunAttributePriority = NULL;
UINT32 priority = 0;
PIceCandidate pIceCandidate = NULL;
CHAR ipAddrStr[KVS_IP_ADDRESS_STRING_BUFFER_LEN], ipAddrStr2[KVS_IP_ADDRESS_STRING_BUFFER_LEN];
PCHAR hexStr = NULL;
UINT32 hexStrLen = 0, checkSum = 0;
UINT64 requestSentTime = 0;
UINT64 connectivityCheckRequestsReceived = 0;
UINT64 connectivityCheckResponsesSent = 0;
UINT64 connectivityCheckResponsesReceived = 0;
// need to determine stunPacketType before deserializing because different password should be used depending on the packet type
stunPacketType = (UINT16) getInt16(*((PUINT16) pBuffer));
switch (stunPacketType) {
case STUN_PACKET_TYPE_BINDING_REQUEST:
connectivityCheckRequestsReceived++;
CHK_STATUS(deserializeStunPacket(pBuffer, bufferLen, (PBYTE) pIceAgent->localPassword,
(UINT32) STRLEN(pIceAgent->localPassword) * SIZEOF(CHAR), &pStunPacket));
CHK_STATUS(createStunPacket(STUN_PACKET_TYPE_BINDING_RESPONSE_SUCCESS, pStunPacket->header.transactionId, &pStunResponse));
CHK_STATUS(appendStunAddressAttribute(pStunResponse, STUN_ATTRIBUTE_TYPE_XOR_MAPPED_ADDRESS, pSrcAddr));
CHK_STATUS(appendStunIceControllAttribute(
pStunResponse, pIceAgent->isControlling ? STUN_ATTRIBUTE_TYPE_ICE_CONTROLLING : STUN_ATTRIBUTE_TYPE_ICE_CONTROLLED,
pIceAgent->tieBreaker));
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_PRIORITY, (PStunAttributeHeader*) &pStunAttributePriority));
priority = pStunAttributePriority == NULL ? 0 : pStunAttributePriority->priority;
CHK_STATUS(iceAgentCheckPeerReflexiveCandidate(pIceAgent, pSrcAddr, priority, TRUE, 0));
CHK_STATUS(findCandidateWithSocketConnection(pSocketConnection, pIceAgent->localCandidates, &pIceCandidate));
CHK_WARN(pIceCandidate != NULL, retStatus, "Could not find local candidate to send STUN response");
CHK_STATUS(iceAgentSendStunPacket(pStunResponse, (PBYTE) pIceAgent->localPassword,
(UINT32) STRLEN(pIceAgent->localPassword) * SIZEOF(CHAR), pIceAgent, pIceCandidate, pSrcAddr));
connectivityCheckResponsesSent++;
// return early if there is no candidate pair. This can happen when we get connectivity check from the peer
// before we receive the answer.
CHK_STATUS(findIceCandidatePairWithLocalSocketConnectionAndRemoteAddr(pIceAgent, pSocketConnection, pSrcAddr, TRUE, &pIceCandidatePair));
CHK(pIceCandidatePair != NULL, retStatus);
if (!pIceCandidatePair->nominated) {
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_USE_CANDIDATE, &pStunAttr));
if (pStunAttr != NULL) {
DLOGD("received candidate with USE_CANDIDATE flag, local candidate type %s.",
iceAgentGetCandidateTypeStr(pIceCandidatePair->local->iceCandidateType));
pIceCandidatePair->nominated = TRUE;
}
}
// schedule a connectivity check for the pair
if (pIceCandidatePair->state == ICE_CANDIDATE_PAIR_STATE_FROZEN || pIceCandidatePair->state == ICE_CANDIDATE_PAIR_STATE_WAITING ||
pIceCandidatePair->state == ICE_CANDIDATE_PAIR_STATE_IN_PROGRESS) {
CHK_STATUS(stackQueueEnqueue(pIceAgent->triggeredCheckQueue, (UINT64) pIceCandidatePair));
}
if (pIceCandidatePair == pIceAgent->pDataSendingIceCandidatePair) {
pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.requestsReceived += connectivityCheckRequestsReceived;
pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.responsesSent += connectivityCheckResponsesSent;
pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.nominated = pIceCandidatePair->nominated;
}
break;
case STUN_PACKET_TYPE_BINDING_RESPONSE_SUCCESS:
connectivityCheckResponsesReceived++;
checkSum = COMPUTE_CRC32(pBuffer + STUN_PACKET_TRANSACTION_ID_OFFSET, STUN_TRANSACTION_ID_LEN);
// check if Binding Response is for finding srflx candidate
if (transactionIdStoreHasId(pIceAgent->pStunBindingRequestTransactionIdStore, pBuffer + STUN_PACKET_TRANSACTION_ID_OFFSET)) {
CHK_STATUS(findCandidateWithSocketConnection(pSocketConnection, pIceAgent->localCandidates, &pIceCandidate));
CHK_WARN(pIceCandidate != NULL, retStatus, "Local candidate with socket %d not found. Dropping STUN binding success response",
pSocketConnection->localSocket);
// Update round trip time for serial reflexive candidate
pIceAgent->rtcIceServerDiagnostics[pIceCandidate->iceServerIndex].totalResponsesReceived++;
retStatus = hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime);
if (retStatus != STATUS_SUCCESS) {
DLOGW("Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x)", retStatus);
} else {
pIceAgent->rtcIceServerDiagnostics[pIceCandidate->iceServerIndex].totalRoundTripTime += GETTIME() - requestSentTime;
CHK_STATUS(hashTableRemove(pIceAgent->requestTimestampDiagnostics, checkSum));
}
CHK_STATUS(deserializeStunPacket(pBuffer, bufferLen, NULL, 0, &pStunPacket));
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_XOR_MAPPED_ADDRESS, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No mapped address attribute found in STUN binding response. Dropping Packet");
pStunAttributeAddress = (PStunAttributeAddress) pStunAttr;
// Update the server reflexive address which later will be picked up by the timer callback
CHK_STATUS(updateCandidateAddress(pIceCandidate, &pStunAttributeAddress->address));
// Remove from the transaction id store as we no longer are awaiting for the bind response
transactionIdStoreRemove(pIceAgent->pStunBindingRequestTransactionIdStore, pBuffer + STUN_PACKET_TRANSACTION_ID_OFFSET);
CHK(FALSE, retStatus);
}
CHK_STATUS(findIceCandidatePairWithLocalSocketConnectionAndRemoteAddr(pIceAgent, pSocketConnection, pSrcAddr, TRUE, &pIceCandidatePair));
if (pIceCandidatePair == NULL) {
CHK_STATUS(getIpAddrStr(pSrcAddr, ipAddrStr, ARRAY_SIZE(ipAddrStr)));
CHK_STATUS(getIpAddrStr(&pSocketConnection->hostIpAddr, ipAddrStr2, ARRAY_SIZE(ipAddrStr2)));
CHK_WARN(FALSE, retStatus,
"Cannot find candidate pair with local candidate %s and remote candidate %s. Dropping STUN binding success response",
ipAddrStr2, ipAddrStr);
}
retStatus = hashTableGet(pIceCandidatePair->requestSentTime, checkSum, &requestSentTime);
if (retStatus != STATUS_SUCCESS) {
DLOGW("Unable to fetch request Timestamp from the hash table. No update to RTT for the pair (error code: 0x%08x)", retStatus);
} else {
pIceCandidatePair->roundTripTime = GETTIME() - requestSentTime;
pIceCandidatePair->rtcIceCandidatePairDiagnostics.currentRoundTripTime =
(DOUBLE) (pIceCandidatePair->roundTripTime) / HUNDREDS_OF_NANOS_IN_A_SECOND;
}
CHK_WARN(transactionIdStoreHasId(pIceCandidatePair->pTransactionIdStore, pBuffer + STUN_PACKET_TRANSACTION_ID_OFFSET), retStatus,
"Dropping response packet because transaction id does not match");
// Update round trip time and responses received only for relay candidates.
if (pIceCandidatePair->local->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED) {
pIceAgent->rtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex].totalResponsesReceived++;
retStatus = hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime);
if (retStatus != STATUS_SUCCESS) {
DLOGW("Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x)", retStatus);
} else {
pIceAgent->rtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex].totalRoundTripTime += GETTIME() - requestSentTime;
CHK_STATUS(hashTableRemove(pIceAgent->requestTimestampDiagnostics, checkSum));
}
}
CHK_STATUS(deserializeStunPacket(pBuffer, bufferLen, (PBYTE) pIceAgent->remotePassword,
(UINT32) STRLEN(pIceAgent->remotePassword) * SIZEOF(CHAR), &pStunPacket));
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_XOR_MAPPED_ADDRESS, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No mapped address attribute found in STUN response. Dropping Packet");
pStunAttributeAddress = (PStunAttributeAddress) pStunAttr;
if (!isSameIpAddress(&pStunAttributeAddress->address, &pIceCandidatePair->local->ipAddress, FALSE)) {
// this can happen for host and server reflexive candidates. If the peer
// is in the same subnet, server reflexive candidate's binding response's xor mapped ip address will be
// the host candidate ip address. In this case we will ignore the packet since the host candidate will
// be getting its own response for the connection check.
DLOGD("local candidate ip address does not match with xor mapped address in binding response");
// we have a peer reflexive local candidate
CHK_STATUS(iceAgentCheckPeerReflexiveCandidate(pIceAgent, &pStunAttributeAddress->address, pIceCandidatePair->local->priority, FALSE,
pSocketConnection));
CHK(FALSE, retStatus);
}
if (pIceCandidatePair->state != ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) {
pIceCandidatePair->state = ICE_CANDIDATE_PAIR_STATE_SUCCEEDED;
retStatus = hashTableGet(pIceCandidatePair->requestSentTime, checkSum, &requestSentTime);
if (retStatus != STATUS_SUCCESS) {
DLOGW("Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x)", retStatus);
} else {
pIceCandidatePair->roundTripTime = GETTIME() - requestSentTime;
DLOGD("Ice candidate pair %s_%s is connected. Round trip time: %" PRIu64 "ms", pIceCandidatePair->local->id,
pIceCandidatePair->remote->id, pIceCandidatePair->roundTripTime / HUNDREDS_OF_NANOS_IN_A_MILLISECOND);
pIceCandidatePair->rtcIceCandidatePairDiagnostics.totalRoundTripTime +=
(DOUBLE) (pIceCandidatePair->roundTripTime) / HUNDREDS_OF_NANOS_IN_A_SECOND;
CHK_STATUS(hashTableRemove(pIceCandidatePair->requestSentTime, checkSum));
}
}
pIceCandidatePair->rtcIceCandidatePairDiagnostics.responsesReceived += connectivityCheckResponsesReceived;
pIceCandidatePair->rtcIceCandidatePairDiagnostics.lastResponseTimestamp = GETTIME();
break;
case STUN_PACKET_TYPE_BINDING_INDICATION:
DLOGD("Received STUN binding indication");
break;
default:
CHK_STATUS(hexEncode(pBuffer, bufferLen, NULL, &hexStrLen));
hexStr = MEMCALLOC(1, hexStrLen * SIZEOF(CHAR));
CHK(hexStr != NULL, STATUS_NOT_ENOUGH_MEMORY);
CHK_STATUS(hexEncode(pBuffer, bufferLen, hexStr, &hexStrLen));
DLOGW("Dropping unrecognized STUN packet. Packet type: 0x%02x. Packet content: \n\t%s", stunPacketType, hexStr);
SAFE_MEMFREE(hexStr);
break;
}
CleanUp:
CHK_LOG_ERR(retStatus);
SAFE_MEMFREE(hexStr);
if (pStunPacket != NULL) {
freeStunPacket(&pStunPacket);
}
if (pStunResponse != NULL) {
freeStunPacket(&pStunResponse);
}
// TODO send error packet
return retStatus;
}