in src/source/Stun/Stun.c [70:515]
STATUS serializeStunPacket(PStunPacket pStunPacket, PBYTE password, UINT32 passwordLen, BOOL generateMessageIntegrity, BOOL generateFingerprint,
PBYTE pBuffer, PUINT32 pSize)
{
ENTERS();
STATUS retStatus = STATUS_SUCCESS;
UINT32 i, encodedLen = 0, packetSize = 0, remaining = 0, crc32, hmacLen;
UINT16 size;
PBYTE pCurrentBufferPosition = pBuffer;
PStunAttributeHeader pStunAttributeHeader;
PStunAttributeAddress pStunAttributeAddress;
PStunAttributeUsername pStunAttributeUsername;
PStunAttributePriority pStunAttributePriority;
PStunAttributeLifetime pStunAttributeLifetime;
PStunAttributeChangeRequest pStunAttributeChangeRequest;
PStunAttributeRequestedTransport pStunAttributeRequestedTransport;
PStunAttributeRealm pStunAttributeRealm;
PStunAttributeNonce pStunAttributeNonce;
PStunAttributeErrorCode pStunAttributeErrorCode;
PStunAttributeIceControl pStunAttributeIceControl;
PStunAttributeData pStunAttributeData;
PStunAttributeChannelNumber pStunAttributeChannelNumber;
BOOL fingerprintFound = FALSE, messaageIntegrityFound = FALSE;
INT64 data64;
CHK(pStunPacket != NULL && (!generateMessageIntegrity || password != NULL) && pSize != NULL, STATUS_NULL_ARG);
CHK(password == NULL || passwordLen != 0, STATUS_INVALID_ARG);
CHK(pStunPacket->header.magicCookie == STUN_HEADER_MAGIC_COOKIE, STATUS_STUN_MAGIC_COOKIE_MISMATCH);
packetSize += STUN_HEADER_LEN;
if (pBuffer != NULL) {
// If the buffer is specified then we use the length
packetSize = *pSize;
// Set the remaining size first
remaining = packetSize;
CHK(remaining >= STUN_HEADER_LEN, STATUS_NOT_ENOUGH_MEMORY);
// Package the STUN packet header
putInt16((PINT16) (pCurrentBufferPosition), pStunPacket->header.stunMessageType);
pCurrentBufferPosition += STUN_HEADER_TYPE_LEN;
// Skip the length - it will be added at the end
pCurrentBufferPosition += STUN_HEADER_DATA_LEN;
putInt32((PINT32) pCurrentBufferPosition, STUN_HEADER_MAGIC_COOKIE);
pCurrentBufferPosition += STUN_HEADER_MAGIC_COOKIE_LEN;
MEMCPY(pCurrentBufferPosition, pStunPacket->header.transactionId, STUN_HEADER_TRANSACTION_ID_LEN);
pCurrentBufferPosition += STUN_HEADER_TRANSACTION_ID_LEN;
remaining -= STUN_HEADER_LEN;
}
for (i = 0; i < pStunPacket->attributesCount; i++) {
// Get the next attribute
pStunAttributeHeader = pStunPacket->attributeList[i];
// Set the encoded length to zero before iteration
encodedLen = 0;
switch (pStunAttributeHeader->type) {
case STUN_ATTRIBUTE_TYPE_MAPPED_ADDRESS:
case STUN_ATTRIBUTE_TYPE_XOR_MAPPED_ADDRESS:
case STUN_ATTRIBUTE_TYPE_RESPONSE_ADDRESS:
case STUN_ATTRIBUTE_TYPE_SOURCE_ADDRESS:
case STUN_ATTRIBUTE_TYPE_REFLECTED_FROM:
case STUN_ATTRIBUTE_TYPE_XOR_PEER_ADDRESS:
case STUN_ATTRIBUTE_TYPE_CHANGED_ADDRESS:
// Set the size before proceeding - this will get reset by the actual required size
encodedLen = remaining;
// TODO refactor this check, we have it for every attribute.
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
pStunAttributeAddress = (PStunAttributeAddress) pStunAttributeHeader;
CHK_STATUS(stunPackageIpAddr(&pStunPacket->header, (STUN_ATTRIBUTE_TYPE) pStunAttributeHeader->type, &pStunAttributeAddress->address,
pCurrentBufferPosition, &encodedLen));
break;
case STUN_ATTRIBUTE_TYPE_USERNAME:
pStunAttributeUsername = (PStunAttributeUsername) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + pStunAttributeUsername->paddedLength;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the user name
MEMCPY(pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN, pStunAttributeUsername + 1, pStunAttributeUsername->paddedLength);
}
break;
case STUN_ATTRIBUTE_TYPE_PRIORITY:
pStunAttributePriority = (PStunAttributePriority) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + STUN_ATTRIBUTE_PRIORITY_LEN;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the value
putInt32((PINT32) (pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN), pStunAttributePriority->priority);
}
break;
case STUN_ATTRIBUTE_TYPE_USE_CANDIDATE:
case STUN_ATTRIBUTE_TYPE_DONT_FRAGMENT:
encodedLen = STUN_ATTRIBUTE_HEADER_LEN;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
}
break;
case STUN_ATTRIBUTE_TYPE_LIFETIME:
pStunAttributeLifetime = (PStunAttributeLifetime) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + STUN_ATTRIBUTE_LIFETIME_LEN;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the value
putInt32((PINT32) (pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN), pStunAttributeLifetime->lifetime);
}
break;
case STUN_ATTRIBUTE_TYPE_CHANGE_REQUEST:
pStunAttributeChangeRequest = (PStunAttributeChangeRequest) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + STUN_ATTRIBUTE_CHANGE_REQUEST_FLAG_LEN;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the value
putInt32((PINT32) (pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN), pStunAttributeChangeRequest->changeFlag);
}
break;
case STUN_ATTRIBUTE_TYPE_REQUESTED_TRANSPORT:
pStunAttributeRequestedTransport = (PStunAttributeRequestedTransport) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + STUN_ATTRIBUTE_REQUESTED_TRANSPORT_PROTOCOL_LEN;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the value
MEMCPY(pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN, pStunAttributeRequestedTransport->protocol,
STUN_ATTRIBUTE_REQUESTED_TRANSPORT_PROTOCOL_LEN);
}
break;
case STUN_ATTRIBUTE_TYPE_REALM:
pStunAttributeRealm = (PStunAttributeRealm) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + pStunAttributeRealm->paddedLength;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the realm
MEMCPY(pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN, pStunAttributeRealm->realm, pStunAttributeRealm->paddedLength);
}
break;
case STUN_ATTRIBUTE_TYPE_NONCE:
pStunAttributeNonce = (PStunAttributeNonce) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + pStunAttributeNonce->paddedLength;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the nonce
MEMCPY(pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN, pStunAttributeNonce->nonce, pStunAttributeNonce->paddedLength);
}
break;
case STUN_ATTRIBUTE_TYPE_ERROR_CODE:
pStunAttributeErrorCode = (PStunAttributeErrorCode) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + pStunAttributeErrorCode->paddedLength;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the error code
putInt16((PINT16) pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN, pStunAttributeErrorCode->errorCode);
// Package the error phrase
MEMCPY(pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN + SIZEOF(pStunAttributeErrorCode->errorCode),
pStunAttributeErrorCode->errorPhrase, pStunAttributeErrorCode->paddedLength);
}
break;
case STUN_ATTRIBUTE_TYPE_ICE_CONTROLLED:
case STUN_ATTRIBUTE_TYPE_ICE_CONTROLLING:
pStunAttributeIceControl = (PStunAttributeIceControl) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + STUN_ATTRIBUTE_ICE_CONTROL_LEN;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the value
MEMCPY(&data64, (PBYTE) pStunAttributeIceControl + STUN_ATTRIBUTE_HEADER_LEN, SIZEOF(INT64));
putInt64(&data64, data64);
MEMCPY(pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN, &data64, SIZEOF(INT64));
}
break;
case STUN_ATTRIBUTE_TYPE_DATA:
pStunAttributeData = (PStunAttributeData) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + pStunAttributeData->paddedLength;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the nonce
MEMCPY(pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN, pStunAttributeData->data, pStunAttributeData->paddedLength);
}
break;
case STUN_ATTRIBUTE_TYPE_CHANNEL_NUMBER:
pStunAttributeChannelNumber = (PStunAttributeChannelNumber) pStunAttributeHeader;
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + STUN_ATTRIBUTE_CHANNEL_NUMBER_LEN;
CHK(!fingerprintFound && !messaageIntegrityFound, STATUS_STUN_ATTRIBUTES_AFTER_FINGERPRINT_MESSAGE_INTEGRITY);
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the message header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, pStunAttributeHeader->type, pStunAttributeHeader->length);
// Package the value
putInt16((PINT16) (pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN), pStunAttributeChannelNumber->channelNumber);
putInt16((PINT16) (pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN + SIZEOF(INT16)), pStunAttributeChannelNumber->reserve);
}
break;
case STUN_ATTRIBUTE_TYPE_MESSAGE_INTEGRITY:
// Validate that the integrity is the last one or comes before fingerprint and ignore the attribute
CHK(i == pStunPacket->attributesCount - 1 || i == pStunPacket->attributesCount - 2, STATUS_STUN_MESSAGE_INTEGRITY_NOT_LAST);
CHK(!messaageIntegrityFound, STATUS_STUN_MULTIPLE_MESSAGE_INTEGRITY_ATTRIBUTES);
CHK(!fingerprintFound, STATUS_STUN_MESSAGE_INTEGRITY_AFTER_FINGERPRINT);
messaageIntegrityFound = TRUE;
break;
case STUN_ATTRIBUTE_TYPE_FINGERPRINT:
// Validate that the fingerprint is the last and ignore the attribute
CHK(i == pStunPacket->attributesCount - 1, STATUS_STUN_FINGERPRINT_NOT_LAST);
CHK(!fingerprintFound, STATUS_STUN_MULTIPLE_FINGERPRINT_ATTRIBUTES);
fingerprintFound = TRUE;
break;
default:
// Do nothing
break;
}
if (pBuffer != NULL) {
// Advance the current ptr needed
pCurrentBufferPosition += encodedLen;
// Decrement the remaining size
remaining -= encodedLen;
} else {
// Increment the overall package size
packetSize += encodedLen;
}
}
// Check if we need to generate the message integrity attribute
if (generateMessageIntegrity) {
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + STUN_HMAC_VALUE_LEN;
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, STUN_ATTRIBUTE_TYPE_MESSAGE_INTEGRITY, STUN_HMAC_VALUE_LEN);
// Fix-up the packet length with message integrity and without the STUN header
size = (UINT16) (pCurrentBufferPosition + encodedLen - pBuffer - STUN_HEADER_LEN);
putInt16((PINT16) (pBuffer + STUN_HEADER_TYPE_LEN), size);
// The size of the message size in bytes should be a multiple of 64 per rfc
// CHK((size & 0x003f) == 0, STATUS_WEBRTC_STUN_MESSAGE_INTEGRITY_SIZE_ALIGNMENT);
// Calculate the HMAC for the integrity of the packet including STUN header and excluding the integrity attribute
size = (UINT16) (pCurrentBufferPosition - pBuffer);
KVS_SHA1_HMAC(password, (INT32) passwordLen, pBuffer, size, pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN, &hmacLen);
// Advance the current position
pCurrentBufferPosition += encodedLen;
// Decrement the remaining size
remaining -= encodedLen;
} else {
packetSize += encodedLen;
}
}
// Check if we need to generate the fingerprint attribute
if (generateFingerprint) {
encodedLen = STUN_ATTRIBUTE_HEADER_LEN + STUN_ATTRIBUTE_FINGERPRINT_LEN;
if (pBuffer != NULL) {
CHK(remaining >= encodedLen, STATUS_NOT_ENOUGH_MEMORY);
// Package the header first
PACKAGE_STUN_ATTR_HEADER(pCurrentBufferPosition, STUN_ATTRIBUTE_TYPE_FINGERPRINT, STUN_ATTRIBUTE_FINGERPRINT_LEN);
// Fix-up the packet length with message integrity and without the STUN header
size = (UINT16) (pCurrentBufferPosition + encodedLen - pBuffer - STUN_HEADER_LEN);
putInt16((PINT16) (pBuffer + STUN_HEADER_TYPE_LEN), size);
// Calculate the fingerprint including STUN header and excluding the fingerprint attribute
size = (UINT16) (pCurrentBufferPosition - pBuffer);
crc32 = COMPUTE_CRC32(pBuffer, (UINT32) size) ^ STUN_FINGERPRINT_ATTRIBUTE_XOR_VALUE;
// Write out the CRC value
putInt32((PINT32) (pCurrentBufferPosition + STUN_ATTRIBUTE_HEADER_LEN), crc32);
// Advance the current position
pCurrentBufferPosition += encodedLen;
// Decrement the remaining size
remaining -= encodedLen;
} else {
packetSize += encodedLen;
}
}
// Package the length if buffer is not NULL
if (pBuffer != NULL) {
encodedLen = (UINT16) (packetSize - STUN_HEADER_LEN);
putInt16((PINT16) (pBuffer + STUN_HEADER_TYPE_LEN), (UINT16) encodedLen);
}
// Validate the overall size if buffer is specified
CHK_ERR(pBuffer == NULL || packetSize == (UINT32) (pCurrentBufferPosition - pBuffer), STATUS_INTERNAL_ERROR,
"Internal error: Invalid offset calculation.");
CleanUp:
if (STATUS_SUCCEEDED(retStatus) && pSize != NULL) {
*pSize = packetSize;
}
LEAVES();
return retStatus;
}