TFTPserver improvements

attached is an improved TFTP server I use it to write in the a FLASH ROM for loadng a XIlinx FPGA. This one works 🙂 . There was an inssue with double responses, and buffer returning. Only a few lines changed Happy to assist anyone with it, or provide flash writing code . tftpserver.c ~~~ /* FreeRTOS V9.0.0 – Copyright (C) 2016 Real Time Engineers Ltd. All rights reserved
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

This file is part of the FreeRTOS distribution.

FreeRTOS is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License (version 2) as published by the
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.

***************************************************************************
>>!   NOTE: The modification to the GPL is included to allow you to     !<<
>>!   distribute a combined work that includes FreeRTOS without being   !<<
>>!   obliged to provide the source code for proprietary components     !<<
>>!   outside of the FreeRTOS kernel.                                   !<<
***************************************************************************

FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  Full license text is available on the following
link: http://www.freertos.org/a00114.html

***************************************************************************
 *                                                                       *
 *    FreeRTOS provides completely free yet professionally developed,    *
 *    robust, strictly quality controlled, supported, and cross          *
 *    platform software that is more than just the market leader, it     *
 *    is the industry's de facto standard.                               *
 *                                                                       *
 *    Help yourself get started quickly while simultaneously helping     *
 *    to support the FreeRTOS project by purchasing a FreeRTOS           *
 *    tutorial book, reference manual, or both:                          *
 *    http://www.FreeRTOS.org/Documentation                              *
 *                                                                       *
***************************************************************************

http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
the FAQ page "My application does not run, what could be wrong?".  Have you
defined configASSERT()?

http://www.FreeRTOS.org/support - In return for receiving this top quality
embedded software for free we request you assist our global community by
participating in the support forum.

http://www.FreeRTOS.org/training - Investing in training allows your team to
be as productive as possible as early as possible.  Now you can receive
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
Ltd, and the world's leading authority on the world's leading RTOS.

http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.

http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.

http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.

http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.

1 tab == 4 spaces!
*/ /* * A basic TFTP server that can currently only be used to receive files, and not * send files. This is a slim implementation intended for use in boot loaders * and other applications that require over the air reception of files. */ /* Standard includes. */

include <stdint.h>

include <stdio.h>

include <string.h>

/* FreeRTOS includes. */

include “FreeRTOS.h”

include “task.h”

/* FreeRTOS+TCP includes. */

include “FreeRTOS_IP.h”

include “FreeRTOS_Sockets.h”

if( ipconfigALLOWSOCKETSENDWITHOUTBIND != 1 )

#error ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND must be set to one to use this TFTP server.

endif

if( configTICKRATEHZ > 1000 )

#error The TFTP server uses the pdMS_TO_TICKS() macro, so configTICK_RATE_HZ must be less than or equal to 1000

endif

ifndef ipconfigTFTPTIMEOUT_MS

#define ipconfigTFTP_TIME_OUT_MS            ( 30000 )

endif

ifndef ipconfigTFTPMAXRETRIES

#define ipconfigTFTP_MAX_RETRIES            ( 6 )

endif

define tftpPORTNUMBER ( ( uint16t ) 69 )

define tftpFILENAMEOFFSET ( 2 )

define tftpACKMESSAGELENGTH 4

define tftpMAXDATALENGTH ( ( size_t ) 512 )

typedef enum { eReadRequest = 1, eWriteRequest, eData, eAck, eError } eTFTPOpcode_t; /* Error codes from the RFC. */ typedef enum { eFileNotFound = 1, eAccessViolation, eDiskFull, eIllegalTFTPOperation, eUnknownTransferID, eFileAlreadyExists } eTFTPErrorCode_t;

include “packstructstart.h”

struct DataPacketHeader { uint16t usOpcode; uint16t usBlockNumber; }

include “packstructend.h”

