summaryrefslogtreecommitdiffstats
path: root/AppPkg/Applications/Sockets/TftpServer/TftpServer.c
diff options
context:
space:
mode:
Diffstat (limited to 'AppPkg/Applications/Sockets/TftpServer/TftpServer.c')
-rw-r--r--AppPkg/Applications/Sockets/TftpServer/TftpServer.c3054
1 files changed, 1527 insertions, 1527 deletions
diff --git a/AppPkg/Applications/Sockets/TftpServer/TftpServer.c b/AppPkg/Applications/Sockets/TftpServer/TftpServer.c
index a1e19c26f9..9b522d1b55 100644
--- a/AppPkg/Applications/Sockets/TftpServer/TftpServer.c
+++ b/AppPkg/Applications/Sockets/TftpServer/TftpServer.c
@@ -1,1527 +1,1527 @@
-/*++
- This file contains an 'Intel UEFI Application' and is
- licensed for Intel CPUs and chipsets under the terms of your
- license agreement with Intel or your vendor. This file may
- be modified by the user, subject to additional terms of the
- license agreement
---*/
-/*++
-
-Copyright (c) 2011 Intel Corporation. All rights reserved
-This software and associated documentation (if any) is furnished
-under a license and may only be used or copied in accordance
-with the terms of the license. Except as permitted by such
-license, no part of this software or documentation may be
-reproduced, stored in a retrieval system, or transmitted in any
-form or by any means without the express written consent of
-Intel Corporation.
-
---*/
-
-/** @file
- This is a simple TFTP server application
-
-**/
-
-#include <TftpServer.h>
-
-TSDT_TFTP_SERVER mTftpServer; ///< TFTP server's control structure
-
-
-/**
- Add a connection context to the list of connection contexts.
-
- @param [in] pTftpServer The TFTP server control structure address.
-
- @retval Context structure address, NULL if allocation fails
-
-**/
-TSDT_CONNECTION_CONTEXT *
-ContextAdd (
- IN TSDT_TFTP_SERVER * pTftpServer
- )
-{
- size_t LengthInBytes;
- TSDT_CONNECTION_CONTEXT * pContext;
- EFI_STATUS Status;
-
- DBG_ENTER ( );
-
- //
- // Use for/break instead of goto
- //
- for ( ; ; ) {
- //
- // Allocate a new context
- //
- LengthInBytes = sizeof ( *pContext );
- Status = gBS->AllocatePool ( EfiRuntimeServicesData,
- LengthInBytes,
- (VOID **)&pContext );
- if ( EFI_ERROR ( Status )) {
- DEBUG (( DEBUG_ERROR | DEBUG_POOL,
- "ERROR - Failed to allocate the context, Status: %r\r\n",
- Status ));
- pContext = NULL;
- break;
- }
-
- //
- // Initialize the context
- //
- ZeroMem ( pContext, LengthInBytes );
- CopyMem ( &pContext->RemoteAddress,
- &pTftpServer->RemoteAddress,
- sizeof ( pContext->RemoteAddress ));
- pContext->BlockSize = TFTP_MAX_BLOCK_SIZE;
- pContext->pBuffer = &pContext->FileData[0];
- pContext->pEnd = &pContext->pBuffer[sizeof ( pContext->pBuffer )];
- pContext->MaxTransferSize = 0;
- pContext->MaxTransferSize -= 1;
-
- //
- // Display the new context
- //
- DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
- "0x%08x: Context for %d.%d.%d.%d:%d\r\n",
- pContext,
- (UINT8)pContext->RemoteAddress.sin_addr.s_addr,
- (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 8 ),
- (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 16 ),
- (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 24 ),
- htons ( pContext->RemoteAddress.sin_port )));
-
- //
- // Add the context to the context list
- //
- pContext->pNext = pTftpServer->pContextList;
- pTftpServer->pContextList = pContext;
-
- //
- // All done
- //
- break;
- }
-
- //
- // Return the connection context
- //
- DBG_EXIT_STATUS ( pContext );
- return pContext;
-}
-
-
-/**
- Locate a remote connection context.
-
- @param [in] pTftpServer The TFTP server control structure address.
-
- @param [in] pIpAddress The start of the remote IP address in network order
-
- @param [in] Port The remote port number
-
- @retval Context structure address, NULL if not found
-
-**/
-TSDT_CONNECTION_CONTEXT *
-ContextFind (
- IN TSDT_TFTP_SERVER * pTftpServer
- )
-{
- TSDT_CONNECTION_CONTEXT * pContext;
-
- DBG_ENTER ( );
-
- //
- // Walk the list of connection contexts
- //
- pContext = pTftpServer->pContextList;
- while ( NULL != pContext ) {
- //
- // Attempt to locate the remote network connection
- //
- if (( pTftpServer->RemoteAddress.sin_addr.s_addr == pContext->RemoteAddress.sin_addr.s_addr )
- && ( pTftpServer->RemoteAddress.sin_port == pContext->RemoteAddress.sin_port )) {
- //
- // The connection was found
- //
- DEBUG (( DEBUG_TFTP_REQUEST,
- "0x%08x: pContext found\r\n",
- pContext ));
- break;
- }
-
- //
- // Set the next context
- //
- pContext = pContext->pNext;
- }
-
- //
- // Return the connection context structure address
- //
- DBG_EXIT_HEX ( pContext );
- return pContext;
-}
-
-
-/**
- Remove a context from the list.
-
- @param [in] pTftpServer The TFTP server control structure address.
-
- @param [in] pContext The context structure address.
-
-**/
-VOID
-ContextRemove (
- IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext
- )
-{
- TSDT_CONNECTION_CONTEXT * pNextContext;
- TSDT_CONNECTION_CONTEXT * pPreviousContext;
-
- DBG_ENTER ( );
-
- //
- // Attempt to locate the context in the list
- //
- pPreviousContext = NULL;
- pNextContext = pTftpServer->pContextList;
- while ( NULL != pNextContext ) {
- //
- // Determine if the context was found
- //
- if ( pNextContext == pContext ) {
- //
- // Remove the context from the list
- //
- if ( NULL == pPreviousContext ) {
- pTftpServer->pContextList = pContext->pNext;
- }
- else {
- pPreviousContext->pNext = pContext->pNext;
- }
- break;
- }
-
- //
- // Set the next context
- //
- pPreviousContext = pNextContext;
- pNextContext = pNextContext->pNext;
- }
-
- //
- // Determine if the context was found
- //
- if ( NULL != pContext ) {
- //
- // Return the resources
- //
- gBS->FreePool ( pContext );
- }
-
- DBG_EXIT ( );
-}
-
-
-/**
- Process the work for the sockets.
-
- @param [in] pTftpServer The TFTP server control structure address.
-
-**/
-VOID
-PortWork (
- IN TSDT_TFTP_SERVER * pTftpServer
- )
-{
- TSDT_CONNECTION_CONTEXT * pContext;
- socklen_t RemoteAddressLength;
-
- DBG_ENTER ( );
-
- //
- // Handle input events
- //
- if ( 0 != ( pTftpServer->TftpPort.revents & POLLRDNORM )) {
- //
- // Receive the message from the remote system
- //
- RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );
- pTftpServer->RxBytes = recvfrom ( pTftpServer->TftpPort.fd,
- &pTftpServer->RxBuffer[0],
- sizeof ( pTftpServer->RxBuffer ),
- 0,
- (struct sockaddr *) &pTftpServer->RemoteAddress,
- &RemoteAddressLength );
- if ( -1 != pTftpServer->RxBytes ) {
- pTftpServer->RemoteAddress.sin_len = (UINT8) RemoteAddressLength;
- DEBUG (( DEBUG_TFTP_PORT,
- "Received %d bytes from %d.%d.%d.%d:%d\r\n",
- pTftpServer->RxBytes,
- pTftpServer->RemoteAddress.sin_addr.s_addr & 0xff,
- ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ) & 0xff,
- ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ) & 0xff,
- ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ) & 0xff,
- htons ( pTftpServer->RemoteAddress.sin_port )));
-
- //
- // Lookup connection context using the remote system address and port
- // to determine if an existing connection to this remote
- // system exists
- //
- pContext = ContextFind ( pTftpServer );
-
- //
- // Process the received message
- //
- TftpProcessRequest ( pTftpServer, pContext );
- }
- else {
- //
- // Receive error on the TFTP server port
- // Close the server socket
- //
- DEBUG (( DEBUG_ERROR,
- "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",
- errno ));
- pTftpServer->TftpPort.revents |= POLLHUP;
- }
- }
-
- //
- // Handle the close event
- //
- if ( 0 != ( pTftpServer->TftpPort.revents & POLLHUP )) {
- //
- // Close the port
- //
- close ( pTftpServer->TftpPort.fd );
- pTftpServer->TftpPort.fd = -1;
- }
-
- DBG_EXIT ( );
-}
-
-
-/**
- Scan the list of sockets and process any pending work
-
- @param [in] pTftpServer The TFTP server control structure address.
-
-**/
-VOID
-SocketPoll (
- IN TSDT_TFTP_SERVER * pTftpServer
- )
-{
- int FDCount;
-
- DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));
-
- //
- // Determine if any ports are active
- //
- FDCount = poll ( &pTftpServer->TftpPort,
- 1,
- CLIENT_POLL_DELAY );
- if ( -1 == FDCount ) {
- DEBUG (( DEBUG_ERROR | DEBUG_SOCKET_POLL,
- "ERROR - errno: %d\r\n",
- errno ));
- }
-
- if ( 0 < FDCount ) {
- //
- // Process this port
- //
- PortWork ( pTftpServer );
- pTftpServer->TftpPort.revents = 0;
- }
-
- DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));
-}
-
-
-/**
- Convert a character to lower case
-
- @param [in] Character The character to convert
-
- @returns The lower case equivalent of the character
-
-**/
-int
-tolower (
- int Character
- )
-{
- //
- // Determine if the character is upper case
- //
- if (( 'A' <= Character ) && ( 'Z' >= Character )) {
- //
- // Convert the character to lower caes
- //
- Character += 'a' - 'A';
- }
-
- //
- // Return the converted character
- //
- return Character;
-}
-
-
-/**
- Case independent string comparison
-
- @param [in] pString1 Zero terminated string address
- @param [in] pString2 Zero terminated string address
-
- @returns Returns the first character difference between string 1
- and string 2.
-
-**/
-int
-stricmp (
- char * pString1,
- char * pString2
- )
-{
- int Char1;
- int Char2;
- int Difference;
-
- //
- // Walk the length of the strings
- //
- do {
- //
- // Get the next characters
- //
- Char1 = (UINT8)*pString1++;
- Char2 = (UINT8)*pString2++;
-
- //
- // Convert them to lower case
- //
- Char1 = tolower ( Char1 );
- Char2 = tolower ( Char2 );
-
- //
- // Done when the characters differ
- //
- Difference = Char1 - Char2;
- if ( 0 != Difference ) {
- break;
- }
-
- //
- // Done at the end of the string
- //
- } while ( 0 != Char1 );
-
- //
- // Return the difference
- //
- return Difference;
-}
-
-
-/**
- Get the next TFTP option
-
- @param [in] pOption Address of a zero terminated option string
- @param [in] pEnd End of buffer address
- @param [in] ppNextOption Address to receive the address of the next
- zero terminated option string
-
- @retval EFI_SUCCESS Message processed successfully
-
-**/
-EFI_STATUS
-TftpOptionGet (
- IN UINT8 * pOption,
- IN UINT8 * pEnd,
- IN UINT8 ** ppNextOption
- )
-{
- UINT8 * pNextOption;
- EFI_STATUS Status;
-
- //
- // Locate the end of the option
- //
- pNextOption = pOption;
- while (( pEnd > pNextOption ) && ( 0 != *pNextOption )) {
- pNextOption += 1;
- }
- if ( pEnd <= pNextOption ) {
- //
- // Error - end of buffer reached
- //
- DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
- "ERROR - Option without zero termination received!\r\n" ));
- Status = EFI_INVALID_PARAMETER;
- }
- else {
- //
- // Zero terminated option found
- //
- pNextOption += 1;
-
- //
- // Display the zero terminated ASCII option string
- //
- DEBUG (( DEBUG_TFTP_REQUEST,
- "Option: %a\r\n",
- pOption ));
- Status = EFI_SUCCESS;
- }
-
- //
- // Return the next option address
- //
- *ppNextOption = pNextOption;
-
- //
- // Return the operation status
- //
- return Status;
-}
-
-
-/**
- Place an option value into the option acknowledgement
-
- @param [in] pOack Option acknowledgement address
- @param [in] Value Value to translate into ASCII decimal
-
- @returns Option acknowledgement address
-
-**/
-UINT8 *
-TftpOptionSet (
- IN UINT8 * pOack,
- IN UINT64 Value
- )
-{
- UINT64 NextValue;
-
- //
- // Determine the next value
- //
- NextValue = Value / 10;
-
- //
- // Supress leading zeros
- //
- if ( 0 != NextValue ) {
- pOack = TftpOptionSet ( pOack, NextValue );
- }
-
- //
- // Output this digit
- //
- *pOack++ = (UINT8)( Value - ( NextValue * 10 ) + '0' );
-
- //
- // Return the next option acknowledgement location
- //
- return pOack;
-}
-
-
-/**
- Process the TFTP request
-
- @param [in] pContext The context structure address.
- @param [in] pOption Address of the first zero terminated option string
- @param [in] pEnd End of buffer address
-
-**/
-VOID
-TftpOptions (
- IN TSDT_CONNECTION_CONTEXT * pContext,
- IN UINT8 * pOption,
- IN UINT8 * pEnd
- )
-{
- UINT8 * pNextOption;
- UINT8 * pOack;
- UINT8 * pTemp;
- UINT8 * pValue;
- EFI_STATUS Status;
- INT32 Value;
-
- //
- // Start the OACK packet
- // Let the OACK handle the parsing errors
- // See http://tools.ietf.org/html/rfc2347
- //
- pOack = &pContext->TxBuffer[0];
- *pOack++ = 0;
- *pOack++ = TFTP_OP_OACK;
- pContext->TxBytes = 2;
-
- //
- // Walk the list of options
- //
- do {
- //
- // Get the next option, skip junk at end of message
- //
- Status = TftpOptionGet ( pOption, pEnd, &pNextOption );
- if ( !EFI_ERROR ( Status )) {
- //
- // Process the option
- //
-
- //
- // blksize - See http://tools.ietf.org/html/rfc2348
- //
- pValue = pNextOption;
- if ( 0 == stricmp ((char *)pOption, "blksize" )) {
- //
- // Get the value
- //
- Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
- if ( !EFI_ERROR ( Status )) {
- //
- // Validate the block size, skip non-numeric block sizes
- //
- Status = TftpOptionValue ( pValue, &Value );
- if ( !EFI_ERROR ( Status )) {
- //
- // Propose a smaller block size if necessary
- //
- if ( Value > TFTP_MAX_BLOCK_SIZE ) {
- Value = TFTP_MAX_BLOCK_SIZE;
- }
-
- //
- // Set the new block size
- //
- pContext->BlockSize = Value;
- DEBUG (( DEBUG_TFTP_REQUEST,
- "Using block size of %d bytes\r\n",
- pContext->BlockSize ));
-
- //
- // Update the OACK
- //
- pTemp = pOack;
- *pOack++ = 'b';
- *pOack++ = 'l';
- *pOack++ = 'k';
- *pOack++ = 's';
- *pOack++ = 'i';
- *pOack++ = 'z';
- *pOack++ = 'e';
- *pOack++ = 0;
- pOack = TftpOptionSet ( pOack, pContext->BlockSize );
- *pOack++ = 0;
- pContext->TxBytes += pOack - pTemp;
- }
- }
- }
-
- //
- // timeout - See http://tools.ietf.org/html/rfc2349
- //
- else if ( 0 == stricmp ((char *)pOption, "timeout" )) {
- //
- // Get the value
- //
- Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
- if ( !EFI_ERROR ( Status )) {
- Status = TftpOptionValue ( pValue, &Value );
- if ( !EFI_ERROR ( Status )) {
- //
- // Set the timeout value
- //
- pContext->Timeout = Value;
- DEBUG (( DEBUG_TFTP_REQUEST,
- "Using timeout of %d seconds\r\n",
- pContext->Timeout ));
-
- //
- // Update the OACK
- //
- pTemp = pOack;
- *pOack++ = 't';
- *pOack++ = 'i';
- *pOack++ = 'm';
- *pOack++ = 'e';
- *pOack++ = 'o';
- *pOack++ = 'u';
- *pOack++ = 't';
- *pOack++ = 0;
- pOack = TftpOptionSet ( pOack, pContext->Timeout );
- *pOack++ = 0;
- pContext->TxBytes += pOack - pTemp;
- }
- }
- }
-
- //
- // tsize - See http://tools.ietf.org/html/rfc2349
- //
- else if ( 0 == stricmp ((char *)pOption, "tsize" )) {
- //
- // Get the value
- //
- Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
- if ( !EFI_ERROR ( Status )) {
- Status = TftpOptionValue ( pValue, &Value );
- if ( !EFI_ERROR ( Status )) {
- //
- // Return the file size
- //
- DEBUG (( DEBUG_TFTP_REQUEST,
- "Returning file size of %Ld bytes\r\n",
- pContext->LengthInBytes ));
-
- //
- // Update the OACK
- //
- pTemp = pOack;
- *pOack++ = 't';
- *pOack++ = 's';
- *pOack++ = 'i';
- *pOack++ = 'z';
- *pOack++ = 'e';
- *pOack++ = 0;
- pOack = TftpOptionSet ( pOack, pContext->LengthInBytes );
- *pOack++ = 0;
- pContext->TxBytes += pOack - pTemp;
- }
- }
- }
- else {
- //
- // Unknown option - Ignore it
- //
- DEBUG (( DEBUG_WARN | DEBUG_TFTP_REQUEST,
- "WARNING - Skipping unknown option: %a\r\n",
- pOption ));
- }
- }
-
- //
- // Set the next option
- //
- pOption = pNextOption;
- } while ( pEnd > pOption );
-}
-
-
-/**
- Process the TFTP request
-
- @param [in] pOption Address of the first zero terminated option string
- @param [in] pValue Address to receive the value
-
- @retval EFI_SUCCESS Option translated into a value
-
-**/
-EFI_STATUS
-TftpOptionValue (
- IN UINT8 * pOption,
- IN INT32 * pValue
- )
-{
- UINT8 Digit;
- EFI_STATUS Status;
- INT32 Value;
-
- //
- // Assume success
- //
- Status = EFI_SUCCESS;
-
- //
- // Walk the characters in the option
- //
- Value = 0;
- while ( 0 != *pOption ) {
- //
- // Convert the next digit to binary
- //
- Digit = *pOption++;
- if (( '0' <= Digit ) && ( '9' >= Digit )) {
- Value *= 10;
- Value += Digit - '0';
- }
- else {
- DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
- "ERROR - Invalid character '0x%02x' in the value\r\n",
- Digit ));
- Status = EFI_INVALID_PARAMETER;
- break;
- }
- }
-
- //
- // Return the value
- //
- *pValue = Value;
-
- //
- // Return the conversion status
- //
- return Status;
-}
-
-
-/**
- Process the TFTP request
-
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext Connection context structure address
-
-**/
-VOID
-TftpProcessRequest (
- IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext
- )
-{
- BOOLEAN bCloseContext;
- BOOLEAN bIgnorePacket;
- UINT16 BlockNumber;
- UINT16 Opcode;
- UINT8 * pBuffer;
- UINT8 * pEnd;
- UINT8 * pFileName;
- UINT8 * pMode;
- UINT8 * pOption;
- EFI_STATUS Status;
-
- DBG_ENTER ( );
-
- //
- // Get the opcode
- //
- pBuffer = &pTftpServer->RxBuffer[0];
- Opcode = HTONS ( *(UINT16 *)&pBuffer[0]);
-Print ( L"TFTP Opcode: 0x%08x\r\n", Opcode );
-
- //
- // Validate the parameters
- //
- bCloseContext = FALSE;
- bIgnorePacket = FALSE;
- switch ( Opcode ) {
- default:
- DEBUG (( DEBUG_TFTP_REQUEST,
- "ERROR - Unknown TFTP opcode: %d\r\n",
- Opcode ));
- bIgnorePacket = TRUE;
- break;
-
- case TFTP_OP_READ_REQUEST:
- break;
-
- case TFTP_OP_DATA:
- if ( NULL == pContext ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
- (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
- htons ( pTftpServer->RemoteAddress.sin_port )));
- bIgnorePacket = TRUE;
- break;
- }
- if ( pContext->bExpectAck ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",
- pContext ));
- bIgnorePacket = TRUE;
- break;
- }
- if ( pTftpServer->RxBytes > (ssize_t)( pContext->BlockSize + 2 + 2 ))
- {
- DEBUG (( DEBUG_ERROR,
- "ERROR - Receive data length of %d > %d bytes (maximum block size) for pContext 0x%08x\r\n",
- pTftpServer->RxBytes - 2 - 2,
- pContext->BlockSize,
- pContext ));
- bIgnorePacket = TRUE;
- break;
- }
- break;
-
- case TFTP_OP_ACK:
- if ( NULL == pContext ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
- (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
- htons ( pTftpServer->RemoteAddress.sin_port )));
- bIgnorePacket = TRUE;
- }
- if ( !pContext->bExpectAck ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",
- pContext ));
- bIgnorePacket = TRUE;
- break;
- }
- break;
-
- case TFTP_OP_ERROR:
- if ( NULL == pContext ) {
- DEBUG (( DEBUG_ERROR,
- "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
- (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
- (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
- htons ( pTftpServer->RemoteAddress.sin_port )));
- bIgnorePacket = TRUE;
- }
- break;
- }
- if ( !bIgnorePacket ) {
- //
- // Process the request
- //
- switch ( Opcode ) {
- default:
- DEBUG (( DEBUG_TFTP_REQUEST,
- "ERROR - Unable to process TFTP opcode: %d\r\n",
- Opcode ));
- break;
-
- case TFTP_OP_READ_REQUEST:
-
- //
- // Close the context if necessary
- //
- if ( NULL != pContext ) {
- ContextRemove ( pTftpServer, pContext );
- }
-
- //
- // Create the connection context
- //
- pContext = ContextAdd ( pTftpServer );
- if ( NULL == pContext ) {
- break;
- }
-
- //
- // Locate the mode
- //
- pFileName = &pBuffer[2];
- pEnd = &pBuffer[pTftpServer->RxBytes];
- pMode = pFileName;
- while (( pEnd > pMode ) && ( 0 != *pMode )) {
- pMode += 1;
- }
- if ( pEnd <= pMode ) {
- //
- // Mode not found
- //
- DEBUG (( DEBUG_ERROR | DEBUG_RX,
- "ERROR - File mode not found\r\n" ));
- //
- // Tell the client of the error
- //
- TftpSendError ( pTftpServer,
- pContext,
- 0,
- (UINT8 *)"File open mode not found" );
- break;
- }
- pMode += 1;
- DEBUG (( DEBUG_TFTP_REQUEST,
- "TFTP - FileName: %a\n",
- pFileName ));
-
- //
- // Locate the options
- //
- pOption = pMode;
- while (( pEnd > pOption ) && ( 0 != *pOption )) {
- pOption += 1;
- }
- if ( pEnd <= pOption ) {
- //
- // End of mode not found
- //
- DEBUG (( DEBUG_ERROR | DEBUG_RX,
- "ERROR - File mode not valid\r\n" ));
- //
- // Tell the client of the error
- //
- TftpSendError ( pTftpServer,
- pContext,
- 0,
- (UINT8 *)"File open mode not valid" );
- break;
- }
- pOption += 1;
- DEBUG (( DEBUG_TFTP_REQUEST,
- "TFTP - Mode: %a\r\n",
- pMode ));
-
- //
- // Verify the mode is supported
- //
- if ( 0 != stricmp ((char *)pMode, "octet" )) {
- //
- // File access mode not supported
- //
- DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
- "ERROR - File mode %a not supported\r\n",
- pMode ));
-
- //
- // Tell the client of the error
- //
- TftpSendError ( pTftpServer,
- pContext,
- 0,
- (UINT8 *)"File open mode not supported" );
- break;
- }
-
- //
- // Open the file, close the context on error
- //
-// TODO: Remove the following line
-pContext->File = (EFI_HANDLE)1;
-
- //
- // Determine the file length
- //
-//fstat
-
- //
- // Process the options
- //
- TftpOptions ( pContext, pOption, pEnd );
-
- //
- // Read in the first portion of the file
- //
-
- //
- // Send the first block
- //
- pContext->bExpectAck = TRUE;
- if ( 2 < pContext->TxBytes ) {
- //
- // Send the OACK
- //
- Status = TftpTxPacket ( pTftpServer, pContext );
- }
- else {
- //
- // Send the first block of data
- //
- Status = TftpSendNextBlock ( pTftpServer, pContext );
- }
- break;
-
- case TFTP_OP_ACK:
- //
- // Get the block number that is being ACKed
- //
- BlockNumber = pTftpServer->RxBuffer[2];
- BlockNumber <<= 8;
- BlockNumber |= pTftpServer->RxBuffer[3];
-
- //
- // Determine if this is the correct ACK
- //
- DEBUG (( DEBUG_TFTP_ACK,
- "ACK for block 0x%04x received\r\n",
- BlockNumber ));
- if (( !pContext->bExpectAck )
- || ( BlockNumber != pContext->AckNext ))
- {
- DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,
- "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",
- pContext->AckNext,
- BlockNumber ));
- }
- else {
- //
- // Process the expected ACK
- //
- if ( pContext->bEofSent ) {
- bCloseContext = TRUE;
- }
- else {
- //
- // Set the next expected ACK
- //
- pContext->AckNext += 1;
-
- //
- // Send the next packet of data
- //
- Status = TftpSendNextBlock ( pTftpServer, pContext );
- }
- }
- break;
- }
- }
-
- //
- // Determine if the context should be closed
- //
- if ( bCloseContext ) {
- ContextRemove ( pTftpServer, pContext );
- }
-
- DBG_EXIT ( );
-}
-
-
-/**
- Build and send an error packet
-
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext The context structure address.
- @param [in] Error Error number for the packet
- @param [in] pError Zero terminated error string address
-
- @retval EFI_SUCCESS Message processed successfully
-
-**/
-EFI_STATUS
-TftpSendError (
- IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext,
- IN UINT16 Error,
- IN UINT8 * pError
- )
-{
- UINT8 Character;
- UINT8 * pBuffer;
- EFI_STATUS Status;
-
- DBG_ENTER ( );
-
- //
- // Build the error packet
- //
- pBuffer = &pContext->TxBuffer[0];
- pBuffer[0] = 0;
- pBuffer[1] = TFTP_OP_ERROR;
- pBuffer[2] = (UINT8)( Error >> 8 );
- pBuffer[3] = (UINT8)Error;
-
- //
- // Copy the zero terminated string into the buffer
- //
- pBuffer += 4;
- do {
- Character = *pError++;
- *pBuffer++ = Character;
- } while ( 0 != Character );
-
- //
- // Send the error message
- //
- pContext->TxBytes = pBuffer - &pContext->TxBuffer[0];
- Status = TftpTxPacket ( pTftpServer, pContext );
-
- //
- // Return the operation status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
-}
-
-
-/**
- Send the next block of file system data
-
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext The context structure address.
-
- @retval EFI_SUCCESS Message processed successfully
-
-**/
-EFI_STATUS
-TftpSendNextBlock (
- IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext
- )
-{
- ssize_t LengthInBytes;
- UINT8 * pBuffer;
- EFI_STATUS Status;
-
- //
- // Determine how much data needs to be sent
- //
- LengthInBytes = pContext->BlockSize;
- if (( pContext->LengthInBytes < TFTP_MAX_BLOCK_SIZE )
- || ( LengthInBytes > (ssize_t)pContext->LengthInBytes )) {
- LengthInBytes = (ssize_t)pContext->LengthInBytes;
- pContext->bEofSent = TRUE;
- }
-
- //
- // Set the TFTP opcode and block number
- //
- pBuffer = &pContext->TxBuffer[0];
- *pBuffer++ = 0;
- *pBuffer++ = TFTP_OP_DATA;
- *pBuffer++ = (UINT8)( pContext->AckNext >> 8 );
- *pBuffer++ = (UINT8)pContext->AckNext;
-
- //
- // Copy the file data into the transmit buffer
- //
- pContext->TxBytes = 2 + 2 + LengthInBytes;
- if ( 0 < LengthInBytes ) {
- CopyMem ( &pBuffer,
- pContext->pBuffer,
- LengthInBytes );
- }
-
- //
- // Send the next block
- //
- Status = TftpTxPacket ( pTftpServer, pContext );
-
- //
- // Return the operation status
- //
- return Status;
-}
-
-
-/**
- Create the port for the TFTP server
-
- This routine polls the network layer to create the TFTP port for the
- TFTP server. More than one attempt may be necessary since it may take
- some time to get the IP address and initialize the upper layers of
- the network stack.
-
- @param [in] pTftpServer The TFTP server control structure address.
-
-**/
-VOID
-TftpServerTimer (
- IN TSDT_TFTP_SERVER * pTftpServer
- )
-{
- UINT16 TftpPort;
- int SocketStatus;
- EFI_STATUS Status;
-
- DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerTimer\r\n" ));
-
- //
- // Open the TFTP port on the server
- //
- do {
- do {
- //
- // Wait for a while
- //
- Status = gBS->CheckEvent ( pTftpServer->TimerEvent );
- } while ( EFI_SUCCESS != Status );
-
- //
- // Attempt to create the socket for the TFTP server
- //
- pTftpServer->TftpPort.events = POLLRDNORM | POLLHUP;
- pTftpServer->TftpPort.revents = 0;
- pTftpServer->TftpPort.fd = socket ( AF_INET,
- SOCK_DGRAM,
- IPPROTO_UDP );
- if ( -1 != pTftpServer->TftpPort.fd )
- {
- //
- // Set the socket address
- //
- ZeroMem ( &pTftpServer->TftpServerAddress,
- sizeof ( pTftpServer->TftpServerAddress ));
- TftpPort = 69;
- DEBUG (( DEBUG_TFTP_PORT,
- "TFTP Port: %d\r\n",
- TftpPort ));
- pTftpServer->TftpServerAddress.sin_len = sizeof ( pTftpServer->TftpServerAddress );
- pTftpServer->TftpServerAddress.sin_family = AF_INET;
- pTftpServer->TftpServerAddress.sin_addr.s_addr = INADDR_ANY;
- pTftpServer->TftpServerAddress.sin_port = htons ( TftpPort );
-
- //
- // Bind the socket to the TFTP port
- //
- SocketStatus = bind ( pTftpServer->TftpPort.fd,
- (struct sockaddr *) &pTftpServer->TftpServerAddress,
- pTftpServer->TftpServerAddress.sin_len );
- if ( -1 != SocketStatus ) {
- DEBUG (( DEBUG_TFTP_PORT,
- "0x%08x: Socket bound to port %d\r\n",
- pTftpServer->TftpPort.fd,
- TftpPort ));
- }
-
- //
- // Release the socket if necessary
- //
- if ( -1 == SocketStatus ) {
- close ( pTftpServer->TftpPort.fd );
- pTftpServer->TftpPort.fd = -1;
- }
- }
-
- //
- // Wait until the socket is open
- //
- }while ( -1 == pTftpServer->TftpPort.fd );
-
- DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerTimer\r\n" ));
-}
-
-
-/**
- Start the TFTP server port creation timer
-
- @param [in] pTftpServer The TFTP server control structure address.
-
- @retval EFI_SUCCESS The timer was successfully started.
- @retval EFI_ALREADY_STARTED The timer is already running.
- @retval Other The timer failed to start.
-
-**/
-EFI_STATUS
-TftpServerTimerStart (
- IN TSDT_TFTP_SERVER * pTftpServer
- )
-{
- EFI_STATUS Status;
- UINT64 TriggerTime;
-
- DBG_ENTER ( );
-
- //
- // Assume the timer is already running
- //
- Status = EFI_ALREADY_STARTED;
- if ( !pTftpServer->bTimerRunning ) {
- //
- // Compute the poll interval
- //
- TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );
- Status = gBS->SetTimer ( pTftpServer->TimerEvent,
- TimerPeriodic,
- TriggerTime );
- if ( !EFI_ERROR ( Status )) {
- DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));
-
- //
- // Mark the timer running
- //
- pTftpServer->bTimerRunning = TRUE;
- }
- else {
- DEBUG (( DEBUG_ERROR | DEBUG_TFTP_PORT,
- "ERROR - Failed to start TFTP port timer, Status: %r\r\n",
- Status ));
- }
- }
-
- //
- // Return the operation status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
-}
-
-
-/**
- Stop the TFTP server port creation timer
-
- @param [in] pTftpServer The TFTP server control structure address.
-
- @retval EFI_SUCCESS The TFTP port timer is stopped
- @retval Other Failed to stop the TFTP port timer
-
-**/
-EFI_STATUS
-TftpServerTimerStop (
- IN TSDT_TFTP_SERVER * pTftpServer
- )
-{
- EFI_STATUS Status;
-
- DBG_ENTER ( );
-
- //
- // Assume the timer is stopped
- //
- Status = EFI_SUCCESS;
- if ( pTftpServer->bTimerRunning ) {
- //
- // Stop the port creation polling
- //
- Status = gBS->SetTimer ( pTftpServer->TimerEvent,
- TimerCancel,
- 0 );
- if ( !EFI_ERROR ( Status )) {
- DEBUG (( DEBUG_TFTP_PORT, "TFT[ port timer stopped\r\n" ));
-
- //
- // Mark the timer stopped
- //
- pTftpServer->bTimerRunning = FALSE;
- }
- else {
- DEBUG (( DEBUG_ERROR | DEBUG_TFTP_PORT,
- "ERROR - Failed to stop TFT[ port timer, Status: %r\r\n",
- Status ));
- }
- }
-
- //
- // Return the operation status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
-}
-
-/**
- Send the next TFTP packet
-
- @param [in] pTftpServer The TFTP server control structure address.
- @param [in] pContext The context structure address.
-
- @retval EFI_SUCCESS Message processed successfully
-
-**/
-EFI_STATUS
-TftpTxPacket (
- IN TSDT_TFTP_SERVER * pTftpServer,
- IN TSDT_CONNECTION_CONTEXT * pContext
- )
-{
- ssize_t LengthInBytes;
- EFI_STATUS Status;
-
- DBG_ENTER ( );
-
- //
- // Assume success
- //
- Status = EFI_SUCCESS;
-
- //
- // Send the TFTP packet
- //
- DEBUG (( DEBUG_TX,
- "0x%08x: pContext sending 0x%08x bytes\r\n",
- pContext,
- pContext->TxBytes ));
- LengthInBytes = sendto ( pTftpServer->TftpPort.fd,
- &pContext->TxBuffer[0],
- pContext->TxBytes,
- 0,
- (struct sockaddr *)&pContext->RemoteAddress,
- pContext->RemoteAddress.sin_len );
- if ( -1 == LengthInBytes ) {
- DEBUG (( DEBUG_ERROR | DEBUG_TX,
- "ERROR - Transmit failure, errno: 0x%08x\r\n",
- errno ));
- Status = EFI_DEVICE_ERROR;
- }
-
- //
- // Return the operation status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
-}
-
-
-/**
- Entry point for the TFTP server application.
-
- @param [in] Argc The number of arguments
- @param [in] Argv The argument value array
-
- @retval 0 The application exited normally.
- @retval Other An error occurred.
-**/
-int
-main (
- IN int Argc,
- IN char **Argv
- )
-{
- TSDT_TFTP_SERVER * pTftpServer;
- EFI_STATUS Status;
-
- //
- // Create a timer event to start TFTP port
- //
- pTftpServer = &mTftpServer;
- Status = gBS->CreateEvent ( EVT_TIMER,
- TPL_TFTP_SERVER,
- NULL,
- NULL,
- &pTftpServer->TimerEvent );
- if ( !EFI_ERROR ( Status )) {
- Status = TftpServerTimerStart ( pTftpServer );
- if ( !EFI_ERROR ( Status )) {
- //
- // Run the TFTP server forever
- //
- for ( ; ; ) {
- //
- // Poll the network layer to create the TFTP port
- // for the tftp server. More than one attempt may
- // be necessary since it may take some time to get
- // the IP address and initialize the upper layers
- // of the network stack.
- //
- TftpServerTimer ( pTftpServer );
-
- //
- // Poll the socket for activity
- //
- do {
- SocketPoll ( pTftpServer );
- } while ( -1 != pTftpServer->TftpPort.fd );
-
-//
-// TODO: Remove the following test code
-// Exit when the network connection is broken
-//
-break;
- }
-
- //
- // Done with the timer event
- //
- TftpServerTimerStop ( pTftpServer );
- Status = gBS->CloseEvent ( pTftpServer->TimerEvent );
- }
- }
-
- //
- // Return the final status
- //
- DBG_EXIT_STATUS ( Status );
- return Status;
-}
+/*++
+ This file contains an 'Intel UEFI Application' and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+--*/
+/*++
+
+Copyright (c) 2011 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+--*/
+
+/** @file
+ This is a simple TFTP server application
+
+**/
+
+#include <TftpServer.h>
+
+TSDT_TFTP_SERVER mTftpServer; ///< TFTP server's control structure
+
+
+/**
+ Add a connection context to the list of connection contexts.
+
+ @param [in] pTftpServer The TFTP server control structure address.
+
+ @retval Context structure address, NULL if allocation fails
+
+**/
+TSDT_CONNECTION_CONTEXT *
+ContextAdd (
+ IN TSDT_TFTP_SERVER * pTftpServer
+ )
+{
+ size_t LengthInBytes;
+ TSDT_CONNECTION_CONTEXT * pContext;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Use for/break instead of goto
+ //
+ for ( ; ; ) {
+ //
+ // Allocate a new context
+ //
+ LengthInBytes = sizeof ( *pContext );
+ Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+ LengthInBytes,
+ (VOID **)&pContext );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL,
+ "ERROR - Failed to allocate the context, Status: %r\r\n",
+ Status ));
+ pContext = NULL;
+ break;
+ }
+
+ //
+ // Initialize the context
+ //
+ ZeroMem ( pContext, LengthInBytes );
+ CopyMem ( &pContext->RemoteAddress,
+ &pTftpServer->RemoteAddress,
+ sizeof ( pContext->RemoteAddress ));
+ pContext->BlockSize = TFTP_MAX_BLOCK_SIZE;
+ pContext->pBuffer = &pContext->FileData[0];
+ pContext->pEnd = &pContext->pBuffer[sizeof ( pContext->pBuffer )];
+ pContext->MaxTransferSize = 0;
+ pContext->MaxTransferSize -= 1;
+
+ //
+ // Display the new context
+ //
+ DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
+ "0x%08x: Context for %d.%d.%d.%d:%d\r\n",
+ pContext,
+ (UINT8)pContext->RemoteAddress.sin_addr.s_addr,
+ (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 8 ),
+ (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 16 ),
+ (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 24 ),
+ htons ( pContext->RemoteAddress.sin_port )));
+
+ //
+ // Add the context to the context list
+ //
+ pContext->pNext = pTftpServer->pContextList;
+ pTftpServer->pContextList = pContext;
+
+ //
+ // All done
+ //
+ break;
+ }
+
+ //
+ // Return the connection context
+ //
+ DBG_EXIT_STATUS ( pContext );
+ return pContext;
+}
+
+
+/**
+ Locate a remote connection context.
+
+ @param [in] pTftpServer The TFTP server control structure address.
+
+ @param [in] pIpAddress The start of the remote IP address in network order
+
+ @param [in] Port The remote port number
+
+ @retval Context structure address, NULL if not found
+
+**/
+TSDT_CONNECTION_CONTEXT *
+ContextFind (
+ IN TSDT_TFTP_SERVER * pTftpServer
+ )
+{
+ TSDT_CONNECTION_CONTEXT * pContext;
+
+ DBG_ENTER ( );
+
+ //
+ // Walk the list of connection contexts
+ //
+ pContext = pTftpServer->pContextList;
+ while ( NULL != pContext ) {
+ //
+ // Attempt to locate the remote network connection
+ //
+ if (( pTftpServer->RemoteAddress.sin_addr.s_addr == pContext->RemoteAddress.sin_addr.s_addr )
+ && ( pTftpServer->RemoteAddress.sin_port == pContext->RemoteAddress.sin_port )) {
+ //
+ // The connection was found
+ //
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "0x%08x: pContext found\r\n",
+ pContext ));
+ break;
+ }
+
+ //
+ // Set the next context
+ //
+ pContext = pContext->pNext;
+ }
+
+ //
+ // Return the connection context structure address
+ //
+ DBG_EXIT_HEX ( pContext );
+ return pContext;
+}
+
+
+/**
+ Remove a context from the list.
+
+ @param [in] pTftpServer The TFTP server control structure address.
+
+ @param [in] pContext The context structure address.
+
+**/
+VOID
+ContextRemove (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext
+ )
+{
+ TSDT_CONNECTION_CONTEXT * pNextContext;
+ TSDT_CONNECTION_CONTEXT * pPreviousContext;
+
+ DBG_ENTER ( );
+
+ //
+ // Attempt to locate the context in the list
+ //
+ pPreviousContext = NULL;
+ pNextContext = pTftpServer->pContextList;
+ while ( NULL != pNextContext ) {
+ //
+ // Determine if the context was found
+ //
+ if ( pNextContext == pContext ) {
+ //
+ // Remove the context from the list
+ //
+ if ( NULL == pPreviousContext ) {
+ pTftpServer->pContextList = pContext->pNext;
+ }
+ else {
+ pPreviousContext->pNext = pContext->pNext;
+ }
+ break;
+ }
+
+ //
+ // Set the next context
+ //
+ pPreviousContext = pNextContext;
+ pNextContext = pNextContext->pNext;
+ }
+
+ //
+ // Determine if the context was found
+ //
+ if ( NULL != pContext ) {
+ //
+ // Return the resources
+ //
+ gBS->FreePool ( pContext );
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Process the work for the sockets.
+
+ @param [in] pTftpServer The TFTP server control structure address.
+
+**/
+VOID
+PortWork (
+ IN TSDT_TFTP_SERVER * pTftpServer
+ )
+{
+ TSDT_CONNECTION_CONTEXT * pContext;
+ socklen_t RemoteAddressLength;
+
+ DBG_ENTER ( );
+
+ //
+ // Handle input events
+ //
+ if ( 0 != ( pTftpServer->TftpPort.revents & POLLRDNORM )) {
+ //
+ // Receive the message from the remote system
+ //
+ RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );
+ pTftpServer->RxBytes = recvfrom ( pTftpServer->TftpPort.fd,
+ &pTftpServer->RxBuffer[0],
+ sizeof ( pTftpServer->RxBuffer ),
+ 0,
+ (struct sockaddr *) &pTftpServer->RemoteAddress,
+ &RemoteAddressLength );
+ if ( -1 != pTftpServer->RxBytes ) {
+ pTftpServer->RemoteAddress.sin_len = (UINT8) RemoteAddressLength;
+ DEBUG (( DEBUG_TFTP_PORT,
+ "Received %d bytes from %d.%d.%d.%d:%d\r\n",
+ pTftpServer->RxBytes,
+ pTftpServer->RemoteAddress.sin_addr.s_addr & 0xff,
+ ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ) & 0xff,
+ ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ) & 0xff,
+ ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ) & 0xff,
+ htons ( pTftpServer->RemoteAddress.sin_port )));
+
+ //
+ // Lookup connection context using the remote system address and port
+ // to determine if an existing connection to this remote
+ // system exists
+ //
+ pContext = ContextFind ( pTftpServer );
+
+ //
+ // Process the received message
+ //
+ TftpProcessRequest ( pTftpServer, pContext );
+ }
+ else {
+ //
+ // Receive error on the TFTP server port
+ // Close the server socket
+ //
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",
+ errno ));
+ pTftpServer->TftpPort.revents |= POLLHUP;
+ }
+ }
+
+ //
+ // Handle the close event
+ //
+ if ( 0 != ( pTftpServer->TftpPort.revents & POLLHUP )) {
+ //
+ // Close the port
+ //
+ close ( pTftpServer->TftpPort.fd );
+ pTftpServer->TftpPort.fd = -1;
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Scan the list of sockets and process any pending work
+
+ @param [in] pTftpServer The TFTP server control structure address.
+
+**/
+VOID
+SocketPoll (
+ IN TSDT_TFTP_SERVER * pTftpServer
+ )
+{
+ int FDCount;
+
+ DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));
+
+ //
+ // Determine if any ports are active
+ //
+ FDCount = poll ( &pTftpServer->TftpPort,
+ 1,
+ CLIENT_POLL_DELAY );
+ if ( -1 == FDCount ) {
+ DEBUG (( DEBUG_ERROR | DEBUG_SOCKET_POLL,
+ "ERROR - errno: %d\r\n",
+ errno ));
+ }
+
+ if ( 0 < FDCount ) {
+ //
+ // Process this port
+ //
+ PortWork ( pTftpServer );
+ pTftpServer->TftpPort.revents = 0;
+ }
+
+ DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));
+}
+
+
+/**
+ Convert a character to lower case
+
+ @param [in] Character The character to convert
+
+ @return The lower case equivalent of the character
+
+**/
+int
+tolower (
+ int Character
+ )
+{
+ //
+ // Determine if the character is upper case
+ //
+ if (( 'A' <= Character ) && ( 'Z' >= Character )) {
+ //
+ // Convert the character to lower caes
+ //
+ Character += 'a' - 'A';
+ }
+
+ //
+ // Return the converted character
+ //
+ return Character;
+}
+
+
+/**
+ Case independent string comparison
+
+ @param [in] pString1 Zero terminated string address
+ @param [in] pString2 Zero terminated string address
+
+ @return Returns the first character difference between string 1
+ and string 2.
+
+**/
+int
+stricmp (
+ char * pString1,
+ char * pString2
+ )
+{
+ int Char1;
+ int Char2;
+ int Difference;
+
+ //
+ // Walk the length of the strings
+ //
+ do {
+ //
+ // Get the next characters
+ //
+ Char1 = (UINT8)*pString1++;
+ Char2 = (UINT8)*pString2++;
+
+ //
+ // Convert them to lower case
+ //
+ Char1 = tolower ( Char1 );
+ Char2 = tolower ( Char2 );
+
+ //
+ // Done when the characters differ
+ //
+ Difference = Char1 - Char2;
+ if ( 0 != Difference ) {
+ break;
+ }
+
+ //
+ // Done at the end of the string
+ //
+ } while ( 0 != Char1 );
+
+ //
+ // Return the difference
+ //
+ return Difference;
+}
+
+
+/**
+ Get the next TFTP option
+
+ @param [in] pOption Address of a zero terminated option string
+ @param [in] pEnd End of buffer address
+ @param [in] ppNextOption Address to receive the address of the next
+ zero terminated option string
+
+ @retval EFI_SUCCESS Message processed successfully
+
+**/
+EFI_STATUS
+TftpOptionGet (
+ IN UINT8 * pOption,
+ IN UINT8 * pEnd,
+ IN UINT8 ** ppNextOption
+ )
+{
+ UINT8 * pNextOption;
+ EFI_STATUS Status;
+
+ //
+ // Locate the end of the option
+ //
+ pNextOption = pOption;
+ while (( pEnd > pNextOption ) && ( 0 != *pNextOption )) {
+ pNextOption += 1;
+ }
+ if ( pEnd <= pNextOption ) {
+ //
+ // Error - end of buffer reached
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
+ "ERROR - Option without zero termination received!\r\n" ));
+ Status = EFI_INVALID_PARAMETER;
+ }
+ else {
+ //
+ // Zero terminated option found
+ //
+ pNextOption += 1;
+
+ //
+ // Display the zero terminated ASCII option string
+ //
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "Option: %a\r\n",
+ pOption ));
+ Status = EFI_SUCCESS;
+ }
+
+ //
+ // Return the next option address
+ //
+ *ppNextOption = pNextOption;
+
+ //
+ // Return the operation status
+ //
+ return Status;
+}
+
+
+/**
+ Place an option value into the option acknowledgement
+
+ @param [in] pOack Option acknowledgement address
+ @param [in] Value Value to translate into ASCII decimal
+
+ @return Option acknowledgement address
+
+**/
+UINT8 *
+TftpOptionSet (
+ IN UINT8 * pOack,
+ IN UINT64 Value
+ )
+{
+ UINT64 NextValue;
+
+ //
+ // Determine the next value
+ //
+ NextValue = Value / 10;
+
+ //
+ // Supress leading zeros
+ //
+ if ( 0 != NextValue ) {
+ pOack = TftpOptionSet ( pOack, NextValue );
+ }
+
+ //
+ // Output this digit
+ //
+ *pOack++ = (UINT8)( Value - ( NextValue * 10 ) + '0' );
+
+ //
+ // Return the next option acknowledgement location
+ //
+ return pOack;
+}
+
+
+/**
+ Process the TFTP request
+
+ @param [in] pContext The context structure address.
+ @param [in] pOption Address of the first zero terminated option string
+ @param [in] pEnd End of buffer address
+
+**/
+VOID
+TftpOptions (
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN UINT8 * pOption,
+ IN UINT8 * pEnd
+ )
+{
+ UINT8 * pNextOption;
+ UINT8 * pOack;
+ UINT8 * pTemp;
+ UINT8 * pValue;
+ EFI_STATUS Status;
+ INT32 Value;
+
+ //
+ // Start the OACK packet
+ // Let the OACK handle the parsing errors
+ // See http://tools.ietf.org/html/rfc2347
+ //
+ pOack = &pContext->TxBuffer[0];
+ *pOack++ = 0;
+ *pOack++ = TFTP_OP_OACK;
+ pContext->TxBytes = 2;
+
+ //
+ // Walk the list of options
+ //
+ do {
+ //
+ // Get the next option, skip junk at end of message
+ //
+ Status = TftpOptionGet ( pOption, pEnd, &pNextOption );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Process the option
+ //
+
+ //
+ // blksize - See http://tools.ietf.org/html/rfc2348
+ //
+ pValue = pNextOption;
+ if ( 0 == stricmp ((char *)pOption, "blksize" )) {
+ //
+ // Get the value
+ //
+ Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Validate the block size, skip non-numeric block sizes
+ //
+ Status = TftpOptionValue ( pValue, &Value );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Propose a smaller block size if necessary
+ //
+ if ( Value > TFTP_MAX_BLOCK_SIZE ) {
+ Value = TFTP_MAX_BLOCK_SIZE;
+ }
+
+ //
+ // Set the new block size
+ //
+ pContext->BlockSize = Value;
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "Using block size of %d bytes\r\n",
+ pContext->BlockSize ));
+
+ //
+ // Update the OACK
+ //
+ pTemp = pOack;
+ *pOack++ = 'b';
+ *pOack++ = 'l';
+ *pOack++ = 'k';
+ *pOack++ = 's';
+ *pOack++ = 'i';
+ *pOack++ = 'z';
+ *pOack++ = 'e';
+ *pOack++ = 0;
+ pOack = TftpOptionSet ( pOack, pContext->BlockSize );
+ *pOack++ = 0;
+ pContext->TxBytes += pOack - pTemp;
+ }
+ }
+ }
+
+ //
+ // timeout - See http://tools.ietf.org/html/rfc2349
+ //
+ else if ( 0 == stricmp ((char *)pOption, "timeout" )) {
+ //
+ // Get the value
+ //
+ Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
+ if ( !EFI_ERROR ( Status )) {
+ Status = TftpOptionValue ( pValue, &Value );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Set the timeout value
+ //
+ pContext->Timeout = Value;
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "Using timeout of %d seconds\r\n",
+ pContext->Timeout ));
+
+ //
+ // Update the OACK
+ //
+ pTemp = pOack;
+ *pOack++ = 't';
+ *pOack++ = 'i';
+ *pOack++ = 'm';
+ *pOack++ = 'e';
+ *pOack++ = 'o';
+ *pOack++ = 'u';
+ *pOack++ = 't';
+ *pOack++ = 0;
+ pOack = TftpOptionSet ( pOack, pContext->Timeout );
+ *pOack++ = 0;
+ pContext->TxBytes += pOack - pTemp;
+ }
+ }
+ }
+
+ //
+ // tsize - See http://tools.ietf.org/html/rfc2349
+ //
+ else if ( 0 == stricmp ((char *)pOption, "tsize" )) {
+ //
+ // Get the value
+ //
+ Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
+ if ( !EFI_ERROR ( Status )) {
+ Status = TftpOptionValue ( pValue, &Value );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Return the file size
+ //
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "Returning file size of %Ld bytes\r\n",
+ pContext->LengthInBytes ));
+
+ //
+ // Update the OACK
+ //
+ pTemp = pOack;
+ *pOack++ = 't';
+ *pOack++ = 's';
+ *pOack++ = 'i';
+ *pOack++ = 'z';
+ *pOack++ = 'e';
+ *pOack++ = 0;
+ pOack = TftpOptionSet ( pOack, pContext->LengthInBytes );
+ *pOack++ = 0;
+ pContext->TxBytes += pOack - pTemp;
+ }
+ }
+ }
+ else {
+ //
+ // Unknown option - Ignore it
+ //
+ DEBUG (( DEBUG_WARN | DEBUG_TFTP_REQUEST,
+ "WARNING - Skipping unknown option: %a\r\n",
+ pOption ));
+ }
+ }
+
+ //
+ // Set the next option
+ //
+ pOption = pNextOption;
+ } while ( pEnd > pOption );
+}
+
+
+/**
+ Process the TFTP request
+
+ @param [in] pOption Address of the first zero terminated option string
+ @param [in] pValue Address to receive the value
+
+ @retval EFI_SUCCESS Option translated into a value
+
+**/
+EFI_STATUS
+TftpOptionValue (
+ IN UINT8 * pOption,
+ IN INT32 * pValue
+ )
+{
+ UINT8 Digit;
+ EFI_STATUS Status;
+ INT32 Value;
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Walk the characters in the option
+ //
+ Value = 0;
+ while ( 0 != *pOption ) {
+ //
+ // Convert the next digit to binary
+ //
+ Digit = *pOption++;
+ if (( '0' <= Digit ) && ( '9' >= Digit )) {
+ Value *= 10;
+ Value += Digit - '0';
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
+ "ERROR - Invalid character '0x%02x' in the value\r\n",
+ Digit ));
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ //
+ // Return the value
+ //
+ *pValue = Value;
+
+ //
+ // Return the conversion status
+ //
+ return Status;
+}
+
+
+/**
+ Process the TFTP request
+
+ @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pContext Connection context structure address
+
+**/
+VOID
+TftpProcessRequest (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext
+ )
+{
+ BOOLEAN bCloseContext;
+ BOOLEAN bIgnorePacket;
+ UINT16 BlockNumber;
+ UINT16 Opcode;
+ UINT8 * pBuffer;
+ UINT8 * pEnd;
+ UINT8 * pFileName;
+ UINT8 * pMode;
+ UINT8 * pOption;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Get the opcode
+ //
+ pBuffer = &pTftpServer->RxBuffer[0];
+ Opcode = HTONS ( *(UINT16 *)&pBuffer[0]);
+Print ( L"TFTP Opcode: 0x%08x\r\n", Opcode );
+
+ //
+ // Validate the parameters
+ //
+ bCloseContext = FALSE;
+ bIgnorePacket = FALSE;
+ switch ( Opcode ) {
+ default:
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "ERROR - Unknown TFTP opcode: %d\r\n",
+ Opcode ));
+ bIgnorePacket = TRUE;
+ break;
+
+ case TFTP_OP_READ_REQUEST:
+ break;
+
+ case TFTP_OP_DATA:
+ if ( NULL == pContext ) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
+ (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
+ htons ( pTftpServer->RemoteAddress.sin_port )));
+ bIgnorePacket = TRUE;
+ break;
+ }
+ if ( pContext->bExpectAck ) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",
+ pContext ));
+ bIgnorePacket = TRUE;
+ break;
+ }
+ if ( pTftpServer->RxBytes > (ssize_t)( pContext->BlockSize + 2 + 2 ))
+ {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Receive data length of %d > %d bytes (maximum block size) for pContext 0x%08x\r\n",
+ pTftpServer->RxBytes - 2 - 2,
+ pContext->BlockSize,
+ pContext ));
+ bIgnorePacket = TRUE;
+ break;
+ }
+ break;
+
+ case TFTP_OP_ACK:
+ if ( NULL == pContext ) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
+ (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
+ htons ( pTftpServer->RemoteAddress.sin_port )));
+ bIgnorePacket = TRUE;
+ }
+ if ( !pContext->bExpectAck ) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",
+ pContext ));
+ bIgnorePacket = TRUE;
+ break;
+ }
+ break;
+
+ case TFTP_OP_ERROR:
+ if ( NULL == pContext ) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
+ (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),
+ (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),
+ htons ( pTftpServer->RemoteAddress.sin_port )));
+ bIgnorePacket = TRUE;
+ }
+ break;
+ }
+ if ( !bIgnorePacket ) {
+ //
+ // Process the request
+ //
+ switch ( Opcode ) {
+ default:
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "ERROR - Unable to process TFTP opcode: %d\r\n",
+ Opcode ));
+ break;
+
+ case TFTP_OP_READ_REQUEST:
+
+ //
+ // Close the context if necessary
+ //
+ if ( NULL != pContext ) {
+ ContextRemove ( pTftpServer, pContext );
+ }
+
+ //
+ // Create the connection context
+ //
+ pContext = ContextAdd ( pTftpServer );
+ if ( NULL == pContext ) {
+ break;
+ }
+
+ //
+ // Locate the mode
+ //
+ pFileName = &pBuffer[2];
+ pEnd = &pBuffer[pTftpServer->RxBytes];
+ pMode = pFileName;
+ while (( pEnd > pMode ) && ( 0 != *pMode )) {
+ pMode += 1;
+ }
+ if ( pEnd <= pMode ) {
+ //
+ // Mode not found
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_RX,
+ "ERROR - File mode not found\r\n" ));
+ //
+ // Tell the client of the error
+ //
+ TftpSendError ( pTftpServer,
+ pContext,
+ 0,
+ (UINT8 *)"File open mode not found" );
+ break;
+ }
+ pMode += 1;
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "TFTP - FileName: %a\n",
+ pFileName ));
+
+ //
+ // Locate the options
+ //
+ pOption = pMode;
+ while (( pEnd > pOption ) && ( 0 != *pOption )) {
+ pOption += 1;
+ }
+ if ( pEnd <= pOption ) {
+ //
+ // End of mode not found
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_RX,
+ "ERROR - File mode not valid\r\n" ));
+ //
+ // Tell the client of the error
+ //
+ TftpSendError ( pTftpServer,
+ pContext,
+ 0,
+ (UINT8 *)"File open mode not valid" );
+ break;
+ }
+ pOption += 1;
+ DEBUG (( DEBUG_TFTP_REQUEST,
+ "TFTP - Mode: %a\r\n",
+ pMode ));
+
+ //
+ // Verify the mode is supported
+ //
+ if ( 0 != stricmp ((char *)pMode, "octet" )) {
+ //
+ // File access mode not supported
+ //
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
+ "ERROR - File mode %a not supported\r\n",
+ pMode ));
+
+ //
+ // Tell the client of the error
+ //
+ TftpSendError ( pTftpServer,
+ pContext,
+ 0,
+ (UINT8 *)"File open mode not supported" );
+ break;
+ }
+
+ //
+ // Open the file, close the context on error
+ //
+// TODO: Remove the following line
+pContext->File = (EFI_HANDLE)1;
+
+ //
+ // Determine the file length
+ //
+//fstat
+
+ //
+ // Process the options
+ //
+ TftpOptions ( pContext, pOption, pEnd );
+
+ //
+ // Read in the first portion of the file
+ //
+
+ //
+ // Send the first block
+ //
+ pContext->bExpectAck = TRUE;
+ if ( 2 < pContext->TxBytes ) {
+ //
+ // Send the OACK
+ //
+ Status = TftpTxPacket ( pTftpServer, pContext );
+ }
+ else {
+ //
+ // Send the first block of data
+ //
+ Status = TftpSendNextBlock ( pTftpServer, pContext );
+ }
+ break;
+
+ case TFTP_OP_ACK:
+ //
+ // Get the block number that is being ACKed
+ //
+ BlockNumber = pTftpServer->RxBuffer[2];
+ BlockNumber <<= 8;
+ BlockNumber |= pTftpServer->RxBuffer[3];
+
+ //
+ // Determine if this is the correct ACK
+ //
+ DEBUG (( DEBUG_TFTP_ACK,
+ "ACK for block 0x%04x received\r\n",
+ BlockNumber ));
+ if (( !pContext->bExpectAck )
+ || ( BlockNumber != pContext->AckNext ))
+ {
+ DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,
+ "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",
+ pContext->AckNext,
+ BlockNumber ));
+ }
+ else {
+ //
+ // Process the expected ACK
+ //
+ if ( pContext->bEofSent ) {
+ bCloseContext = TRUE;
+ }
+ else {
+ //
+ // Set the next expected ACK
+ //
+ pContext->AckNext += 1;
+
+ //
+ // Send the next packet of data
+ //
+ Status = TftpSendNextBlock ( pTftpServer, pContext );
+ }
+ }
+ break;
+ }
+ }
+
+ //
+ // Determine if the context should be closed
+ //
+ if ( bCloseContext ) {
+ ContextRemove ( pTftpServer, pContext );
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Build and send an error packet
+
+ @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pContext The context structure address.
+ @param [in] Error Error number for the packet
+ @param [in] pError Zero terminated error string address
+
+ @retval EFI_SUCCESS Message processed successfully
+
+**/
+EFI_STATUS
+TftpSendError (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext,
+ IN UINT16 Error,
+ IN UINT8 * pError
+ )
+{
+ UINT8 Character;
+ UINT8 * pBuffer;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Build the error packet
+ //
+ pBuffer = &pContext->TxBuffer[0];
+ pBuffer[0] = 0;
+ pBuffer[1] = TFTP_OP_ERROR;
+ pBuffer[2] = (UINT8)( Error >> 8 );
+ pBuffer[3] = (UINT8)Error;
+
+ //
+ // Copy the zero terminated string into the buffer
+ //
+ pBuffer += 4;
+ do {
+ Character = *pError++;
+ *pBuffer++ = Character;
+ } while ( 0 != Character );
+
+ //
+ // Send the error message
+ //
+ pContext->TxBytes = pBuffer - &pContext->TxBuffer[0];
+ Status = TftpTxPacket ( pTftpServer, pContext );
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Send the next block of file system data
+
+ @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pContext The context structure address.
+
+ @retval EFI_SUCCESS Message processed successfully
+
+**/
+EFI_STATUS
+TftpSendNextBlock (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext
+ )
+{
+ ssize_t LengthInBytes;
+ UINT8 * pBuffer;
+ EFI_STATUS Status;
+
+ //
+ // Determine how much data needs to be sent
+ //
+ LengthInBytes = pContext->BlockSize;
+ if (( pContext->LengthInBytes < TFTP_MAX_BLOCK_SIZE )
+ || ( LengthInBytes > (ssize_t)pContext->LengthInBytes )) {
+ LengthInBytes = (ssize_t)pContext->LengthInBytes;
+ pContext->bEofSent = TRUE;
+ }
+
+ //
+ // Set the TFTP opcode and block number
+ //
+ pBuffer = &pContext->TxBuffer[0];
+ *pBuffer++ = 0;
+ *pBuffer++ = TFTP_OP_DATA;
+ *pBuffer++ = (UINT8)( pContext->AckNext >> 8 );
+ *pBuffer++ = (UINT8)pContext->AckNext;
+
+ //
+ // Copy the file data into the transmit buffer
+ //
+ pContext->TxBytes = 2 + 2 + LengthInBytes;
+ if ( 0 < LengthInBytes ) {
+ CopyMem ( &pBuffer,
+ pContext->pBuffer,
+ LengthInBytes );
+ }
+
+ //
+ // Send the next block
+ //
+ Status = TftpTxPacket ( pTftpServer, pContext );
+
+ //
+ // Return the operation status
+ //
+ return Status;
+}
+
+
+/**
+ Create the port for the TFTP server
+
+ This routine polls the network layer to create the TFTP port for the
+ TFTP server. More than one attempt may be necessary since it may take
+ some time to get the IP address and initialize the upper layers of
+ the network stack.
+
+ @param [in] pTftpServer The TFTP server control structure address.
+
+**/
+VOID
+TftpServerTimer (
+ IN TSDT_TFTP_SERVER * pTftpServer
+ )
+{
+ UINT16 TftpPort;
+ int SocketStatus;
+ EFI_STATUS Status;
+
+ DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerTimer\r\n" ));
+
+ //
+ // Open the TFTP port on the server
+ //
+ do {
+ do {
+ //
+ // Wait for a while
+ //
+ Status = gBS->CheckEvent ( pTftpServer->TimerEvent );
+ } while ( EFI_SUCCESS != Status );
+
+ //
+ // Attempt to create the socket for the TFTP server
+ //
+ pTftpServer->TftpPort.events = POLLRDNORM | POLLHUP;
+ pTftpServer->TftpPort.revents = 0;
+ pTftpServer->TftpPort.fd = socket ( AF_INET,
+ SOCK_DGRAM,
+ IPPROTO_UDP );
+ if ( -1 != pTftpServer->TftpPort.fd )
+ {
+ //
+ // Set the socket address
+ //
+ ZeroMem ( &pTftpServer->TftpServerAddress,
+ sizeof ( pTftpServer->TftpServerAddress ));
+ TftpPort = 69;
+ DEBUG (( DEBUG_TFTP_PORT,
+ "TFTP Port: %d\r\n",
+ TftpPort ));
+ pTftpServer->TftpServerAddress.sin_len = sizeof ( pTftpServer->TftpServerAddress );
+ pTftpServer->TftpServerAddress.sin_family = AF_INET;
+ pTftpServer->TftpServerAddress.sin_addr.s_addr = INADDR_ANY;
+ pTftpServer->TftpServerAddress.sin_port = htons ( TftpPort );
+
+ //
+ // Bind the socket to the TFTP port
+ //
+ SocketStatus = bind ( pTftpServer->TftpPort.fd,
+ (struct sockaddr *) &pTftpServer->TftpServerAddress,
+ pTftpServer->TftpServerAddress.sin_len );
+ if ( -1 != SocketStatus ) {
+ DEBUG (( DEBUG_TFTP_PORT,
+ "0x%08x: Socket bound to port %d\r\n",
+ pTftpServer->TftpPort.fd,
+ TftpPort ));
+ }
+
+ //
+ // Release the socket if necessary
+ //
+ if ( -1 == SocketStatus ) {
+ close ( pTftpServer->TftpPort.fd );
+ pTftpServer->TftpPort.fd = -1;
+ }
+ }
+
+ //
+ // Wait until the socket is open
+ //
+ }while ( -1 == pTftpServer->TftpPort.fd );
+
+ DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerTimer\r\n" ));
+}
+
+
+/**
+ Start the TFTP server port creation timer
+
+ @param [in] pTftpServer The TFTP server control structure address.
+
+ @retval EFI_SUCCESS The timer was successfully started.
+ @retval EFI_ALREADY_STARTED The timer is already running.
+ @retval Other The timer failed to start.
+
+**/
+EFI_STATUS
+TftpServerTimerStart (
+ IN TSDT_TFTP_SERVER * pTftpServer
+ )
+{
+ EFI_STATUS Status;
+ UINT64 TriggerTime;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume the timer is already running
+ //
+ Status = EFI_ALREADY_STARTED;
+ if ( !pTftpServer->bTimerRunning ) {
+ //
+ // Compute the poll interval
+ //
+ TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );
+ Status = gBS->SetTimer ( pTftpServer->TimerEvent,
+ TimerPeriodic,
+ TriggerTime );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));
+
+ //
+ // Mark the timer running
+ //
+ pTftpServer->bTimerRunning = TRUE;
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_PORT,
+ "ERROR - Failed to start TFTP port timer, Status: %r\r\n",
+ Status ));
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Stop the TFTP server port creation timer
+
+ @param [in] pTftpServer The TFTP server control structure address.
+
+ @retval EFI_SUCCESS The TFTP port timer is stopped
+ @retval Other Failed to stop the TFTP port timer
+
+**/
+EFI_STATUS
+TftpServerTimerStop (
+ IN TSDT_TFTP_SERVER * pTftpServer
+ )
+{
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume the timer is stopped
+ //
+ Status = EFI_SUCCESS;
+ if ( pTftpServer->bTimerRunning ) {
+ //
+ // Stop the port creation polling
+ //
+ Status = gBS->SetTimer ( pTftpServer->TimerEvent,
+ TimerCancel,
+ 0 );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_TFTP_PORT, "TFT[ port timer stopped\r\n" ));
+
+ //
+ // Mark the timer stopped
+ //
+ pTftpServer->bTimerRunning = FALSE;
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_PORT,
+ "ERROR - Failed to stop TFT[ port timer, Status: %r\r\n",
+ Status ));
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+/**
+ Send the next TFTP packet
+
+ @param [in] pTftpServer The TFTP server control structure address.
+ @param [in] pContext The context structure address.
+
+ @retval EFI_SUCCESS Message processed successfully
+
+**/
+EFI_STATUS
+TftpTxPacket (
+ IN TSDT_TFTP_SERVER * pTftpServer,
+ IN TSDT_CONNECTION_CONTEXT * pContext
+ )
+{
+ ssize_t LengthInBytes;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Send the TFTP packet
+ //
+ DEBUG (( DEBUG_TX,
+ "0x%08x: pContext sending 0x%08x bytes\r\n",
+ pContext,
+ pContext->TxBytes ));
+ LengthInBytes = sendto ( pTftpServer->TftpPort.fd,
+ &pContext->TxBuffer[0],
+ pContext->TxBytes,
+ 0,
+ (struct sockaddr *)&pContext->RemoteAddress,
+ pContext->RemoteAddress.sin_len );
+ if ( -1 == LengthInBytes ) {
+ DEBUG (( DEBUG_ERROR | DEBUG_TX,
+ "ERROR - Transmit failure, errno: 0x%08x\r\n",
+ errno ));
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Entry point for the TFTP server application.
+
+ @param [in] Argc The number of arguments
+ @param [in] Argv The argument value array
+
+ @retval 0 The application exited normally.
+ @retval Other An error occurred.
+**/
+int
+main (
+ IN int Argc,
+ IN char **Argv
+ )
+{
+ TSDT_TFTP_SERVER * pTftpServer;
+ EFI_STATUS Status;
+
+ //
+ // Create a timer event to start TFTP port
+ //
+ pTftpServer = &mTftpServer;
+ Status = gBS->CreateEvent ( EVT_TIMER,
+ TPL_TFTP_SERVER,
+ NULL,
+ NULL,
+ &pTftpServer->TimerEvent );
+ if ( !EFI_ERROR ( Status )) {
+ Status = TftpServerTimerStart ( pTftpServer );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Run the TFTP server forever
+ //
+ for ( ; ; ) {
+ //
+ // Poll the network layer to create the TFTP port
+ // for the tftp server. More than one attempt may
+ // be necessary since it may take some time to get
+ // the IP address and initialize the upper layers
+ // of the network stack.
+ //
+ TftpServerTimer ( pTftpServer );
+
+ //
+ // Poll the socket for activity
+ //
+ do {
+ SocketPoll ( pTftpServer );
+ } while ( -1 != pTftpServer->TftpPort.fd );
+
+//
+// TODO: Remove the following test code
+// Exit when the network connection is broken
+//
+break;
+ }
+
+ //
+ // Done with the timer event
+ //
+ TftpServerTimerStop ( pTftpServer );
+ Status = gBS->CloseEvent ( pTftpServer->TimerEvent );
+ }
+ }
+
+ //
+ // Return the final status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}