X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=AppPkg%2FApplications%2FSockets%2FTftpServer%2FTftpServer.c;h=9b522d1b5570bdbe5c84342b4afa5275f99edf5e;hp=a1e19c26f96a2a098bfbd9c3b0b6a758c97677c7;hb=7dc1329100c370992cdd430359512443bd1ee9f2;hpb=4684b66f261ffb74fa7575ed10df43cc0645a42f 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 - -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 + +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; +}