typedef struct DataPacketHeader TFTPBlockNumberHeader_t; /* this will be the flash write routine */ extern size_t ff_fwrite(void * data, size_t nBytes, size_t blocksToWrite, uint32_t blocknumber); /* * Manages a single TFTP connection at a time. */ static void prvSimpleTFTPServerTask( void *pvParameters ); /* * Manage the reception of a file. If the file is received correctly then * return pdPASS, otherwise return pdFAIL. */ static BaseType_t prvReceiveFile( void *pxFile, struct freertos_sockaddr *pxClient ); /* * Send an error frame to the client. */ static void prvSendTFTPError( Socket_t xSocket, struct freertos_sockaddr *pxClient, eTFTPErrorCode_t eErrorCode ); /* * Check a received write request contains a potentially valid file name string, * and is a binary mode transfer. If so return a pointer to the file name with * the write request packet received from the network, otherwise return NULL. / static const char prvValidateWriteRequest( Sockett xSocket, struct freertossockaddr *pxClient, uint8_t *pucUDPPayloadBuffer ); /* * Called after a valid write request has been received to first check the file * does not already exist, and if the file does not exist, create the file ready * to be written. If the file did already exist, or if the file could not be * created, then NULL is returned – otherwise a handle to the created file is * returned. / static void prvValidateFileToWrite( Sockett xSocket, struct freertossockaddr *pxClient, const char *pcFileName ); /* * Send an acknowledgement packet to pxClient with block number usBlockNumber. */ static void prvSendAcknowledgement( Socket_t xSocket, struct freertos_sockaddr *pxClient, uint16_t usBlockNumber ); /* The index for the error string below MUST match the value of the applicable eTFTPErrorCode_t error code value. */ static const char *cErrorStrings[] = { NULL, /* Not valid. */ “File not found.”, “Access violation.”, “Disk full or allocation exceeded.”, “Illegal TFTP operation.”, “Unknown transfer ID.”, “File already exists.”, “No such user.” }; /———————————————————–/ void vStartTFTPServerTask( uint16t usStackSize, UBaseTypet uxPriority ) { /* A single server task is created. Currently this is only capable of managing one TFTP transfer at a time. / xTaskCreate( prvSimpleTFTPServerTask, “TFTPd”, usStackSize, NULL, uxPriority, NULL ); } /———————————————————–*/ static void prvSimpleTFTPServerTask( void *pvParameters ) { int32_t lBytes; uint8_t *pucUDPPayloadBuffer; struct freertos_sockaddr xClient, xBindAddress; uint32_t xClientLength = sizeof( xClient ), ulIPAddress; Socket_t xTFTPListeningSocket; const char *pcFileName; void *pxFile; int HaveReleasedBuffer = 0;
/* Just to prevent compiler warnings. */
( void ) pvParameters;

/* Attempt to open the socket.  The receive block time defaults to the max
delay, so there is no need to set that separately. */
xTFTPListeningSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
configASSERT( xTFTPListeningSocket != FREERTOS_INVALID_SOCKET );

/* Bind to the standard TFTP port. */
FreeRTOS_GetAddressConfiguration( &ulIPAddress, NULL, NULL, NULL );
xBindAddress.sin_addr = ulIPAddress;
xBindAddress.sin_port = FreeRTOS_htons( tftpPORT_NUMBER );
FreeRTOS_bind( xTFTPListeningSocket, &xBindAddress, sizeof( xBindAddress ) );

for( ;; )
{
    /* Look for the start of a new transfer on the TFTP port.  ulFlags has
    the zero copy bit set (FREERTOS_ZERO_COPY) indicating to the stack that
    a reference to the received data should be passed out to this task using
    the second parameter to the FreeRTOS_recvfrom() call.  When this is done
    the IP stack is no longer responsible for releasing the buffer, and the
    task *must* return the buffer to the stack when it is no longer
    needed. */
    lBytes = FreeRTOS_recvfrom( xTFTPListeningSocket, ( void * ) &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xClient, &xClientLength );

    if( lBytes > 0 ) {

        /* Could this be a new write request?  The opcode is contained in
        the first two bytes of the received data. */

        HaveReleasedBuffer=0;

        if( ( pucUDPPayloadBuffer[ 0 ] == ( uint8_t ) 0 ) && ( pucUDPPayloadBuffer[ 1 ] == ( uint8_t ) eWriteRequest ) ) {
            pcFileName = prvValidateWriteRequest( xTFTPListeningSocket, &xClient, pucUDPPayloadBuffer );

            if( pcFileName != NULL )    {
                pxFile = prvValidateFileToWrite( xTFTPListeningSocket, &xClient, pcFileName );
                FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );/* to do - better structure */
                HaveReleasedBuffer=1;
                if( pxFile != NULL ) {
                    /* Manage reception of the reset of the file. */
                    prvReceiveFile( pxFile, &xClient );
                }
                else {
                    /* bad file name */
                    prvSendTFTPError( xTFTPListeningSocket, &xClient, eFileNotFound );
                }
            }
            else {
                prvSendTFTPError( xTFTPListeningSocket, &xClient, eFileNotFound );

            }
        }
        else {

            /* Not a transfer ID handled by this server. */
            prvSendTFTPError( xTFTPListeningSocket, &xClient, eUnknownTransferID );
        }

        if (!HaveReleasedBuffer) FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );/* to do - better structure */
    }
    else {
        /* socket error  ?*/
    }
}
} /———————————————————–/ static BaseTypet prvReceiveFile( void *pxFile, struct freertossockaddr *pxClient ) { BaseType_t xReturn = pdPASS, xRetries = 0; uint16_t usExpectedBlockNumber; Socket_t xTFTPRxSocket = FREERTOS_INVALID_SOCKET; TickType_t xRxTimeout = pdMS_TO_TICKS( ipconfigTFTP_TIME_OUT_MS ); int32_t lBytes; uint8_t *pucFileBuffer; struct freertos_sockaddr xClient; uint32_t xClientLength = sizeof( xClient ); TFTPBlockNumberHeader_t *pxHeader; size_t xBlocksWritten, xBytesOfFileDataReceived = tftpMAX_DATA_LENGTH; const size_t xBlocksToWrite = 1;
/* The file is open for writing, now create the socket on which the file
will be received from the client.  Note the socket is not bound here - so
will be automatically bound to a port number selected by the IP stack when
it is used for the first time. */
xTFTPRxSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );



