_static uint32_t prvParseDNSReply()

in FreeRTOS_DNS.c [1168:1569]


    _static uint32_t prvParseDNSReply( uint8_t * pucUDPPayloadBuffer,
                                       size_t uxBufferLength,
                                       BaseType_t xExpected )
    {
        DNSMessage_t * pxDNSMessageHeader;
        /* This pointer is not used to modify anything */
        const DNSAnswerRecord_t * pxDNSAnswerRecord;
        uint32_t ulIPAddress = 0U;

        #if ( ipconfigUSE_LLMNR == 1 )
            char * pcRequestedName = NULL;
        #endif
        uint8_t * pucByte;
        size_t uxSourceBytesRemaining;
        uint16_t x, usDataLength, usQuestions;
        uint16_t usType = 0U;
        BaseType_t xReturn = pdTRUE;
        /* memcpy() helper variables for MISRA Rule 21.15 compliance*/
        const void * pvCopySource;
        void * pvCopyDest;

        #if ( ipconfigUSE_LLMNR == 1 )
            uint16_t usClass = 0U;
        #endif
        #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
            BaseType_t xDoStore = xExpected;
            char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ] = "";
        #endif
        const size_t uxAddressLength = ipSIZE_OF_IPv4_ADDRESS;

        /* Ensure that the buffer is of at least minimal DNS message length. */
        if( uxBufferLength < sizeof( DNSMessage_t ) )
        {
            xReturn = pdFALSE;
        }
        else
        {
            uxSourceBytesRemaining = uxBufferLength;

            /* Parse the DNS message header. Map the byte stream onto a structure
             * for easier access. */
            pxDNSMessageHeader = ipCAST_PTR_TO_TYPE_PTR( DNSMessage_t, pucUDPPayloadBuffer );

            /* Introduce a do {} while (0) to allow the use of breaks. */
            do
            {
                size_t uxBytesRead = 0U;
                size_t uxResult;

                /* Start at the first byte after the header. */
                pucByte = &( pucUDPPayloadBuffer[ sizeof( DNSMessage_t ) ] );
                uxSourceBytesRemaining -= sizeof( DNSMessage_t );

                /* Skip any question records. */
                usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );

                for( x = 0U; x < usQuestions; x++ )
                {
                    #if ( ipconfigUSE_LLMNR == 1 )
                        {
                            if( x == 0U )
                            {
                                pcRequestedName = ( char * ) pucByte;
                            }
                        }
                    #endif

                    #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
                        if( x == 0U )
                        {
                            uxResult = prvReadNameField( pucByte,
                                                         uxSourceBytesRemaining,
                                                         pcName,
                                                         sizeof( pcName ) );

                            /* Check for a malformed response. */
                            if( uxResult == 0U )
                            {
                                xReturn = pdFALSE;
                                break;
                            }

                            uxBytesRead += uxResult;
                            pucByte = &( pucByte[ uxResult ] );
                            uxSourceBytesRemaining -= uxResult;
                        }
                        else
                    #endif /* ipconfigUSE_DNS_CACHE || ipconfigDNS_USE_CALLBACKS */
                    {
                        /* Skip the variable length pcName field. */
                        uxResult = prvSkipNameField( pucByte,
                                                     uxSourceBytesRemaining );

                        /* Check for a malformed response. */
                        if( uxResult == 0U )
                        {
                            xReturn = pdFALSE;
                            break;
                        }

                        uxBytesRead += uxResult;
                        pucByte = &( pucByte[ uxResult ] );
                        uxSourceBytesRemaining -= uxResult;
                    }

                    /* Check the remaining buffer size. */
                    if( uxSourceBytesRemaining >= sizeof( uint32_t ) )
                    {
                        #if ( ipconfigUSE_LLMNR == 1 )
                            {
                                /* usChar2u16 returns value in host endianness. */
                                usType = usChar2u16( pucByte );
                                usClass = usChar2u16( &( pucByte[ 2 ] ) );
                            }
                        #endif /* ipconfigUSE_LLMNR */

                        /* Skip the type and class fields. */
                        pucByte = &( pucByte[ sizeof( uint32_t ) ] );
                        uxSourceBytesRemaining -= sizeof( uint32_t );
                    }
                    else
                    {
                        xReturn = pdFALSE;
                        break;
                    }
                }

                if( xReturn == pdFALSE )
                {
                    /* No need to proceed. Break out of the do-while loop. */
                    break;
                }

                /* Search through the answer records. */
                pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );

                if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
                {
                    const uint16_t usCount = ( uint16_t ) ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY;
                    uint16_t usNumARecordsStored = 0;

                    for( x = 0U; x < pxDNSMessageHeader->usAnswers; x++ )
                    {
                        BaseType_t xDoAccept;

                        if( usNumARecordsStored >= usCount )
                        {
                            /* Only count ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY number of records. */
                            break;
                        }

                        uxResult = prvSkipNameField( pucByte,
                                                     uxSourceBytesRemaining );

                        /* Check for a malformed response. */
                        if( uxResult == 0U )
                        {
                            xReturn = pdFALSE;
                            break;
                        }

                        uxBytesRead += uxResult;
                        pucByte = &( pucByte[ uxResult ] );
                        uxSourceBytesRemaining -= uxResult;

                        /* Is there enough data for an IPv4 A record answer and, if so,
                         * is this an A record? */
                        if( uxSourceBytesRemaining < sizeof( uint16_t ) )
                        {
                            xReturn = pdFALSE;
                            break;
                        }

                        usType = usChar2u16( pucByte );

                        if( usType == ( uint16_t ) dnsTYPE_A_HOST )
                        {
                            if( uxSourceBytesRemaining >= ( sizeof( DNSAnswerRecord_t ) + uxAddressLength ) )
                            {
                                xDoAccept = pdTRUE;
                            }
                            else
                            {
                                xDoAccept = pdFALSE;
                            }
                        }
                        else
                        {
                            /* Unknown host type. */
                            xDoAccept = pdFALSE;
                        }

                        if( xDoAccept != pdFALSE )
                        {
                            /* This is the required record type and is of sufficient size. */

                            /* Mapping pucByte to a DNSAnswerRecord allows easy access of the
                             * fields of the structure. */
                            pxDNSAnswerRecord = ipCAST_PTR_TO_TYPE_PTR( DNSAnswerRecord_t, pucByte );

                            /* Sanity check the data length of an IPv4 answer. */
                            if( FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ) == ( uint16_t ) uxAddressLength )
                            {
                                /* Copy the IP address out of the record. Using different pointers
                                 * to copy only the portion we want is intentional here. */

                                /*
                                 * Use helper variables for memcpy() to remain
                                 * compliant with MISRA Rule 21.15.  These should be
                                 * optimized away.
                                 */
                                pvCopySource = &pucByte[ sizeof( DNSAnswerRecord_t ) ];
                                pvCopyDest = &ulIPAddress;
                                ( void ) memcpy( pvCopyDest, pvCopySource, uxAddressLength );

                                #if ( ipconfigDNS_USE_CALLBACKS == 1 )
                                    {
                                        /* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */
                                        if( xDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, pcName, ulIPAddress ) != pdFALSE )
                                        {
                                            /* This device has requested this DNS look-up.
                                             * The result may be stored in the DNS cache. */
                                            xDoStore = pdTRUE;
                                        }
                                    }
                                #endif /* ipconfigDNS_USE_CALLBACKS == 1 */
                                #if ( ipconfigUSE_DNS_CACHE == 1 )
                                    {
                                        char cBuffer[ 16 ];

                                        /* The reply will only be stored in the DNS cache when the
                                         * request was issued by this device. */
                                        if( xDoStore != pdFALSE )
                                        {
                                            ( void ) prvProcessDNSCache( pcName, &ulIPAddress, pxDNSAnswerRecord->ulTTL, pdFALSE );
                                            usNumARecordsStored++; /* Track # of A records stored */
                                        }

                                        ( void ) FreeRTOS_inet_ntop( FREERTOS_AF_INET, ( const void * ) &( ulIPAddress ), cBuffer, ( socklen_t ) sizeof( cBuffer ) );
                                        /* Show what has happened. */
                                        FreeRTOS_printf( ( "DNS[0x%04lX]: The answer to '%s' (%s) will%s be stored\n",
                                                           ( UBaseType_t ) pxDNSMessageHeader->usIdentifier,
                                                           pcName,
                                                           cBuffer,
                                                           ( xDoStore != 0 ) ? "" : " NOT" ) );
                                    }
                                #endif /* ipconfigUSE_DNS_CACHE */
                            }

                            pucByte = &( pucByte[ sizeof( DNSAnswerRecord_t ) + uxAddressLength ] );
                            uxSourceBytesRemaining -= ( sizeof( DNSAnswerRecord_t ) + uxAddressLength );
                        }
                        else if( uxSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) )
                        {
                            /* It's not an A record, so skip it. Get the header location
                             * and then jump over the header. */
                            /* Cast the response to DNSAnswerRecord for easy access to fields of the DNS response. */
                            pxDNSAnswerRecord = ipCAST_PTR_TO_TYPE_PTR( DNSAnswerRecord_t, pucByte );

                            pucByte = &( pucByte[ sizeof( DNSAnswerRecord_t ) ] );
                            uxSourceBytesRemaining -= sizeof( DNSAnswerRecord_t );

                            /* Determine the length of the answer data from the header. */
                            usDataLength = FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength );

                            /* Jump over the answer. */
                            if( uxSourceBytesRemaining >= usDataLength )
                            {
                                pucByte = &( pucByte[ usDataLength ] );
                                uxSourceBytesRemaining -= usDataLength;
                            }
                            else
                            {
                                /* Malformed response. */
                                xReturn = pdFALSE;
                                break;
                            }
                        }
                        else
                        {
                            /* Do nothing */
                        }
                    }
                }

                #if ( ipconfigUSE_LLMNR == 1 )

                    /* No need to check that pcRequestedName != NULL since is usQuestions != 0, then
                     * pcRequestedName is assigned with this statement
                     * "pcRequestedName = ( char * ) pucByte;" */
                    else if( ( usQuestions != ( uint16_t ) 0U ) && ( usType == dnsTYPE_A_HOST ) && ( usClass == dnsCLASS_IN ) )
                    {
                        /* If this is not a reply to our DNS request, it might an LLMNR
                         * request. */
                        if( xApplicationDNSQueryHook( &( pcRequestedName[ 1 ] ) ) != pdFALSE )
                        {
                            int16_t usLength;
                            NetworkBufferDescriptor_t * pxNewBuffer = NULL;
                            NetworkBufferDescriptor_t * pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
                            LLMNRAnswer_t * pxAnswer;
                            uint8_t * pucNewBuffer = NULL;

                            if( pxNetworkBuffer != NULL )
                            {
                                if( xBufferAllocFixedSize == pdFALSE )
                                {
                                    size_t uxDataLength = uxBufferLength + sizeof( UDPHeader_t ) + sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );

                                    /* Set the size of the outgoing packet. */
                                    pxNetworkBuffer->xDataLength = uxDataLength;
                                    pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, uxDataLength + sizeof( LLMNRAnswer_t ) );

                                    if( pxNewBuffer != NULL )
                                    {
                                        BaseType_t xOffset1, xOffset2;

                                        xOffset1 = ( BaseType_t ) ( pucByte - pucUDPPayloadBuffer );
                                        xOffset2 = ( BaseType_t ) ( ( ( uint8_t * ) pcRequestedName ) - pucUDPPayloadBuffer );

                                        pxNetworkBuffer = pxNewBuffer;
                                        pucNewBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] );

                                        pucByte = &( pucNewBuffer[ xOffset1 ] );
                                        pcRequestedName = ( char * ) &( pucNewBuffer[ xOffset2 ] );
                                        pxDNSMessageHeader = ipCAST_PTR_TO_TYPE_PTR( DNSMessage_t, pucNewBuffer );
                                    }
                                    else
                                    {
                                        /* Just to indicate that the message may not be answered. */
                                        pxNetworkBuffer = NULL;
                                    }
                                }
                                else
                                {
                                    pucNewBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] );
                                }
                            }

                            /* The test on 'pucNewBuffer' is only to satisfy lint. */
                            if( ( pxNetworkBuffer != NULL ) && ( pucNewBuffer != NULL ) )
                            {
                                pxAnswer = ipCAST_PTR_TO_TYPE_PTR( LLMNRAnswer_t, pucByte );

                                /* We leave 'usIdentifier' and 'usQuestions' untouched */
                                #ifndef _lint
                                    vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE ); /* Set the response flag */
                                    vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 );                       /* Provide a single answer */
                                    vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 );                  /* No authority */
                                    vSetField16( pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 );                 /* No additional info */
                                #endif /* lint */

                                pxAnswer->ucNameCode = dnsNAME_IS_OFFSET;
                                pxAnswer->ucNameOffset = ( uint8_t ) ( pcRequestedName - ( char * ) pucNewBuffer );

                                #ifndef _lint
                                    vSetField16( pxAnswer, LLMNRAnswer_t, usType, dnsTYPE_A_HOST ); /* Type A: host */
                                    vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN );   /* 1: Class IN */
                                    vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE );
                                    vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, 4 );
                                    vSetField32( pxAnswer, LLMNRAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) );
                                #endif /* lint */
                                usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucNewBuffer ) );

                                prvReplyDNSMessage( pxNetworkBuffer, usLength );

                                if( pxNewBuffer != NULL )
                                {
                                    vReleaseNetworkBufferAndDescriptor( pxNewBuffer );
                                }
                            }
                        }
                    }
                    else
                    {
                        /* Not an expected reply. */
                    }
                #endif /* ipconfigUSE_LLMNR == 1 */
                ( void ) uxBytesRead;
            } while( ipFALSE_BOOL );
        }

        if( xReturn == pdFALSE )
        {
            /* There was an error while parsing the DNS response. Return error code. */
            ulIPAddress = dnsPARSE_ERROR;
        }
        else if( xExpected == pdFALSE )
        {
            /* Do not return a valid IP-address in case the reply was not expected. */
            ulIPAddress = 0U;
        }
        else
        {
            /* The IP-address found will be returned. */
        }

        #if ( ipconfigUSE_DNS_CACHE == 1 ) || ( ipconfigDNS_USE_CALLBACKS == 1 )
            ( void ) xDoStore;
        #endif

        return ulIPAddress;
    }