STATUS serializeStunPacket()

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;
}