if((xTFTPRxSocket != FREERTOS_INVALID_SOCKET )&&( xTFTPRxSocket != FREERTOS_INVALID_SOCKET )) {
    /* The socket's Rx block time is set to the user configurable timeout
    value. */
    FreeRTOS_setsockopt( xTFTPRxSocket, 0, FREERTOS_SO_RCVTIMEO, &xRxTimeout, sizeof( xRxTimeout ) );

    /* Acknowledge the write request so the client starts to send the file.
    The first acknowledgment does not have a corresponding block number so
    the special case block number 0 is used. */
    usExpectedBlockNumber = 0;

    /*
    after first return, seems that some TFTP clients can't turnaround fast enough for this embedded
    so we just delay a little more.....
    */
    vTaskDelay(pdMS_TO_TICKS( 50 ));

    prvSendAcknowledgement( xTFTPRxSocket, pxClient, usExpectedBlockNumber );/* acknowledge block 0 (no data)  */


    do {
        /* Wait for next data packet.  Zero copy is used so it is the
        responsibility of this task to free the received data once it is no
        longer required. */
        lBytes = FreeRTOS_recvfrom( xTFTPRxSocket, ( void * ) &pucFileBuffer, 0, FREERTOS_ZERO_COPY, &xClient, &xClientLength );

        if( lBytes <= 0 ) {
            /* timed out ???? really - there is a 30 second timeout- it should have recovered.. give up now*/

            FreeRTOS_printf( ( "Error: Timeout.n" ) );

if 1

            xRetries++;
            if( xRetries > ipconfigTFTP_MAX_RETRIES )   {
                FreeRTOS_printf( ( "Error: Retry limit exceeded.n" ) );
                xReturn = pdFAIL;
            }

else

            xReturn = pdFAIL;

endif

        }

        else {
            /* Data received.  It is expected to be the next sequential
            block. */
            usExpectedBlockNumber++;
            pxHeader = ( TFTPBlockNumberHeader_t * ) pucFileBuffer;
            pxHeader->usOpcode = FreeRTOS_ntohs( pxHeader->usOpcode );
            pxHeader->usBlockNumber = FreeRTOS_ntohs( pxHeader->usBlockNumber );

            /* Is the data as expected and from the expected IP address and
            port? */
            if( ( pxHeader->usOpcode == ( uint16_t ) eData )         &&
                ( pxHeader->usBlockNumber == usExpectedBlockNumber ) &&
                ( pxClient->sin_addr == xClient.sin_addr )           &&
                ( pxClient->sin_port == xClient.sin_port ) )
                {
                /* Everything in the packet other than the header is file
                data. */
                xBytesOfFileDataReceived = ( size_t ) lBytes - sizeof( TFTPBlockNumberHeader_t );
                FreeRTOS_printf( ( "Received %d bytes of file data.n", ( int ) xBytesOfFileDataReceived ) );

if 1

                xReturn = CheckWriteVerifyRecvTFTPBlock( pucFileBuffer + sizeof( TFTPBlockNumberHeader_t ),
                                            xBytesOfFileDataReceived,
                                            (pxHeader->usBlockNumber)-1);

else

xReturn = 0; /* dry run without hitting flash */

endif

                FreeRTOS_ReleaseUDPPayloadBuffer( pucFileBuffer );

                if( xReturn != 0 ) {
                    /* File could not be written. */
                    prvSendTFTPError( xTFTPRxSocket, pxClient, eAccessViolation );
                    xReturn = pdFAIL;
                }
                else {
                    /* Ack the data then write the data to the file. */
                    prvSendAcknowledgement( xTFTPRxSocket, pxClient, usExpectedBlockNumber );
                    xReturn = pdPASS;
                    /* Start to receive the next block. */
                    xRetries = 0;
                }
            }
            else    {
                FreeRTOS_ReleaseUDPPayloadBuffer( pucFileBuffer );
                prvSendTFTPError( xTFTPRxSocket, pxClient, eIllegalTFTPOperation );
                xReturn = pdFAIL;
            }
        }

      /* Until a disk write fails, or the maximum number of retries is
      exceeded, or fewer bytes than tftpMAX_DATA_LENGTH are received (which
      indicates the end of the file). */
    } while( ( xReturn != pdFAIL ) && ( xBytesOfFileDataReceived == tftpMAX_DATA_LENGTH ) );

    FreeRTOS_printf( ( "Closing connection.n" ) );
    FreeRTOS_closesocket( xTFTPRxSocket );

}
else
{
    /* An error could be returned here, but it is probably cleaner to just
    time out as the error would have to be sent via the listening socket
    outside of this function. */
    FreeRTOS_printf( ( "Could not create socket to receive file.n" ) );
}

