static uint32_t prvParseDNSReply()

in device_firmware/libraries/freertos_plus/standard/freertos_plus_tcp/source/FreeRTOS_DNS.c [856:1118]


static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, size_t xBufferLength, TickType_t xIdentifier )
{
DNSMessage_t *pxDNSMessageHeader;
DNSAnswerRecord_t *pxDNSAnswerRecord;
uint32_t ulIPAddress = 0UL;
#if( ipconfigUSE_LLMNR == 1 )
	char *pcRequestedName = NULL;
#endif
uint8_t *pucByte;
size_t xSourceBytesRemaining;
uint16_t x, usDataLength, usQuestions;
#if( ipconfigUSE_LLMNR == 1 )
	uint16_t usType = 0, usClass = 0;
#endif
#if( ipconfigUSE_DNS_CACHE == 1 )
	char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ] = "";
#endif

	/* Ensure that the buffer is of at least minimal DNS message length. */
	if( xBufferLength < sizeof( DNSMessage_t ) )
	{
		return dnsPARSE_ERROR;
	}
	else
	{
		xSourceBytesRemaining = xBufferLength;
	}

	/* Parse the DNS message header. */
	pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;

	if( pxDNSMessageHeader->usIdentifier == ( uint16_t ) xIdentifier )
	{
		/* Start at the first byte after the header. */
		pucByte = pucUDPPayloadBuffer + sizeof( DNSMessage_t );
		xSourceBytesRemaining -= sizeof( DNSMessage_t );

		/* Skip any question records. */
		usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );
		for( x = 0; x < usQuestions; x++ )
		{
			#if( ipconfigUSE_LLMNR == 1 )
			{
				if( x == 0 )
				{
					pcRequestedName = ( char * ) pucByte;
				}
			}
			#endif

#if( ipconfigUSE_DNS_CACHE == 1 )
			if( x == 0 )
			{
				pucByte = prvReadNameField( pucByte,
											xSourceBytesRemaining,
											pcName,
											sizeof( pcName ) );

				/* Check for a malformed response. */
				if( NULL == pucByte )
				{
					return dnsPARSE_ERROR;
				}
				else
				{
					xSourceBytesRemaining = ( pucUDPPayloadBuffer + xBufferLength ) - pucByte;
				}
			}
			else
#endif /* ipconfigUSE_DNS_CACHE */
			{
				/* Skip the variable length pcName field. */
				pucByte = prvSkipNameField( pucByte,
											xSourceBytesRemaining );

				/* Check for a malformed response. */
				if( NULL == pucByte )
				{
					return dnsPARSE_ERROR;
				}
				else
				{
					xSourceBytesRemaining = pucUDPPayloadBuffer + xBufferLength - pucByte;
				}
			}

			/* Check the remaining buffer size. */
			if( xSourceBytesRemaining >= 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 += sizeof( uint32_t );
				xSourceBytesRemaining -= sizeof( uint32_t );
			}
			else
			{
				/* Malformed response. */
				return dnsPARSE_ERROR;
			}
		}

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

		if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
		{
			for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
			{
				pucByte = prvSkipNameField( pucByte,
											xSourceBytesRemaining );

				/* Check for a malformed response. */
				if( NULL == pucByte )
				{
					return dnsPARSE_ERROR;
				}
				else
				{
					xSourceBytesRemaining = pucUDPPayloadBuffer + xBufferLength - pucByte;
				}

				/* Is there enough data for an IPv4 A record answer and, if so,
				is this an A record? */
				if( xSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) &&
					usChar2u16( pucByte ) == dnsTYPE_A_HOST )
				{
					/* This is the required record type and is of sufficient size. */
					pxDNSAnswerRecord = ( DNSAnswerRecord_t * )pucByte;

					/* Sanity check the data length of an IPv4 answer. */
					if( FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ) == sizeof( uint32_t ) )
					{
						/* Copy the IP address out of the record. */
						memcpy( &ulIPAddress,
								pucByte + sizeof( DNSAnswerRecord_t ),
								sizeof( uint32_t ) );

						#if( ipconfigUSE_DNS_CACHE == 1 )
						{
							prvProcessDNSCache( pcName, &ulIPAddress, pxDNSAnswerRecord->ulTTL, pdFALSE );
						}
						#endif /* ipconfigUSE_DNS_CACHE */
						#if( ipconfigDNS_USE_CALLBACKS != 0 )
						{
							/* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */
							vDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, pcName, ulIPAddress );
						}
						#endif	/* ipconfigDNS_USE_CALLBACKS != 0 */
					}

					pucByte += sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t );
					xSourceBytesRemaining -= ( sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) );
					break;
				}
				else if( xSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) )
				{
					/* It's not an A record, so skip it. Get the header location
					and then jump over the header. */
					pxDNSAnswerRecord = ( DNSAnswerRecord_t * )pucByte;
					pucByte += sizeof( DNSAnswerRecord_t );
					xSourceBytesRemaining -= sizeof( DNSAnswerRecord_t );

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

					/* Jump over the answer. */
					if( xSourceBytesRemaining >= usDataLength )
					{
						pucByte += usDataLength;
						xSourceBytesRemaining -= usDataLength;
					}
					else
					{
						/* Malformed response. */
						return dnsPARSE_ERROR;
					}
				}
			}
		}
#if( ipconfigUSE_LLMNR == 1 )
		else if( usQuestions && ( 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 ) ) )
			{
			int16_t usLength;
			NetworkBufferDescriptor_t *pxNewBuffer = NULL;
			NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer );
			LLMNRAnswer_t *pxAnswer;

				if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )
				{
				BaseType_t xDataLength = xBufferLength + sizeof( UDPHeader_t ) + sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );

					/* The field xDataLength was set to the length of the UDP payload.
					The answer (reply) will be longer than the request, so the packet
					must be duplicaed into a bigger buffer */
					pxNetworkBuffer->xDataLength = xDataLength;
					pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, xDataLength + 16 );
					if( pxNewBuffer != NULL )
					{
					BaseType_t xOffset1, xOffset2;

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

						pxNetworkBuffer = pxNewBuffer;
						pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + ipUDP_PAYLOAD_OFFSET_IPv4;

						pucByte = pucUDPPayloadBuffer + xOffset1;
						pcRequestedName = ( char * ) ( pucUDPPayloadBuffer + xOffset2 );
						pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;

					}
					else
					{
						/* Just to indicate that the message may not be answered. */
						pxNetworkBuffer = NULL;
					}
				}
				if( pxNetworkBuffer != NULL )
				{
					pxAnswer = (LLMNRAnswer_t *)pucByte;

					/* We leave 'usIdentifier' and 'usQuestions' untouched */
					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 */

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

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

					usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucUDPPayloadBuffer ) );

					prvReplyDNSMessage( pxNetworkBuffer, usLength );

					if( pxNewBuffer != NULL )
					{
						vReleaseNetworkBufferAndDescriptor( pxNewBuffer );
					}
				}
			}
		}
#endif /* ipconfigUSE_LLMNR == 1 */
	}

	return ulIPAddress;
}