static void prvCheckOptions()

in amazon-freertos/lib/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_IP.c [1136:1316]


static void prvCheckOptions( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer )
{
TCPPacket_t * pxTCPPacket;
TCPHeader_t * pxTCPHeader;
const unsigned char *pucPtr;
const unsigned char *pucLast;
TCPWindow_t *pxTCPWindow;
UBaseType_t uxNewMSS;

	pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer );
	pxTCPHeader = &pxTCPPacket->xTCPHeader;

	/* A character pointer to iterate through the option data */
	pucPtr = pxTCPHeader->ucOptdata;
	pucLast = pucPtr + (((pxTCPHeader->ucTCPOffset >> 4) - 5) << 2);
	pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;

	/* Validate options size calculation. */
	if( pucLast > ( pxNetworkBuffer->pucEthernetBuffer + pxNetworkBuffer->xDataLength ) )
	{
		return;
	}

	/* The comparison with pucLast is only necessary in case the option data are
	corrupted, we don't like to run into invalid memory and crash. */
	while( pucPtr < pucLast )
	{
		UBaseType_t xRemainingOptionsBytes = pucLast - pucPtr;

		if( pucPtr[ 0 ] == TCP_OPT_END )
		{
			/* End of options. */
			break;
		}
		if( pucPtr[ 0 ] == TCP_OPT_NOOP)
		{
			/* NOP option, inserted to make the length a multiple of 4. */
			pucPtr++;
			continue;
		}

		/* Any other well-formed option must be at least two bytes: the option
		type byte followed by a length byte. */
		if( xRemainingOptionsBytes < 2 )
		{
			break;
		}
#if( ipconfigUSE_TCP_WIN != 0 )
		else if( pucPtr[ 0 ] == TCP_OPT_WSOPT )
		{
			/* Confirm that the option fits in the remaining buffer space. */
			if( ( xRemainingOptionsBytes < TCP_OPT_WSOPT_LEN ) || ( pucPtr[ 1 ] != TCP_OPT_WSOPT_LEN ) )
			{
				break;
			}

			pxSocket->u.xTCP.ucPeerWinScaleFactor = pucPtr[ 2 ];
			pxSocket->u.xTCP.bits.bWinScaling = pdTRUE_UNSIGNED;
			pucPtr += TCP_OPT_WSOPT_LEN;
		}
#endif	/* ipconfigUSE_TCP_WIN */
		else if( pucPtr[ 0 ] == TCP_OPT_MSS )
		{
			/* Confirm that the option fits in the remaining buffer space. */
			if( ( xRemainingOptionsBytes < TCP_OPT_MSS_LEN )|| ( pucPtr[ 1 ] != TCP_OPT_MSS_LEN ) )
			{
				break;
			}

			/* An MSS option with the correct option length.  FreeRTOS_htons()
			is not needed here because usChar2u16() already returns a host
			endian number. */
			uxNewMSS = usChar2u16( pucPtr + 2 );

			if( pxSocket->u.xTCP.usInitMSS != uxNewMSS )
			{
				/* Perform a basic check on the the new MSS. */
				if( uxNewMSS == 0 )
				{
					break;
				}

				FreeRTOS_debug_printf( ( "MSS change %u -> %lu\n", pxSocket->u.xTCP.usInitMSS, uxNewMSS ) );
			}

			if( pxSocket->u.xTCP.usInitMSS > uxNewMSS )
			{
				/* our MSS was bigger than the MSS of the other party: adapt it. */
				pxSocket->u.xTCP.bits.bMssChange = pdTRUE_UNSIGNED;
				if( ( pxTCPWindow != NULL ) && ( pxSocket->u.xTCP.usCurMSS > uxNewMSS ) )
				{
					/* The peer advertises a smaller MSS than this socket was
					using.  Use that as well. */
					FreeRTOS_debug_printf( ( "Change mss %d => %lu\n", pxSocket->u.xTCP.usCurMSS, uxNewMSS ) );
					pxSocket->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS;
				}
				pxTCPWindow->xSize.ulRxWindowLength = ( ( uint32_t ) uxNewMSS ) * ( pxTCPWindow->xSize.ulRxWindowLength / ( ( uint32_t ) uxNewMSS ) );
				pxTCPWindow->usMSSInit = ( uint16_t ) uxNewMSS;
				pxTCPWindow->usMSS = ( uint16_t ) uxNewMSS;
				pxSocket->u.xTCP.usInitMSS = ( uint16_t ) uxNewMSS;
				pxSocket->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS;
			}

			#if( ipconfigUSE_TCP_WIN != 1 )
				/* Without scaled windows, MSS is the only interesting option. */
				break;
			#else
				/* Or else we continue to check another option: selective ACK. */
				pucPtr += TCP_OPT_MSS_LEN;
			#endif	/* ipconfigUSE_TCP_WIN != 1 */
		}
		else
		{
			/* All other options have a length field, so that we easily
			can skip past them. */
			unsigned char len = pucPtr[ 1 ];
			if( ( len < 2 ) || ( len > xRemainingOptionsBytes ) )
			{
				/* If the length field is too small or too big, the options are malformed.
				Don't process them further. */
				break;
			}

			#if( ipconfigUSE_TCP_WIN == 1 )
			{
				/* Selective ACK: the peer has received a packet but it is missing earlier
				packets.  At least this packet does not need retransmission anymore
				ulTCPWindowTxSack( ) takes care of this administration. */
				if( pucPtr[0] == TCP_OPT_SACK_A )
				{
					len -= 2;
					pucPtr += 2;

					while( len >= 8 )
					{
					uint32_t ulFirst = ulChar2u32( pucPtr );
					uint32_t ulLast  = ulChar2u32( pucPtr + 4 );
					uint32_t ulCount = ulTCPWindowTxSack( &pxSocket->u.xTCP.xTCPWindow, ulFirst, ulLast );
						/* ulTCPWindowTxSack( ) returns the number of bytes which have been acked
						starting from the head position.
						Advance the tail pointer in txStream. */
						if( ( pxSocket->u.xTCP.txStream  != NULL ) && ( ulCount > 0 ) )
						{
							/* Just advancing the tail index, 'ulCount' bytes have been confirmed. */
							uxStreamBufferGet( pxSocket->u.xTCP.txStream, 0, NULL, ( size_t ) ulCount, pdFALSE );
							pxSocket->xEventBits |= eSOCKET_SEND;

							#if ipconfigSUPPORT_SELECT_FUNCTION == 1
							{
								if( pxSocket->xSelectBits & eSELECT_WRITE )
								{
									/* The field 'xEventBits' is used to store regular socket events (at most 8),
									as well as 'select events', which will be left-shifted */
									pxSocket->xEventBits |= ( eSELECT_WRITE << SOCKET_EVENT_BIT_COUNT );
								}
							}
							#endif

							/* In case the socket owner has installed an OnSent handler,
							call it now. */
							#if( ipconfigUSE_CALLBACKS == 1 )
							{
								if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleSent ) )
								{
									pxSocket->u.xTCP.pxHandleSent( (Socket_t *)pxSocket, ulCount );
								}
							}
							#endif /* ipconfigUSE_CALLBACKS == 1  */
						}
						pucPtr += 8;
						len -= 8;
					}
					/* len should be 0 by now. */
				}
			}
			#endif	/* ipconfigUSE_TCP_WIN == 1 */

			pucPtr += len;
		}
	}
}