return xReturn;
} /———————————————————–/ void prvSendAcknowledgement( Sockett xSocket, struct freertossockaddr pxClient, uint16_t usBlockNumber ) { / Small fixed size buffer, so not much to be gained by using the zero copy interface, just send the buffer directly. */ TFTPBlockNumberHeader_t xAckMessage;
xAckMessage.usOpcode = FreeRTOS_htons( ( ( uint16_t ) eAck ) );
xAckMessage.usBlockNumber = FreeRTOS_htons( usBlockNumber );

FreeRTOS_sendto( xSocket, ( void * ) &xAckMessage, tftpACK_MESSAGE_LENGTH, 0, pxClient, sizeof( struct freertos_sockaddr ) );
} /———————————————————–/ static void* prvValidateFileToWrite( Sockett xSocket, struct freertossockaddr *pxClient, const char *pcFileName ) { void *pxFile;
FreeRTOS_printf( ( "Write request for %s receivedn", pcFileName ) );

/* The file cannot be received if it already exists.  Attempt to open the
file in read mode to see if it exists. */
#if 0
if (strcasecmp(pcFileName,"fpga2_k70t") == 0) return (void *)1;
else return (void *)0;
#else
    return (void *)1;/* any file... */
#endif
} /———————————————————–/ static const char* prvValidateWriteRequest( Sockett xSocket, struct freertossockaddr *pxClient, uint8_t *pucUDPPayloadBuffer ) { char *pcFileName; BaseType_t x; const char *pcOctedMode = “octet”;
/* pcFileName is set to point to the file name which is inside the write
request frame, so its important not to free the frame until the operation is
over.  The start of the file name string is after the opcode, so two bytes
into the packet. */
pcFileName = ( char * ) &( pucUDPPayloadBuffer[ tftpFILE_NAME_OFFSET ] );
const int ffconfigMAX_FILENAME = 16;
/* Sanity check the file name. */
for( x = 0; x < ffconfigMAX_FILENAME; x++ )
{
    if( pcFileName[ x ] == 0x00 )
    {
        /* The end of the string was located. */
        break;
    }
    else if( ( pcFileName[ x ] < ' ' ) || ( pcFileName[ x ] > '~' ) )
    {
        /* Not a valid file name character. */
        pcFileName = NULL;
        break;
    }
    else
    {
        /* Just a character in the file name. */
    }
}

if( pcFileName != NULL )
{
    /* Only binary transfers are supported, indicated by an 'octet' mode
    string following the file name. +1 to move past the null terminator to
    the start of the next string. */
    x++;
    if( strcasecmp( pcOctedMode, ( const char * ) &( pucUDPPayloadBuffer[ tftpFILE_NAME_OFFSET + x ] ) ) != 0 )
    {
        /* Not the expected mode. */
        prvSendTFTPError( xSocket, pxClient, eIllegalTFTPOperation );
        pcFileName = NULL;
    }
}
else
{
    prvSendTFTPError( xSocket, pxClient, eFileNotFound );
}

return pcFileName;
} /———————————————————–/ static void prvSendTFTPError( Sockett xSocket, struct freertossockaddr pxClient, eTFTPErrorCode_t eErrorCode ) { uint8_t *pucUDPPayloadBuffer = NULL; const size_t xFixedSizePart = ( size_t ) 5; / 2 byte opcode, plus two byte error code, plus string terminating 0. / const size_t xNumberOfErrorStrings = sizeof( cErrorStrings ) / sizeof( char * ); size_t xErrorCode = ( size_t ) eErrorCode, xTotalLength = 0; / Only initialised to keep compiler quiet. */ const char *pcErrorString = NULL; int32_t lReturned;
/* The total size of the packet to be sent depends on the length of the
error string. */
if( xErrorCode < xNumberOfErrorStrings )
{
    pcErrorString = cErrorStrings[ xErrorCode ];

    /* This task is going to send using the zero copy interface.  The data
    being sent is therefore written directly into a buffer that is passed
    into, rather than copied into, the FreeRTOS_sendto() function.  First
    obtain a buffer of adequate length from the IP stack into which the
    error packet will be written.  Although a max delay is used, the actual
    delay will be capped to ipconfigMAX_SEND_BLOCK_TIME_TICKS. */
    xTotalLength = strlen( pcErrorString ) + xFixedSizePart;
    pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xTotalLength, portMAX_DELAY );
}

