diff --git a/source/FreeRTOS_DNS_Parser.c b/source/FreeRTOS_DNS_Parser.c index bb070c369c..1666a3e8ed 100644 --- a/source/FreeRTOS_DNS_Parser.c +++ b/source/FreeRTOS_DNS_Parser.c @@ -263,6 +263,7 @@ BaseType_t xReturn = pdTRUE; uint32_t ulIPAddress = 0U; BaseType_t xDNSHookReturn = 0U; + NetworkBufferDescriptor_t * pxNewBuffer = NULL; ( void ) memset( &( xSet ), 0, sizeof( xSet ) ); xSet.usPortNumber = usPort; @@ -383,6 +384,13 @@ xSet.pucByte = &( xSet.pucByte[ uxResult ] ); xSet.uxSourceBytesRemaining -= uxResult; + if( ( x == 0U ) && ( xSet.usPortNumber == ipMDNS_PORT ) ) + { + /* Note that the Questions section turns into the Answers section. + * uxSkipCount points to the first byte after e.g. 'name.local' */ + xSet.uxSkipCount = ( size_t ) ( xSet.pucByte - pucUDPPayloadBuffer ); + } + /* Check the remaining buffer size. */ if( xSet.uxSourceBytesRemaining >= sizeof( uint32_t ) ) { @@ -430,6 +438,21 @@ NetworkEndPoint_t * pxEndPoint, xEndPoint; size_t uxUDPOffset; + #if ( ipconfigUSE_IPv6 == 0 ) + /* Don't treat AAAA request when IPv6 is not enabled. */ + if( xSet.usType == dnsTYPE_AAAA_HOST ) + { + break; + } + #endif + #if ( ipconfigUSE_IPv4 == 0 ) + /* Don't treat A request when IPv4 is not enabled. */ + if( xSet.usType == dnsTYPE_A_HOST ) + { + break; + } + #endif + pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer ); /* This test could be replaced with a assert(). */ @@ -458,7 +481,7 @@ #if ( ipconfigUSE_IPv6 != 0 ) { /*logging*/ - FreeRTOS_printf( ( "prvParseDNS_HandleLLMNRRequest[%s]: type %04X\n", xSet.pcName, xSet.usType ) ); + FreeRTOS_printf( ( "DNS_ParseDNSReply[%s]: type %04X\n", xSet.pcName, xSet.usType ) ); xEndPoint.usDNSType = ( uint8_t ) xSet.usType; } @@ -482,24 +505,26 @@ if( xDNSHookReturn != pdFALSE ) { int16_t usLength; - NetworkBufferDescriptor_t * pxNewBuffer = NULL; LLMNRAnswer_t * pxAnswer; uint8_t * pucNewBuffer = NULL; - size_t uxExtraLength; - size_t uxDataLength = uxBufferLength + - sizeof( UDPHeader_t ) + - sizeof( EthernetHeader_t ) + - uxIPHeaderSizePacket( pxNetworkBuffer ); + /* Number of bytes to write the Answers section: 16 (A) or 28 (AAAA). */ + size_t uxExtraLength = 0U; + size_t uxDataLength = uxBufferLength + /* Length of the UDP payload buffer */ + sizeof( UDPHeader_t ) + /* Length of the UDP header */ + sizeof( EthernetHeader_t ) + /* Length of the Ethernet header */ + uxIPHeaderSizePacket( pxNetworkBuffer ); /* Lentgh of IP 20 or 40. */ #if ( ipconfigUSE_IPv6 != 0 ) if( xSet.usType == dnsTYPE_AAAA_HOST ) { + /* The number of bytes needed by Answers section (28 bytes). */ uxExtraLength = sizeof( LLMNRAnswer_t ) + ipSIZE_OF_IPv6_ADDRESS - sizeof( pxAnswer->ulIPAddress ); } else #endif /* ( ipconfigUSE_IPv6 != 0 ) */ #if ( ipconfigUSE_IPv4 != 0 ) { + /* The number of bytes needed by Answers section (16 bytes). */ uxExtraLength = sizeof( LLMNRAnswer_t ); } #else /* ( ipconfigUSE_IPv4 != 0 ) */ @@ -510,7 +535,8 @@ if( xBufferAllocFixedSize == pdFALSE ) { - /* Set the size of the outgoing packet. */ + /* Set the size of the outgoing packet. + * setting 'xDataLength' determines the minimum number of bytes copied. */ pxNetworkBuffer->xDataLength = uxDataLength; pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, uxDataLength + @@ -553,50 +579,63 @@ if( ( pxNetworkBuffer != NULL ) ) { - pxAnswer = ( ( LLMNRAnswer_t * ) xSet.pucByte ); + size_t uxDistance; + /* We leave 'usIdentifier' and 'usQuestions' untouched */ - vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_RESPONSE ); /* Set the response flag */ - vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 ); /* Provide a single answer */ - vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 ); /* No authority */ - vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 ); /* No additional info */ + if( xSet.uxSkipCount >= 2 ) + { + /* Four bytes back to write usType/usClass. */ + pxAnswer = ( LLMNRAnswer_t * ) ( &( pucNewBuffer[ xSet.uxSkipCount - 2 ] ) ); + /* Follow RFC6762 to set QR bit (section 18.2) and authoritative answer (AA) bit (section 18.4). */ + vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usFlags, dnsMDNS_FLAGS_IS_RESPONSE ); + /* When replying to an mDNS request, do not include the Questions section. */ + xSet.usQuestions = 0; + } + else + { + pxAnswer = ( ( LLMNRAnswer_t * ) xSet.pucByte ); + /* Follow RFC4795 to set QR bit (section 2.1.1) */ + vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_RESPONSE ); + } - pxAnswer->ucNameCode = dnsNAME_IS_OFFSET; - pxAnswer->ucNameOffset = ( uint8_t ) ( xSet.pcRequestedName - ( char * ) pucNewBuffer ); + vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usQuestions, xSet.usQuestions ); /* Might be zero. */ + vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 ); /* Provide a single answer */ + vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 ); /* No authority */ + vSetField16( xSet.pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 ); /* No additional info */ + + if( xSet.usQuestions > 0 ) + { + pxAnswer->ucNameCode = dnsNAME_IS_OFFSET; + pxAnswer->ucNameOffset = ( uint8_t ) ( xSet.pcRequestedName - ( char * ) pucNewBuffer ); + } vSetField16( pxAnswer, LLMNRAnswer_t, usType, xSet.usType ); /* Type A or AAAA: host */ vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN ); /* 1: Class IN */ vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE ); - usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( xSet.pucByte - pucNewBuffer ) ); + uxDistance = ( size_t ) ( ( ( const uint8_t * ) pxAnswer ) - pucNewBuffer ); #if ( ipconfigUSE_IPv6 != 0 ) if( xSet.usType == dnsTYPE_AAAA_HOST ) { - size_t uxDistance; vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, ipSIZE_OF_IPv6_ADDRESS ); ( void ) memcpy( &( pxAnswer->ulIPAddress ), xEndPoint.ipv6_settings.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS ); - uxDistance = ( size_t ) ( xSet.pucByte - pucNewBuffer ); /* An extra 12 bytes will be sent compared to an A-record. */ usLength = ( int16_t ) ( sizeof( *pxAnswer ) + uxDistance + ipSIZE_OF_IPv6_ADDRESS - sizeof( pxAnswer->ulIPAddress ) ); } else #endif /* ( ipconfigUSE_IPv6 != 0 ) */ { - size_t uxDistance; vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, ( uint16_t ) sizeof( pxAnswer->ulIPAddress ) ); vSetField32( pxAnswer, LLMNRAnswer_t, ulIPAddress, FreeRTOS_ntohl( xEndPoint.ipv4_settings.ulIPAddress ) ); - uxDistance = ( size_t ) ( xSet.pucByte - pucNewBuffer ); usLength = ( int16_t ) ( sizeof( *pxAnswer ) + uxDistance ); } prepareReplyDNSMessage( pxNetworkBuffer, usLength ); - /* This function will fill in the eth addresses and send the packet */ - vReturnEthernetFrame( pxNetworkBuffer, pdFALSE ); - if( pxNewBuffer != NULL ) - { - vReleaseNetworkBufferAndDescriptor( pxNewBuffer ); - } + /* This function will fill in the eth addresses and send the packet. + * xReleaseAfterSend = pdFALSE. */ + vReturnEthernetFrame( pxNetworkBuffer, pdFALSE ); } } else @@ -607,6 +646,11 @@ #endif /* ipconfigUSE_LLMNR == 1 */ ( void ) uxBytesRead; } while( ipFALSE_BOOL ); + + if( pxNewBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNewBuffer ); + } } if( xReturn == pdFALSE ) diff --git a/source/include/FreeRTOS_DNS_Globals.h b/source/include/FreeRTOS_DNS_Globals.h index 0de266e6c8..36d74b359e 100644 --- a/source/include/FreeRTOS_DNS_Globals.h +++ b/source/include/FreeRTOS_DNS_Globals.h @@ -109,12 +109,14 @@ #define ipLLMNR_IP_ADDR 0xFC0000E0UL #endif /* ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN */ - #define ipMDNS_TIME_TO_LIVE 255U +/* MDNS constants. */ + #define ipMDNS_TIME_TO_LIVE 255U + #define dnsMDNS_FLAGS_IS_RESPONSE 0x8400U /**< MDNS flag value for response. */ - #define ipLLMNR_PORT 5355U /* Standard LLMNR port. */ - #define ipDNS_PORT 53U /* Standard DNS port. */ - #define ipNBNS_PORT 137U /* NetBIOS Name Service. */ - #define ipNBDGM_PORT 138U /* Datagram Service, not included. */ + #define ipLLMNR_PORT 5355U /* Standard LLMNR port. */ + #define ipDNS_PORT 53U /* Standard DNS port. */ + #define ipNBNS_PORT 137U /* NetBIOS Name Service. */ + #define ipNBDGM_PORT 138U /* Datagram Service, not included. */ /** @brief freertos_addrinfo is the equivalent of 'struct addrinfo'. */ struct freertos_addrinfo @@ -175,6 +177,7 @@ uint16_t usAnswers; /**< The number of DNS answers that were given. */ uint8_t * pucUDPPayloadBuffer; /**< A pointer to the original UDP load buffer. */ uint8_t * pucByte; /**< A pointer that is used while parsing. */ + size_t uxSkipCount; /**< Points to the byte after the complete name (mDNS only) */ size_t uxBufferLength; /**< The total number of bytes received in the UDP payload. */ size_t uxSourceBytesRemaining; /**< As pucByte is incremented, 'uxSourceBytesRemaining' will be decremented. */ uint16_t usType; /**< The type of address, recognised are dnsTYPE_A_HOST ( Ipv4 ) and diff --git a/test/unit-test/FreeRTOS_DNS_Parser/FreeRTOS_DNS_Parser_utest.c b/test/unit-test/FreeRTOS_DNS_Parser/FreeRTOS_DNS_Parser_utest.c index 77607346ee..ac18be894a 100644 --- a/test/unit-test/FreeRTOS_DNS_Parser/FreeRTOS_DNS_Parser_utest.c +++ b/test/unit-test/FreeRTOS_DNS_Parser/FreeRTOS_DNS_Parser_utest.c @@ -4025,3 +4025,68 @@ void test_prepareReplyDNSMessage_null_pointer( void ) uxIPHeaderSizePacket_IgnoreAndReturn( ipSIZE_OF_IPv4_HEADER ); catch_assert( prepareReplyDNSMessage( &pxNetworkBuffer, lNetLength ) ); } + +void test_DNS_ParseDNSReply_mdns_request( void ) +{ + uint32_t ret; + uint8_t pucPacketBuffer[ 312 ]; + size_t uxBufferLength = 250; + char dns[ 64 ]; + struct freertos_addrinfo * pxAddressInfo; + uint16_t usPort = ipMDNS_PORT; + uint8_t * pucPayloadBuffer = pucPacketBuffer + sizeof( UDPPacket_t ); /* Skip 42 bytes. */ + + memset( dns, 'a', 64 ); + memset( pucPacketBuffer, 0x00, sizeof pucPacketBuffer ); + dns[ 63 ] = 0; + BaseType_t xExpected = pdFALSE; + size_t beg = sizeof( DNSMessage_t ); /* 8 x 2 = 16 bytes */ + NetworkEndPoint_t xEndPoint = { 0 }; + + DNSMessage_t * dns_header; + + NetworkBufferDescriptor_t xNetworkBuffer = { 0 }; + + xNetworkBuffer.pucEthernetBuffer = pucPacketBuffer; + xNetworkBuffer.pxEndPoint = &xEndPoint; + xEndPoint.ipv4_settings.ulIPAddress = 0xC0A8020B; + + dns_header = ( DNSMessage_t * ) pucPayloadBuffer; /* pucPayloadBuffer without '&' */ + + dns_header->usQuestions = FreeRTOS_htons( 1 ); + dns_header->usAnswers = FreeRTOS_htons( 2 ); + dns_header->usFlags = 0; /* dnsEXPECTED_RX_FLAGS; */ + + /* Question-1: */ + pucPayloadBuffer[ beg ] = 38; /* Length of name. */ + beg++; /* Skip the length byte. */ + strcpy( pucPayloadBuffer + beg, "FreeRTOSbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" ); + beg += 38 + 1 + 4; /* Skip name, nul-byte, and Type/Class. */ + + /* xApplicationDNSQueryHook_Multi() must be called. */ + hook_return = pdTRUE; + /* it hasn't been called yet. */ + hook_called = pdFALSE; + + usChar2u16_ExpectAnyArgsAndReturn( 0x0001 ); /* usType */ + usChar2u16_ExpectAnyArgsAndReturn( 0x0001 ); /* usClass */ + pxUDPPayloadBuffer_to_NetworkBuffer_ExpectAnyArgsAndReturn( &xNetworkBuffer ); + uxIPHeaderSizePacket_IgnoreAndReturn( ipSIZE_OF_IPv4_HEADER ); + + pxDuplicateNetworkBufferWithDescriptor_ExpectAnyArgsAndReturn( &xNetworkBuffer ); + + usGenerateChecksum_ExpectAnyArgsAndReturn( 555 ); + usGenerateProtocolChecksum_ExpectAnyArgsAndReturn( 444 ); + + vReturnEthernetFrame_Expect( &xNetworkBuffer, pdFALSE ); + vReleaseNetworkBufferAndDescriptor_Expect( &xNetworkBuffer ); + + ret = DNS_ParseDNSReply( pucPayloadBuffer, + beg, /* uxBufferLength, */ + &pxAddressInfo, + xExpected, + usPort ); + + TEST_ASSERT_EQUAL( pdFALSE, ret ); + ASSERT_DNS_QUERY_HOOK_CALLED(); +}