in src/source/Ice/TurnConnection.c [192:332]
STATUS turnConnectionHandleStun(PTurnConnection pTurnConnection, PBYTE pBuffer, UINT32 bufferLen)
{
ENTERS();
STATUS retStatus = STATUS_SUCCESS;
UINT16 stunPacketType = 0;
PStunAttributeHeader pStunAttr = NULL;
PStunAttributeAddress pStunAttributeAddress = NULL;
PStunAttributeLifetime pStunAttributeLifetime = NULL;
PStunPacket pStunPacket = NULL;
CHAR ipAddrStr[KVS_IP_ADDRESS_STRING_BUFFER_LEN];
BOOL locked = FALSE;
ATOMIC_BOOL hasAllocation = FALSE;
PTurnPeer pTurnPeer = NULL;
UINT64 currentTime;
UINT32 i;
CHK(pTurnConnection != NULL, STATUS_NULL_ARG);
CHK(pBuffer != NULL && bufferLen > 0, STATUS_INVALID_ARG);
CHK(IS_STUN_PACKET(pBuffer) && !STUN_PACKET_IS_TYPE_ERROR(pBuffer), retStatus);
MUTEX_LOCK(pTurnConnection->lock);
locked = TRUE;
currentTime = GETTIME();
// only handling STUN response
stunPacketType = (UINT16) getInt16(*((PUINT16) pBuffer));
switch (stunPacketType) {
case STUN_PACKET_TYPE_ALLOCATE_SUCCESS_RESPONSE:
/* If shutdown has been initiated, ignore the allocation response */
CHK(!ATOMIC_LOAD(&pTurnConnection->stopTurnConnection), retStatus);
CHK_STATUS(deserializeStunPacket(pBuffer, bufferLen, pTurnConnection->longTermKey, KVS_MD5_DIGEST_LENGTH, &pStunPacket));
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_XOR_RELAYED_ADDRESS, &pStunAttr));
CHK_WARN(pStunAttr != NULL, retStatus, "No relay address attribute found in TURN allocate response. Dropping Packet");
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_LIFETIME, (PStunAttributeHeader*) &pStunAttributeLifetime));
CHK_WARN(pStunAttributeLifetime != NULL, retStatus, "Missing lifetime in Allocation response. Dropping Packet");
// convert lifetime to 100ns and store it
pTurnConnection->allocationExpirationTime = (pStunAttributeLifetime->lifetime * HUNDREDS_OF_NANOS_IN_A_SECOND) + currentTime;
DLOGD("TURN Allocation succeeded. Life time: %u seconds. Allocation expiration epoch %" PRIu64, pStunAttributeLifetime->lifetime,
pTurnConnection->allocationExpirationTime / DEFAULT_TIME_UNIT_IN_NANOS);
pStunAttributeAddress = (PStunAttributeAddress) pStunAttr;
pTurnConnection->relayAddress = pStunAttributeAddress->address;
ATOMIC_STORE_BOOL(&pTurnConnection->hasAllocation, TRUE);
if (!pTurnConnection->relayAddressReported && pTurnConnection->turnConnectionCallbacks.relayAddressAvailableFn != NULL) {
pTurnConnection->relayAddressReported = TRUE;
// release lock early and report relay candidate
MUTEX_UNLOCK(pTurnConnection->lock);
locked = FALSE;
pTurnConnection->turnConnectionCallbacks.relayAddressAvailableFn(pTurnConnection->turnConnectionCallbacks.customData,
&pTurnConnection->relayAddress, pTurnConnection->pControlChannel);
}
break;
case STUN_PACKET_TYPE_REFRESH_SUCCESS_RESPONSE:
CHK_STATUS(deserializeStunPacket(pBuffer, bufferLen, pTurnConnection->longTermKey, KVS_MD5_DIGEST_LENGTH, &pStunPacket));
CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_LIFETIME, (PStunAttributeHeader*) &pStunAttributeLifetime));
CHK_WARN(pStunAttributeLifetime != NULL, retStatus, "No lifetime attribute found in TURN refresh response. Dropping Packet");
if (pStunAttributeLifetime->lifetime == 0) {
hasAllocation = ATOMIC_EXCHANGE_BOOL(&pTurnConnection->hasAllocation, FALSE);
CHK(hasAllocation, retStatus);
DLOGD("TURN Allocation freed.");
CVAR_SIGNAL(pTurnConnection->freeAllocationCvar);
} else {
// convert lifetime to 100ns and store it
pTurnConnection->allocationExpirationTime = (pStunAttributeLifetime->lifetime * HUNDREDS_OF_NANOS_IN_A_SECOND) + currentTime;
DLOGD("Refreshed TURN allocation lifetime is %u seconds. Allocation expiration epoch %" PRIu64, pStunAttributeLifetime->lifetime,
pTurnConnection->allocationExpirationTime / DEFAULT_TIME_UNIT_IN_NANOS);
}
break;
case STUN_PACKET_TYPE_CREATE_PERMISSION_SUCCESS_RESPONSE:
for (i = 0; i < pTurnConnection->turnPeerCount; ++i) {
pTurnPeer = &pTurnConnection->turnPeerList[i];
if (transactionIdStoreHasId(pTurnPeer->pTransactionIdStore, pBuffer + STUN_PACKET_TRANSACTION_ID_OFFSET)) {
if (pTurnPeer->connectionState == TURN_PEER_CONN_STATE_CREATE_PERMISSION) {
pTurnPeer->connectionState = TURN_PEER_CONN_STATE_BIND_CHANNEL;
CHK_STATUS(getIpAddrStr(&pTurnPeer->address, ipAddrStr, ARRAY_SIZE(ipAddrStr)));
DLOGD("create permission succeeded for peer %s", ipAddrStr);
}
pTurnPeer->permissionExpirationTime = TURN_PERMISSION_LIFETIME + currentTime;
}
}
break;
case STUN_PACKET_TYPE_CHANNEL_BIND_SUCCESS_RESPONSE:
for (i = 0; i < pTurnConnection->turnPeerCount; ++i) {
pTurnPeer = &pTurnConnection->turnPeerList[i];
if (pTurnPeer->connectionState == TURN_PEER_CONN_STATE_BIND_CHANNEL &&
transactionIdStoreHasId(pTurnPeer->pTransactionIdStore, pBuffer + STUN_PACKET_TRANSACTION_ID_OFFSET)) {
// pTurnPeer->ready means this peer is ready to receive data. pTurnPeer->connectionState could
// change after reaching TURN_PEER_CONN_STATE_READY due to refreshing permission and channel.
if (!pTurnPeer->ready) {
pTurnPeer->ready = TRUE;
}
pTurnPeer->connectionState = TURN_PEER_CONN_STATE_READY;
CHK_STATUS(getIpAddrStr(&pTurnPeer->address, ipAddrStr, ARRAY_SIZE(ipAddrStr)));
DLOGD("Channel bind succeeded with peer %s, port: %u, channel number %u", ipAddrStr, (UINT16) getInt16(pTurnPeer->address.port),
pTurnPeer->channelNumber);
break;
}
}
break;
case STUN_PACKET_TYPE_DATA_INDICATION:
DLOGD("Received data indication");
// no-ops for now, turn server has to use data channel if it is established. client is free to use either.
break;
default:
DLOGD("Drop unsupported TURN message with type 0x%02x", stunPacketType);
break;
}
CleanUp:
CHK_LOG_ERR(retStatus);
if (locked) {
MUTEX_UNLOCK(pTurnConnection->lock);
}
if (pStunPacket != NULL) {
freeStunPacket(&pStunPacket);
}
LEAVES();
return retStatus;
}