static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, TickType_t xIdentifier ) { DNSMessage_t *pxDNSMessageHeader; uint32_t ulIPAddress = 0UL; #if( ipconfigUSE_LLMNR == 1 ) char *pcRequestedName = NULL; #endif uint8_t *pucByte; uint16_t x, usDataLength, usQuestions; #if( ipconfigUSE_LLMNR == 1 ) uint16_t usType = 0, usClass = 0; #endif #if( ipconfigUSE_DNS_CACHE == 1 ) char pcName[128] = ""; /*_RB_ What is the significance of 128? Probably too big to go on the stack for a small MCU but don't know how else it could be made re-entrant. Might be necessary. */ #endif pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer; if( pxDNSMessageHeader->usIdentifier == ( uint16_t ) xIdentifier ) { /* Start at the first byte after the header. */ pucByte = pucUDPPayloadBuffer + 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, pcName, sizeof( pcName ) ); } else #endif /* ipconfigUSE_DNS_CACHE */ { /* Skip the variable length pcName field. */ pucByte = prvSkipNameField( pucByte ); } #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 ); } /* Search through the answers 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 ); /* Is the type field that of an A record? */ if( usChar2u16( pucByte ) == dnsTYPE_A_HOST ) { /* This is the required record. Skip the type, class, and time to live fields, plus the first byte of the data length. */ pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) ); /* Sanity check the data length. */ if( ( size_t ) *pucByte == sizeof( uint32_t ) ) { /* Skip the second byte of the length. */ pucByte++; /* Copy the IP address out of the record. */ memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) ); #if( ipconfigUSE_DNS_CACHE == 1 ) { prvProcessDNSCache( pcName, &ulIPAddress, 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 */ } break; } else { /* Skip the type, class and time to live fields. */ pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) ); /* Determine the length of the data in the field. */ memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) ); usDataLength = FreeRTOS_ntohs( usDataLength ); /* Jump over the data length bytes, and the data itself. */ pucByte += usDataLength + sizeof( uint16_t ); } } } #if( ipconfigUSE_LLMNR == 1 ) else if( ( usQuestions != ( uint16_t )0u ) && ( usType == ( uint16_t )dnsTYPE_A_HOST ) && ( usClass == ( uint16_t )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 = pxNetworkBuffer->xDataLength + 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; /* 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; }
static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, uint16_t usIdentifier ) { xDNSMessage_t *pxDNSMessageHeader; uint32_t ulIPAddress = 0UL; uint8_t *pucByte; uint16_t x, usDataLength; const uint16_t usARecordType = dnsTYPE; pxDNSMessageHeader = ( xDNSMessage_t * ) pucUDPPayloadBuffer; if( pxDNSMessageHeader->usIdentifier == usIdentifier ) { if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS ) { /* Start at the first byte after the header. */ pucByte = pucUDPPayloadBuffer + sizeof( xDNSMessage_t ); /* Skip any question records. */ pxDNSMessageHeader->usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions ); for( x = 0; x < pxDNSMessageHeader->usQuestions; x++ ) { /* Skip the variable length name field. */ pucByte = prvSkipNameField( pucByte ); /* Skip the type and class fields. */ pucByte += sizeof( uint32_t ); } /* Search through the answers records. */ pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers ); for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ ) { pucByte = prvSkipNameField( pucByte ); /* Is the type field that of an A record? */ if( memcmp( ( void * ) pucByte, ( void * ) &usARecordType, sizeof( uint16_t ) ) == 0 ) { /* This is the required record. Skip the type, class, and time to live fields, plus the first byte of the data length. */ pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) ); /* Sanity check the data length. */ if( *pucByte == sizeof( uint32_t ) ) { /* Skip the second byte of the length. */ pucByte++; /* Copy the IP address out of the record. */ memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) ); } break; } else { /* Skip the type, class and time to live fields. */ pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) ); /* Determine the length of the data in the field. */ memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) ); usDataLength = FreeRTOS_ntohs( usDataLength ); /* Jump over the data lenth bytes, and the data itself. */ pucByte += usDataLength + sizeof( uint16_t ); } } } } return ulIPAddress; }