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