if( pucUDPPayloadBuffer != NULL )
{
    FreeRTOS_printf( ( "Error: %sn", pcErrorString ) );

    /* Create error packet: Opcode. */
    pucUDPPayloadBuffer[ 0 ] = 0;
    pucUDPPayloadBuffer[ 1 ] = ( uint8_t ) eError;

    /* Create error packet: Error code. */
    pucUDPPayloadBuffer[ 2 ] = 0;
    pucUDPPayloadBuffer[ 3 ] = ( uint8_t ) eErrorCode;

    /* Create error packet: Error string. */
    strcpy( ( ( char * ) &( pucUDPPayloadBuffer[ 4 ] ) ), pcErrorString );

    /* Pass the buffer into the send function.  ulFlags has the
    FREERTOS_ZERO_COPY bit set so the IP stack will take control of the
    buffer rather than copy data out of the buffer. */
    lReturned = FreeRTOS_sendto( xSocket,                       /* The socket to which the error frame is sent. */
                                ( void * ) pucUDPPayloadBuffer, /* A pointer to the the data being sent. */
                                xTotalLength,                   /* The length of the data being sent. */
                                FREERTOS_ZERO_COPY,             /* ulFlags with the FREERTOS_ZERO_COPY bit set. */
                                pxClient,           /* Where the data is being sent. */
                                sizeof( *pxClient ) );

    if( lReturned == 0 )
    {
        /* The send operation failed, so this task is still responsible
        for the buffer obtained from the IP stack.  To ensure the buffer
        is not lost it must either be used again, or, as in this case,
        returned to the IP stack using FreeRTOS_ReleaseUDPPayloadBuffer(). */
        FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
    }
    else
    {
        /* The send was successful so the IP stack is now managing the
        buffer pointed to by pucUDPPayloadBuffer, and the IP stack will
        return the buffer once it has been sent. */
    }
}
} ~~~

TFTPserver improvements

Great thanks. Sorry it took so long for your post to appear – for some reason it went into moderation and we don’t receive notifications when that happens (it should happen in the first place).

TFTPserver improvements

No sweat. couldnt figure out how to attach a file ?