/*\r
- * FreeRTOS+TCP V2.0.3\r
+ * FreeRTOS+TCP V2.0.7\r
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
*\r
* Permission is hereby granted, free of charge, to any person obtaining a copy of\r
#if( ipconfigUSE_DNS != 0 )\r
\r
#if( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN )\r
- #define dnsDNS_PORT 0x3500u\r
- #define dnsONE_QUESTION 0x0100u\r
- #define dnsOUTGOING_FLAGS 0x0001u /* Standard query. */\r
- #define dnsRX_FLAGS_MASK 0x0f80u /* The bits of interest in the flags field of incoming DNS messages. */\r
- #define dnsEXPECTED_RX_FLAGS 0x0080u /* Should be a response, without any errors. */\r
+ #define dnsDNS_PORT 0x3500\r
+ #define dnsONE_QUESTION 0x0100\r
+ #define dnsOUTGOING_FLAGS 0x0001 /* Standard query. */\r
+ #define dnsRX_FLAGS_MASK 0x0f80 /* The bits of interest in the flags field of incoming DNS messages. */\r
+ #define dnsEXPECTED_RX_FLAGS 0x0080 /* Should be a response, without any errors. */\r
#else\r
- #define dnsDNS_PORT 0x0035u\r
- #define dnsONE_QUESTION 0x0001u\r
- #define dnsOUTGOING_FLAGS 0x0100u /* Standard query. */\r
- #define dnsRX_FLAGS_MASK 0x800fu /* The bits of interest in the flags field of incoming DNS messages. */\r
- #define dnsEXPECTED_RX_FLAGS 0x8000u /* Should be a response, without any errors. */\r
+ #define dnsDNS_PORT 0x0035\r
+ #define dnsONE_QUESTION 0x0001\r
+ #define dnsOUTGOING_FLAGS 0x0100 /* Standard query. */\r
+ #define dnsRX_FLAGS_MASK 0x800f /* The bits of interest in the flags field of incoming DNS messages. */\r
+ #define dnsEXPECTED_RX_FLAGS 0x8000 /* Should be a response, without any errors. */\r
\r
#endif /* ipconfigBYTE_ORDER */\r
\r
#define dnsNAME_IS_OFFSET ( ( uint8_t ) 0xc0 )\r
\r
/* NBNS flags. */\r
-#define dnsNBNS_FLAGS_RESPONSE 0x8000u\r
-#define dnsNBNS_FLAGS_OPCODE_MASK 0x7800u\r
-#define dnsNBNS_FLAGS_OPCODE_QUERY 0x0000u\r
-#define dnsNBNS_FLAGS_OPCODE_REGISTRATION 0x2800u\r
+#define dnsNBNS_FLAGS_RESPONSE 0x8000\r
+#define dnsNBNS_FLAGS_OPCODE_MASK 0x7800\r
+#define dnsNBNS_FLAGS_OPCODE_QUERY 0x0000\r
+#define dnsNBNS_FLAGS_OPCODE_REGISTRATION 0x2800\r
\r
/* Host types. */\r
-#define dnsTYPE_A_HOST 0x0001u\r
-#define dnsCLASS_IN 0x0001u\r
+#define dnsTYPE_A_HOST 0x01\r
+#define dnsCLASS_IN 0x01\r
\r
/* LLMNR constants. */\r
-#define dnsLLMNR_TTL_VALUE 300000u\r
-#define dnsLLMNR_FLAGS_IS_REPONSE 0x8000u\r
+#define dnsLLMNR_TTL_VALUE 300000\r
+#define dnsLLMNR_FLAGS_IS_REPONSE 0x8000\r
\r
/* NBNS constants. */\r
-#define dnsNBNS_TTL_VALUE 3600u /* 1 hour valid */\r
-#define dnsNBNS_TYPE_NET_BIOS 0x0020u\r
-#define dnsNBNS_CLASS_IN 0x0001u\r
-#define dnsNBNS_NAME_FLAGS 0x6000u\r
+#define dnsNBNS_TTL_VALUE 3600 /* 1 hour valid */\r
+#define dnsNBNS_TYPE_NET_BIOS 0x0020\r
+#define dnsNBNS_CLASS_IN 0x01\r
+#define dnsNBNS_NAME_FLAGS 0x6000\r
#define dnsNBNS_ENCODED_NAME_LENGTH 32\r
\r
/* If the queried NBNS name matches with the device's name,\r
the query will be responded to with these flags: */\r
-#define dnsNBNS_QUERY_RESPONSE_FLAGS 0x8500u\r
+#define dnsNBNS_QUERY_RESPONSE_FLAGS ( 0x8500 )\r
+\r
+/* Flag DNS parsing errors in situations where an IPv4 address is the return \r
+type. */\r
+#define dnsPARSE_ERROR 0UL\r
\r
/*\r
* Create a socket and bind it to the standard DNS port number. Return the\r
/*\r
* Simple routine that jumps over the NAME field of a resource record.\r
*/\r
-static uint8_t *prvSkipNameField( uint8_t *pucByte );\r
+static uint8_t *prvSkipNameField( uint8_t *pucByte, size_t xSourceLen );\r
\r
/*\r
* Process a response packet from a DNS server.\r
*/\r
-static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, TickType_t xIdentifier );\r
+static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, size_t xBufferLength, TickType_t xIdentifier );\r
\r
/*\r
* Prepare and send a message to a DNS server. 'xReadTimeOut_ms' will be passed as\r
#endif\r
\r
#if( ipconfigUSE_NBNS == 1 )\r
- static portINLINE void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer, uint32_t ulIPAddress );\r
+ static portINLINE void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer, size_t xBufferLength, uint32_t ulIPAddress );\r
#endif /* ipconfigUSE_NBNS */\r
\r
#if( ipconfigUSE_DNS_CACHE == 1 )\r
- static uint8_t *prvReadNameField( uint8_t *pucByte, char *pcName, BaseType_t xLen );\r
- static void prvProcessDNSCache( const char *pcName, uint32_t *pulIP, BaseType_t xLookUp );\r
+ static uint8_t *prvReadNameField( uint8_t *pucByte, size_t xSourceLen, char *pcName, size_t xLen );\r
+ static void prvProcessDNSCache( const char *pcName, uint32_t *pulIP, uint32_t ulTTL, BaseType_t xLookUp );\r
\r
typedef struct xDNS_CACHE_TABLE_ROW\r
{\r
uint32_t ulIPAddress; /* The IP address of an ARP cache entry. */\r
- char pcName[ipconfigDNS_CACHE_NAME_LENGTH]; /* The name of the host */\r
- uint8_t ucAge; /* A value that is periodically decremented but can also be refreshed by active communication. The ARP cache entry is removed if the value reaches zero. */\r
+ char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ]; /* The name of the host */\r
+ uint32_t ulTTL; /* Time-to-Live (in seconds) from the DNS server. */\r
+ uint32_t ulTimeWhenAddedInSeconds;\r
} DNSCacheRow_t;\r
\r
static DNSCacheRow_t xDNSCache[ ipconfigDNS_CACHE_ENTRIES ];\r
#include "pack_struct_end.h"\r
typedef struct xDNSTail DNSTail_t;\r
\r
+/* DNS answer record header. */\r
+#include "pack_struct_start.h"\r
+struct xDNSAnswerRecord\r
+{\r
+ uint16_t usType;\r
+ uint16_t usClass;\r
+ uint32_t ulTTL;\r
+ uint16_t usDataLength;\r
+}\r
+#include "pack_struct_end.h"\r
+typedef struct xDNSAnswerRecord DNSAnswerRecord_t;\r
+\r
#if( ipconfigUSE_LLMNR == 1 )\r
\r
#include "pack_struct_start.h"\r
uint32_t FreeRTOS_dnslookup( const char *pcHostName )\r
{\r
uint32_t ulIPAddress = 0UL;\r
- prvProcessDNSCache( pcHostName, &ulIPAddress, pdTRUE );\r
+ prvProcessDNSCache( pcHostName, &ulIPAddress, 0, pdTRUE );\r
return ulIPAddress;\r
}\r
#endif /* ipconfigUSE_DNS_CACHE == 1 */\r
#endif\r
{\r
uint32_t ulIPAddress = 0UL;\r
-static uint16_t usIdentifier = 0u;\r
-TickType_t xReadTimeOut_ms = 1200U;\r
-/* Generate a unique identifier for this query. Keep it in a local variable\r
- as gethostbyname() may be called from different threads */\r
-TickType_t xIdentifier = ( TickType_t )usIdentifier++;\r
-\r
- /* If the supplied hostname is IP address, convert it to uint32_t\r
- and return. */\r
- #if( ipconfigINCLUDE_FULL_INET_ADDR == 1 )\r
- {\r
- ulIPAddress = FreeRTOS_inet_addr( pcHostName );\r
- }\r
- #endif /* ipconfigINCLUDE_FULL_INET_ADDR == 1 */\r
-\r
- /* If a DNS cache is used then check the cache before issuing another DNS\r
- request. */\r
- #if( ipconfigUSE_DNS_CACHE == 1 )\r
- {\r
- if( ulIPAddress == 0UL )\r
- {\r
- ulIPAddress = FreeRTOS_dnslookup( pcHostName );\r
- if( ulIPAddress != 0 )\r
- {\r
- FreeRTOS_debug_printf( ( "FreeRTOS_gethostbyname: found '%s' in cache: %lxip\n", pcHostName, ulIPAddress ) );\r
- }\r
- else\r
- {\r
- /* prvGetHostByName will be called to start a DNS lookup */\r
- }\r
- }\r
- }\r
- #endif /* ipconfigUSE_DNS_CACHE == 1 */\r
-\r
- #if( ipconfigDNS_USE_CALLBACKS != 0 )\r
- {\r
- if( pCallback != NULL )\r
- {\r
- if( ulIPAddress == 0UL )\r
- {\r
- /* The user has provided a callback function, so do not block on recvfrom() */\r
- xReadTimeOut_ms = 0;\r
- vDNSSetCallBack( pcHostName, pvSearchID, pCallback, xTimeout, ( TickType_t ) xIdentifier );\r
- }\r
- else\r
- {\r
- /* The IP address is known, do the call-back now. */\r
- pCallback( pcHostName, pvSearchID, ulIPAddress );\r
- }\r
- }\r
- }\r
- #endif\r
-\r
- if( ulIPAddress == 0UL)\r
- {\r
- ulIPAddress = prvGetHostByName( pcHostName, xIdentifier, xReadTimeOut_ms );\r
- }\r
-\r
- return ulIPAddress;\r
+TickType_t xReadTimeOut_ms = ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME;\r
+TickType_t xIdentifier = 0;\r
+\r
+ /* If the supplied hostname is IP address, convert it to uint32_t\r
+ and return. */\r
+ #if( ipconfigINCLUDE_FULL_INET_ADDR == 1 )\r
+ {\r
+ ulIPAddress = FreeRTOS_inet_addr( pcHostName );\r
+ }\r
+ #endif /* ipconfigINCLUDE_FULL_INET_ADDR == 1 */\r
+\r
+ /* If a DNS cache is used then check the cache before issuing another DNS\r
+ request. */\r
+ #if( ipconfigUSE_DNS_CACHE == 1 )\r
+ {\r
+ if( ulIPAddress == 0UL )\r
+ {\r
+ ulIPAddress = FreeRTOS_dnslookup( pcHostName );\r
+ if( ulIPAddress != 0 )\r
+ {\r
+ FreeRTOS_debug_printf( ( "FreeRTOS_gethostbyname: found '%s' in cache: %lxip\n", pcHostName, ulIPAddress ) );\r
+ }\r
+ else\r
+ {\r
+ /* prvGetHostByName will be called to start a DNS lookup */\r
+ }\r
+ }\r
+ }\r
+ #endif /* ipconfigUSE_DNS_CACHE == 1 */\r
+\r
+ /* Generate a unique identifier. */\r
+ if( 0 == ulIPAddress )\r
+ {\r
+ xIdentifier = ( TickType_t )ipconfigRAND32( );\r
+ }\r
+\r
+ #if( ipconfigDNS_USE_CALLBACKS != 0 )\r
+ {\r
+ if( pCallback != NULL )\r
+ {\r
+ if( ulIPAddress == 0UL )\r
+ {\r
+ /* The user has provided a callback function, so do not block on recvfrom() */\r
+ if( 0 != xIdentifier )\r
+ {\r
+ xReadTimeOut_ms = 0;\r
+ vDNSSetCallBack( pcHostName, pvSearchID, pCallback, xTimeout, ( TickType_t )xIdentifier );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* The IP address is known, do the call-back now. */\r
+ pCallback( pcHostName, pvSearchID, ulIPAddress );\r
+ }\r
+ }\r
+ }\r
+ #endif\r
+\r
+ if( ulIPAddress == 0UL && 0 != xIdentifier )\r
+ {\r
+ ulIPAddress = prvGetHostByName( pcHostName, xIdentifier, xReadTimeOut_ms );\r
+ }\r
+\r
+ return ulIPAddress;\r
}\r
/*-----------------------------------------------------------*/\r
\r
BaseType_t xAttempt;\r
int32_t lBytes;\r
size_t xPayloadLength, xExpectedPayloadLength;\r
-TickType_t xWriteTimeOut_ms = 100U;\r
+TickType_t xWriteTimeOut_ms = ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME;\r
\r
#if( ipconfigUSE_LLMNR == 1 )\r
BaseType_t bHasDot = pdFALSE;\r
if( lBytes > 0 )\r
{\r
/* The reply was received. Process it. */\r
- ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, xIdentifier );\r
+ ulIPAddress = prvParseDNSReply( pucUDPPayloadBuffer, lBytes, xIdentifier );\r
\r
/* Finished with the buffer. The zero copy interface\r
is being used, so the buffer must be freed by the\r
\r
#if( ipconfigUSE_DNS_CACHE == 1 )\r
\r
- static uint8_t *prvReadNameField( uint8_t *pucByte, char *pcName, BaseType_t xLen )\r
+ static uint8_t *prvReadNameField( uint8_t *pucByte, size_t xSourceLen, char *pcName, size_t xDestLen )\r
{\r
- BaseType_t xNameLen = 0;\r
+ size_t xNameLen = 0;\r
+ BaseType_t xCount;\r
+\r
+ if( 0 == xSourceLen )\r
+ {\r
+ return NULL;\r
+ }\r
+\r
/* Determine if the name is the fully coded name, or an offset to the name\r
elsewhere in the message. */\r
if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )\r
{\r
/* Jump over the two byte offset. */\r
- pucByte += sizeof( uint16_t );\r
-\r
+ if( xSourceLen > sizeof( uint16_t ) )\r
+ {\r
+ pucByte += sizeof( uint16_t );\r
+ }\r
+ else\r
+ {\r
+ pucByte = NULL;\r
+ }\r
}\r
else\r
{\r
- /* pucByte points to the full name. Walk over the string. */\r
- while( *pucByte != 0x00 )\r
+ /* pucByte points to the full name. Walk over the string. */\r
+ while( NULL != pucByte && *pucByte != 0x00 && xSourceLen > 1 )\r
{\r
- BaseType_t xCount;\r
- if( xNameLen && xNameLen < xLen - 1 )\r
- pcName[xNameLen++] = '.';\r
- for( xCount = *(pucByte++); xCount--; pucByte++ )\r
+ /* If this is not the first time through the loop, then add a \r
+ separator in the output. */\r
+ if( xNameLen > 0 && xNameLen < xDestLen - 1 )\r
+ {\r
+ pcName[ xNameLen++ ] = '.';\r
+ }\r
+\r
+ /* Process the first/next sub-string. */\r
+ for( xCount = *(pucByte++), xSourceLen--;\r
+ xCount-- && xSourceLen > 1; \r
+ pucByte++, xSourceLen-- )\r
{\r
- if( xNameLen < xLen - 1 )\r
- pcName[xNameLen++] = *( ( char * ) pucByte );\r
+ if( xNameLen < xDestLen - 1 )\r
+ {\r
+ pcName[ xNameLen++ ] = *( ( char * )pucByte );\r
+ }\r
+ else\r
+ {\r
+ /* DNS name is too big for the provided buffer. */\r
+ pucByte = NULL;\r
+ break;\r
+ }\r
}\r
}\r
\r
- pucByte++;\r
+ /* Confirm that a fully formed name was found. */\r
+ if( NULL != pucByte )\r
+ {\r
+ if( 0x00 == *pucByte )\r
+ {\r
+ pucByte++;\r
+ xSourceLen--;\r
+ pcName[ xNameLen++ ] = '\0';\r
+ }\r
+ else\r
+ {\r
+ pucByte = NULL;\r
+ }\r
+ }\r
}\r
\r
return pucByte;\r
#endif /* ipconfigUSE_DNS_CACHE == 1 */\r
/*-----------------------------------------------------------*/\r
\r
-static uint8_t *prvSkipNameField( uint8_t *pucByte )\r
+static uint8_t *prvSkipNameField( uint8_t *pucByte, size_t xSourceLen )\r
{\r
- /* Determine if the name is the fully coded name, or an offset to the name\r
+ size_t xChunkLength;\r
+\r
+ if( 0 == xSourceLen )\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ /* Determine if the name is the fully coded name, or an offset to the name\r
elsewhere in the message. */\r
if( ( *pucByte & dnsNAME_IS_OFFSET ) == dnsNAME_IS_OFFSET )\r
{\r
/* Jump over the two byte offset. */\r
- pucByte += sizeof( uint16_t );\r
-\r
+ if( xSourceLen > sizeof( uint16_t ) )\r
+ {\r
+ pucByte += sizeof( uint16_t );\r
+ }\r
+ else\r
+ {\r
+ pucByte = NULL;\r
+ }\r
}\r
else\r
{\r
- /* pucByte points to the full name. Walk over the string. */\r
- while( *pucByte != 0x00 )\r
+ /* pucByte points to the full name. Walk over the string. */\r
+ while( *pucByte != 0x00 && xSourceLen > 1 )\r
{\r
- /* The number of bytes to jump for each name section is stored in the byte\r
- before the name section. */\r
- pucByte += ( *pucByte + 1 );\r
+ xChunkLength = *pucByte + 1;\r
+\r
+ if( xSourceLen > xChunkLength )\r
+ {\r
+ xSourceLen -= xChunkLength;\r
+ pucByte += xChunkLength;\r
+ }\r
+ else\r
+ {\r
+ pucByte = NULL;\r
+ break;\r
+ }\r
}\r
\r
- pucByte++;\r
+ /* Confirm that a fully formed name was found. */\r
+ if( NULL != pucByte )\r
+ {\r
+ if( 0x00 == *pucByte )\r
+ {\r
+ pucByte++;\r
+ }\r
+ else\r
+ {\r
+ pucByte = NULL;\r
+ }\r
+ }\r
}\r
\r
return pucByte;\r
\r
uint32_t ulDNSHandlePacket( NetworkBufferDescriptor_t *pxNetworkBuffer )\r
{\r
-uint8_t *pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );\r
-DNSMessage_t *pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;\r
+uint8_t *pucUDPPayloadBuffer;\r
+size_t xPlayloadBufferLength;\r
+DNSMessage_t *pxDNSMessageHeader;\r
\r
- prvParseDNSReply( pucUDPPayloadBuffer, ( uint32_t ) pxDNSMessageHeader->usIdentifier );\r
+ xPlayloadBufferLength = pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t );\r
+ if ( xPlayloadBufferLength < sizeof( DNSMessage_t ) )\r
+ {\r
+ return pdFAIL;\r
+ }\r
+\r
+ pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );\r
+ pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;\r
+\r
+ if( pxNetworkBuffer->xDataLength > sizeof( UDPPacket_t ) )\r
+ {\r
+ prvParseDNSReply( pucUDPPayloadBuffer, \r
+ xPlayloadBufferLength,\r
+ ( uint32_t )pxDNSMessageHeader->usIdentifier );\r
+ }\r
\r
/* The packet was not consumed. */\r
return pdFAIL;\r
uint32_t ulNBNSHandlePacket (NetworkBufferDescriptor_t *pxNetworkBuffer )\r
{\r
UDPPacket_t *pxUDPPacket = ( UDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer;\r
- uint8_t *pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + sizeof( *pxUDPPacket );\r
+ uint8_t *pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + sizeof( UDPPacket_t );\r
\r
- prvTreatNBNS( pucUDPPayloadBuffer, pxUDPPacket->xIPHeader.ulSourceIPAddress );\r
+ if( pxNetworkBuffer->xDataLength > sizeof( UDPPacket_t) )\r
+ {\r
+ prvTreatNBNS( pucUDPPayloadBuffer, \r
+ pxNetworkBuffer->xDataLength - sizeof( UDPPacket_t ),\r
+ pxUDPPacket->xIPHeader.ulSourceIPAddress );\r
+ }\r
\r
/* The packet was not consumed. */\r
return pdFAIL;\r
#endif /* ipconfigUSE_NBNS */\r
/*-----------------------------------------------------------*/\r
\r
-static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, TickType_t xIdentifier )\r
+static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, size_t xBufferLength, TickType_t xIdentifier )\r
{\r
DNSMessage_t *pxDNSMessageHeader;\r
+DNSAnswerRecord_t *pxDNSAnswerRecord;\r
uint32_t ulIPAddress = 0UL;\r
#if( ipconfigUSE_LLMNR == 1 )\r
char *pcRequestedName = NULL;\r
#endif\r
uint8_t *pucByte;\r
+size_t xSourceBytesRemaining;\r
uint16_t x, usDataLength, usQuestions;\r
#if( ipconfigUSE_LLMNR == 1 )\r
uint16_t usType = 0, usClass = 0;\r
#endif\r
#if( ipconfigUSE_DNS_CACHE == 1 )\r
- 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. */\r
+ char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ] = ""; \r
#endif\r
\r
+ /* Ensure that the buffer is of at least minimal DNS message length. */\r
+ if( xBufferLength < sizeof( DNSMessage_t ) )\r
+ {\r
+ return dnsPARSE_ERROR;\r
+ }\r
+ else\r
+ {\r
+ xSourceBytesRemaining = xBufferLength;\r
+ }\r
+\r
+ /* Parse the DNS message header. */\r
pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer;\r
\r
if( pxDNSMessageHeader->usIdentifier == ( uint16_t ) xIdentifier )\r
{\r
/* Start at the first byte after the header. */\r
pucByte = pucUDPPayloadBuffer + sizeof( DNSMessage_t );\r
+ xSourceBytesRemaining -= sizeof( DNSMessage_t );\r
\r
/* Skip any question records. */\r
usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions );\r
#if( ipconfigUSE_DNS_CACHE == 1 )\r
if( x == 0 )\r
{\r
- pucByte = prvReadNameField( pucByte, pcName, sizeof( pcName ) );\r
+ pucByte = prvReadNameField( pucByte, \r
+ xSourceBytesRemaining,\r
+ pcName, \r
+ sizeof( pcName ) );\r
+ \r
+ /* Check for a malformed response. */\r
+ if( NULL == pucByte )\r
+ {\r
+ return dnsPARSE_ERROR;\r
+ }\r
+ else\r
+ {\r
+ xSourceBytesRemaining = ( pucUDPPayloadBuffer + xBufferLength ) - pucByte;\r
+ }\r
}\r
else\r
#endif /* ipconfigUSE_DNS_CACHE */\r
{\r
/* Skip the variable length pcName field. */\r
- pucByte = prvSkipNameField( pucByte );\r
- }\r
-\r
- #if( ipconfigUSE_LLMNR == 1 )\r
- {\r
- /* usChar2u16 returns value in host endianness. */\r
- usType = usChar2u16( pucByte );\r
- usClass = usChar2u16( pucByte + 2 );\r
- }\r
- #endif /* ipconfigUSE_LLMNR */\r
-\r
- /* Skip the type and class fields. */\r
- pucByte += sizeof( uint32_t );\r
+ pucByte = prvSkipNameField( pucByte, \r
+ xSourceBytesRemaining );\r
+ \r
+ /* Check for a malformed response. */\r
+ if( NULL == pucByte )\r
+ {\r
+ return dnsPARSE_ERROR;\r
+ }\r
+ else\r
+ {\r
+ xSourceBytesRemaining = pucUDPPayloadBuffer + xBufferLength - pucByte;\r
+ }\r
+ }\r
+\r
+ /* Check the remaining buffer size. */\r
+ if( xSourceBytesRemaining >= sizeof( uint32_t ) )\r
+ {\r
+ #if( ipconfigUSE_LLMNR == 1 )\r
+ {\r
+ /* usChar2u16 returns value in host endianness */\r
+ usType = usChar2u16( pucByte );\r
+ usClass = usChar2u16( pucByte + 2 );\r
+ }\r
+ #endif /* ipconfigUSE_LLMNR */\r
+\r
+ /* Skip the type and class fields. */\r
+ pucByte += sizeof( uint32_t );\r
+ xSourceBytesRemaining -= sizeof( uint32_t );\r
+ }\r
+ else\r
+ {\r
+ /* Malformed response. */\r
+ return dnsPARSE_ERROR;\r
+ }\r
}\r
\r
- /* Search through the answers records. */\r
+ /* Search through the answer records. */\r
pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers );\r
\r
if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )\r
{\r
for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )\r
{\r
- pucByte = prvSkipNameField( pucByte );\r
-\r
- /* Is the type field that of an A record? */\r
- if( usChar2u16( pucByte ) == dnsTYPE_A_HOST )\r
+ pucByte = prvSkipNameField( pucByte,\r
+ xSourceBytesRemaining );\r
+\r
+ /* Check for a malformed response. */\r
+ if( NULL == pucByte )\r
+ {\r
+ return dnsPARSE_ERROR;\r
+ }\r
+ else\r
+ {\r
+ xSourceBytesRemaining = pucUDPPayloadBuffer + xBufferLength - pucByte;\r
+ }\r
+\r
+ /* Is there enough data for an IPv4 A record answer and, if so, \r
+ is this an A record? */\r
+ if( xSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) && \r
+ usChar2u16( pucByte ) == dnsTYPE_A_HOST )\r
{\r
- /* This is the required record. Skip the type, class, and\r
- time to live fields, plus the first byte of the data\r
- length. */\r
- pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) );\r
+ /* This is the required record type and is of sufficient size. */\r
+ pxDNSAnswerRecord = ( DNSAnswerRecord_t * )pucByte;\r
\r
- /* Sanity check the data length. */\r
- if( ( size_t ) *pucByte == sizeof( uint32_t ) )\r
+ /* Sanity check the data length of an IPv4 answer. */\r
+ if( FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength ) == sizeof( uint32_t ) )\r
{\r
- /* Skip the second byte of the length. */\r
- pucByte++;\r
-\r
/* Copy the IP address out of the record. */\r
- memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) );\r
+ memcpy( &ulIPAddress, \r
+ pucByte + sizeof( DNSAnswerRecord_t ),\r
+ sizeof( uint32_t ) );\r
\r
#if( ipconfigUSE_DNS_CACHE == 1 )\r
{\r
- prvProcessDNSCache( pcName, &ulIPAddress, pdFALSE );\r
+ prvProcessDNSCache( pcName, &ulIPAddress, pxDNSAnswerRecord->ulTTL, pdFALSE );\r
}\r
#endif /* ipconfigUSE_DNS_CACHE */\r
#if( ipconfigDNS_USE_CALLBACKS != 0 )\r
#endif /* ipconfigDNS_USE_CALLBACKS != 0 */\r
}\r
\r
+ pucByte += sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t );\r
+ xSourceBytesRemaining -= ( sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) );\r
break;\r
}\r
- else\r
+ else if( xSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) )\r
{\r
- /* Skip the type, class and time to live fields. */\r
- pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) );\r
-\r
- /* Determine the length of the data in the field. */\r
- memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) );\r
- usDataLength = FreeRTOS_ntohs( usDataLength );\r
-\r
- /* Jump over the data length bytes, and the data itself. */\r
- pucByte += usDataLength + sizeof( uint16_t );\r
+ /* It's not an A record, so skip it. Get the header location \r
+ and then jump over the header. */\r
+ pxDNSAnswerRecord = ( DNSAnswerRecord_t * )pucByte;\r
+ pucByte += sizeof( DNSAnswerRecord_t );\r
+ xSourceBytesRemaining -= sizeof( DNSAnswerRecord_t );\r
+ \r
+ /* Determine the length of the answer data from the header. */\r
+ usDataLength = FreeRTOS_ntohs( pxDNSAnswerRecord->usDataLength );\r
+\r
+ /* Jump over the answer. */\r
+ if( xSourceBytesRemaining >= usDataLength )\r
+ {\r
+ pucByte += usDataLength;\r
+ xSourceBytesRemaining -= usDataLength;\r
+ }\r
+ else\r
+ {\r
+ /* Malformed response. */\r
+ return dnsPARSE_ERROR;\r
+ }\r
}\r
}\r
}\r
#if( ipconfigUSE_LLMNR == 1 )\r
- else if( ( usQuestions != ( uint16_t )0u ) && ( usType == ( uint16_t )dnsTYPE_A_HOST ) && ( usClass == ( uint16_t )dnsCLASS_IN ) )\r
+ else if( usQuestions && ( usType == dnsTYPE_A_HOST ) && ( usClass == dnsCLASS_IN ) )\r
{\r
/* If this is not a reply to our DNS request, it might an LLMNR\r
request. */\r
\r
if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) )\r
{\r
- BaseType_t xDataLength = pxNetworkBuffer->xDataLength + sizeof( UDPHeader_t ) +\r
+ BaseType_t xDataLength = xBufferLength + sizeof( UDPHeader_t ) +\r
sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );\r
\r
/* The field xDataLength was set to the length of the UDP payload.\r
{\r
pxAnswer = (LLMNRAnswer_t *)pucByte;\r
\r
- /* Leave 'usIdentifier' and 'usQuestions' untouched. */\r
+ /* We leave 'usIdentifier' and 'usQuestions' untouched */\r
vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE ); /* Set the response flag */\r
vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 ); /* Provide a single answer */\r
vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 ); /* No authority */\r
\r
#if( ipconfigUSE_NBNS == 1 )\r
\r
- static void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer, uint32_t ulIPAddress )\r
+ static void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer, size_t xBufferLength, uint32_t ulIPAddress )\r
{\r
uint16_t usFlags, usType, usClass;\r
uint8_t *pucSource, *pucTarget;\r
uint8_t ucByte;\r
uint8_t ucNBNSName[ 17 ];\r
\r
+ /* Check for minimum buffer size. */\r
+ if( xBufferLength < sizeof( NBNSRequest_t ) )\r
+ {\r
+ return;\r
+ }\r
+ \r
+ /* Read the request flags in host endianness. */\r
usFlags = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usFlags ) );\r
\r
if( ( usFlags & dnsNBNS_FLAGS_OPCODE_MASK ) == dnsNBNS_FLAGS_OPCODE_QUERY )\r
{\r
/* If this is a response from another device,\r
add the name to the DNS cache */\r
- prvProcessDNSCache( ( char * ) ucNBNSName, &ulIPAddress, pdFALSE );\r
+ prvProcessDNSCache( ( char * ) ucNBNSName, &ulIPAddress, 0, pdFALSE );\r
}\r
}\r
#else\r
{\r
NetworkBufferDescriptor_t *pxNewBuffer;\r
BaseType_t xDataLength = pxNetworkBuffer->xDataLength + sizeof( UDPHeader_t ) +\r
-\r
sizeof( EthernetHeader_t ) + sizeof( IPHeader_t );\r
\r
/* The field xDataLength was set to the length of the UDP payload.\r
pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );\r
\r
/* calculate the UDP checksum for outgoing package */\r
- usGenerateProtocolChecksum( ( uint8_t* ) pxUDPPacket, pdTRUE );\r
+ usGenerateProtocolChecksum( ( uint8_t* ) pxUDPPacket, lNetLength, pdTRUE );\r
}\r
#endif\r
\r
\r
#if( ipconfigUSE_DNS_CACHE == 1 )\r
\r
- static void prvProcessDNSCache( const char *pcName, uint32_t *pulIP, BaseType_t xLookUp )\r
+ static void prvProcessDNSCache( const char *pcName, uint32_t *pulIP, uint32_t ulTTL, BaseType_t xLookUp )\r
{\r
BaseType_t x;\r
BaseType_t xFound = pdFALSE;\r
+ uint32_t ulCurrentTimeSeconds = \r
+ xTaskGetTickCount( ) / portTICK_PERIOD_MS / 1000;\r
static BaseType_t xFreeEntry = 0;\r
\r
/* For each entry in the DNS cache table. */\r
break;\r
}\r
\r
- if( strncmp( xDNSCache[ x ].pcName, pcName, sizeof( xDNSCache[ x ].pcName ) ) == 0 )\r
+ if( 0 == strcmp( xDNSCache[ x ].pcName, pcName ) )\r
{\r
/* Is this function called for a lookup or to add/update an IP address? */\r
if( xLookUp != pdFALSE )\r
{\r
- *pulIP = xDNSCache[ x ].ulIPAddress;\r
+ /* Confirm that the record is still fresh. */\r
+ if( ulCurrentTimeSeconds < \r
+ xDNSCache[ x ].ulTimeWhenAddedInSeconds + \r
+ FreeRTOS_ntohl( xDNSCache[ x ].ulTTL ) )\r
+ {\r
+ *pulIP = xDNSCache[ x ].ulIPAddress;\r
+ }\r
+ else\r
+ {\r
+ /* Age out the old cached record. */\r
+ xDNSCache[ x ].pcName[ 0 ] = 0;\r
+ }\r
}\r
else\r
{\r
xDNSCache[ x ].ulIPAddress = *pulIP;\r
+ xDNSCache[ x ].ulTTL = ulTTL;\r
+ xDNSCache[ x ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;\r
}\r
\r
xFound = pdTRUE;\r
}\r
else\r
{\r
- /* Called to add or update an item */\r
- strncpy( xDNSCache[ xFreeEntry ].pcName, pcName, sizeof( xDNSCache[ xFreeEntry ].pcName ) );\r
- xDNSCache[ xFreeEntry ].ulIPAddress = *pulIP;\r
-\r
- xFreeEntry++;\r
- if( xFreeEntry == ipconfigDNS_CACHE_ENTRIES )\r
- {\r
- xFreeEntry = 0;\r
- }\r
+ /* Add or update the item. */\r
+ if( strlen( pcName ) < ipconfigDNS_CACHE_NAME_LENGTH )\r
+ {\r
+ strcpy( xDNSCache[ xFreeEntry ].pcName, pcName );\r
+\r
+ xDNSCache[ xFreeEntry ].ulIPAddress = *pulIP;\r
+ xDNSCache[ xFreeEntry ].ulTTL = ulTTL;\r
+ xDNSCache[ xFreeEntry ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;\r
+\r
+ xFreeEntry++;\r
+ if( xFreeEntry == ipconfigDNS_CACHE_ENTRIES )\r
+ {\r
+ xFreeEntry = 0;\r
+ }\r
+ }\r
}\r
}\r
\r
\r
#endif /* ipconfigUSE_DNS != 0 */\r
\r
+/*-----------------------------------------------------------*/\r
\r
+/* Provide access to private members for testing. */\r
+#ifdef AMAZON_FREERTOS_ENABLE_UNIT_TESTS\r
+ #include "aws_freertos_tcp_test_access_dns_define.h"\r
+#endif\r