-/*++
- 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;
-}
+/** @file\r
+ This is a simple TFTP server application\r
+\r
+ Copyright (c) 2011, 2012, Intel Corporation\r
+ All rights reserved. This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions of the BSD License\r
+ which accompanies this distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <TftpServer.h>\r
+\r
+TSDT_TFTP_SERVER mTftpServer; ///< TFTP server's control structure\r
+volatile BOOLEAN mbTftpServerExit; ///< Set TRUE to cause TFTP server to exit\r
+\r
+\r
+/**\r
+ Read file data into a buffer\r
+\r
+ @param [in] pContext Connection context structure address\r
+\r
+ @retval TRUE if a read error occurred\r
+\r
+**/\r
+BOOLEAN\r
+BufferFill (\r
+ IN TSDT_CONNECTION_CONTEXT * pContext\r
+ )\r
+{\r
+ BOOLEAN bReadError;\r
+ size_t BytesRead;\r
+ UINT64 LengthInBytes;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Use break instead of goto\r
+ //\r
+ bReadError = FALSE;\r
+ for ( ; ; ) {\r
+ //\r
+ // Determine if there is any work to do\r
+ //\r
+ LengthInBytes = DIM ( pContext->FileData ) >> 1;\r
+ if (( pContext->ValidBytes > LengthInBytes )\r
+ || ( 0 == pContext->BytesRemaining )) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Determine the number of bytes to read\r
+ //\r
+ if ( LengthInBytes > pContext->BytesRemaining ) {\r
+ LengthInBytes = pContext->BytesRemaining;\r
+ }\r
+\r
+ //\r
+ // Read in the next portion of the file\r
+ //\r
+ BytesRead = fread ( pContext->pFill,\r
+ 1,\r
+ (size_t)LengthInBytes,\r
+ pContext->File );\r
+ if ( -1 == BytesRead ) {\r
+ bReadError = TRUE;\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Account for the file data read\r
+ //\r
+ pContext->BytesRemaining -= BytesRead;\r
+ pContext->ValidBytes += BytesRead;\r
+ DEBUG (( DEBUG_FILE_BUFFER,\r
+ "0x%08x: Buffer filled with %Ld bytes, %Ld bytes ramaining\r\n",\r
+ pContext->pFill,\r
+ BytesRead,\r
+ pContext->BytesRemaining ));\r
+\r
+ //\r
+ // Set the next buffer location\r
+ //\r
+ pContext->pFill += BytesRead;\r
+ if ( pContext->pEnd <= pContext->pFill ) {\r
+ pContext->pFill = &pContext->FileData[ 0 ];\r
+ }\r
+\r
+ //\r
+ // Verify that the end of the buffer is reached\r
+ //\r
+ ASSERT ( 0 == ( DIM ( pContext->FileData ) & 1 ));\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Return the read status\r
+ //\r
+ DBG_EXIT ( );\r
+ return bReadError;\r
+}\r
+\r
+\r
+/**\r
+ Add a connection context to the list of connection contexts.\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] SocketFd Socket file descriptor\r
+\r
+ @retval Context structure address, NULL if allocation fails\r
+\r
+**/\r
+TSDT_CONNECTION_CONTEXT *\r
+ContextAdd (\r
+ IN TSDT_TFTP_SERVER * pTftpServer,\r
+ IN int SocketFd\r
+ )\r
+{\r
+ TSDT_CONNECTION_CONTEXT * pContext;\r
+ TFTP_PACKET * pEnd;\r
+ TFTP_PACKET * pPacket;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Allocate a new context\r
+ //\r
+ pContext = (TSDT_CONNECTION_CONTEXT *)AllocateZeroPool ( sizeof ( *pContext ));\r
+ if ( NULL != pContext ) {\r
+ //\r
+ // Initialize the context\r
+ //\r
+ pContext->SocketFd = SocketFd;\r
+ CopyMem ( &pContext->RemoteAddress,\r
+ &pTftpServer->RemoteAddress,\r
+ sizeof ( pContext->RemoteAddress ));\r
+ pContext->BlockSize = 512;\r
+\r
+ //\r
+ // Buffer management\r
+ //\r
+ pContext->pFill = &pContext->FileData[ 0 ];\r
+ pContext->pEnd = &pContext->FileData[ sizeof ( pContext->FileData )];\r
+ pContext->pBuffer = pContext->pFill;\r
+\r
+ //\r
+ // Window management\r
+ //\r
+ pContext->MaxTimeout = MultU64x32 ( PcdGet32 ( Tftp_MaxTimeoutInSec ),\r
+ 2 * 1000 * 1000 * 1000 );\r
+ pContext->Rtt2x = pContext->MaxTimeout;\r
+ pContext->WindowSize = MAX_PACKETS;\r
+ WindowTimeout ( pContext );\r
+\r
+ //\r
+ // Place the packets on the free list\r
+ //\r
+ pPacket = &pContext->Tx[ 0 ];\r
+ pEnd = &pPacket[ DIM ( pContext->Tx )];\r
+ while ( pEnd > pPacket ) {\r
+ PacketFree ( pContext, pPacket );\r
+ pPacket += 1;\r
+ }\r
+\r
+ //\r
+ // Display the new context\r
+ //\r
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+ DEBUG (( DEBUG_PORT_WORK,\r
+ "0x%08x: Context for %d.%d.%d.%d:%d\r\n",\r
+ pContext,\r
+ (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+ }\r
+ else {\r
+ DEBUG (( DEBUG_PORT_WORK,\r
+ "0x%08x: Context for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+ pContext,\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+ }\r
+\r
+ //\r
+ // Add the context to the context list\r
+ //\r
+ pContext->pNext = pTftpServer->pContextList;\r
+ pTftpServer->pContextList = pContext;\r
+ }\r
+\r
+ //\r
+ // Return the connection context\r
+ //\r
+ DBG_EXIT_STATUS ( pContext );\r
+ return pContext;\r
+}\r
+\r
+\r
+/**\r
+ Locate a remote connection context.\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] pIpAddress The start of the remote IP address in network order\r
+ @param [in] Port The remote port number\r
+\r
+ @retval Context structure address, NULL if not found\r
+\r
+**/\r
+TSDT_CONNECTION_CONTEXT *\r
+ContextFind (\r
+ IN TSDT_TFTP_SERVER * pTftpServer\r
+ )\r
+{\r
+ TSDT_CONNECTION_CONTEXT * pContext;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Walk the list of connection contexts\r
+ //\r
+ pContext = pTftpServer->pContextList;\r
+ while ( NULL != pContext ) {\r
+ //\r
+ // Attempt to locate the remote network connection\r
+ //\r
+ if ( 0 == memcmp ( &pTftpServer->RemoteAddress,\r
+ &pContext->RemoteAddress,\r
+ pTftpServer->RemoteAddress.v6.sin6_len )) {\r
+ //\r
+ // The connection was found\r
+ //\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "0x%08x: pContext found\r\n",\r
+ pContext ));\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Set the next context\r
+ //\r
+ pContext = pContext->pNext;\r
+ }\r
+\r
+ //\r
+ // Return the connection context structure address\r
+ //\r
+ DBG_EXIT_HEX ( pContext );\r
+ return pContext;\r
+}\r
+\r
+\r
+/**\r
+ Remove a context from the list.\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+\r
+**/\r
+VOID\r
+ContextRemove (\r
+ IN TSDT_TFTP_SERVER * pTftpServer,\r
+ IN TSDT_CONNECTION_CONTEXT * pContext\r
+ )\r
+{\r
+ TSDT_CONNECTION_CONTEXT * pNextContext;\r
+ TSDT_CONNECTION_CONTEXT * pPreviousContext;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Attempt to locate the context in the list\r
+ //\r
+ pPreviousContext = NULL;\r
+ pNextContext = pTftpServer->pContextList;\r
+ while ( NULL != pNextContext ) {\r
+ //\r
+ // Determine if the context was found\r
+ //\r
+ if ( pNextContext == pContext ) {\r
+ //\r
+ // Remove the context from the list\r
+ //\r
+ if ( NULL == pPreviousContext ) {\r
+ pTftpServer->pContextList = pContext->pNext;\r
+ }\r
+ else {\r
+ pPreviousContext->pNext = pContext->pNext;\r
+ }\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Set the next context\r
+ //\r
+ pPreviousContext = pNextContext;\r
+ pNextContext = pNextContext->pNext;\r
+ }\r
+\r
+ //\r
+ // Determine if the context was found\r
+ //\r
+ if ( NULL != pContext ) {\r
+ //\r
+ // Return the resources\r
+ //\r
+ gBS->FreePool ( pContext );\r
+ }\r
+\r
+ DBG_EXIT ( );\r
+}\r
+\r
+\r
+/**\r
+ Queue data packets for transmission\r
+\r
+ @param [in] pContext Connection context structure address\r
+\r
+ @retval TRUE if a read error occurred\r
+\r
+**/\r
+BOOLEAN\r
+PacketFill (\r
+ IN TSDT_CONNECTION_CONTEXT * pContext\r
+ )\r
+{\r
+ BOOLEAN bReadError;\r
+ UINT64 LengthInBytes;\r
+ UINT8 * pBuffer;\r
+ TFTP_PACKET * pPacket;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Use break instead of goto\r
+ //\r
+ bReadError = FALSE;\r
+ for ( ; ; ) {\r
+ //\r
+ // Fill the buffer if necessary\r
+ //\r
+ bReadError = BufferFill ( pContext );\r
+ if ( bReadError ) {\r
+ //\r
+ // File access mode not supported\r
+ //\r
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
+ "ERROR - File read failure!\r\n" ));\r
+\r
+ //\r
+ // Tell the client of the error\r
+ //\r
+ SendError ( pContext,\r
+ TFTP_ERROR_SEE_MSG,\r
+ (UINT8 *)"Read failure" );\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Determine if any packets can be filled\r
+ //\r
+ if ( pContext->bEofSent\r
+ || ( NULL == pContext->pFreeList )) {\r
+ //\r
+ // All of the packets are filled\r
+ //\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Set the TFTP opcode and block number\r
+ //\r
+ pPacket = PacketGet ( pContext );\r
+ pBuffer = &pPacket->TxBuffer[ 0 ];\r
+ *pBuffer++ = 0;\r
+ *pBuffer++ = TFTP_OP_DATA;\r
+ *pBuffer++ = (UINT8)( pContext->BlockNumber >> 8 );\r
+ *pBuffer++ = (UINT8)pContext->BlockNumber;\r
+\r
+ //\r
+ // Determine how much data needs to be sent\r
+ //\r
+ LengthInBytes = pContext->BlockSize;\r
+ if (( pContext->BytesToSend < TFTP_MAX_BLOCK_SIZE )\r
+ && ( LengthInBytes > pContext->BytesToSend )) {\r
+ LengthInBytes = pContext->BytesToSend;\r
+ pContext->bEofSent = TRUE;\r
+ }\r
+ DEBUG (( DEBUG_TX_PACKET,\r
+ "0x%08x: Packet, Block %d filled with %d bytes\r\n",\r
+ pPacket,\r
+ pContext->BlockNumber,\r
+ (UINT32)LengthInBytes ));\r
+ \r
+ //\r
+ // Copy the file data into the packet\r
+ //\r
+ pPacket->TxBytes = (ssize_t)( 2 + 2 + LengthInBytes );\r
+ if ( 0 < LengthInBytes ) {\r
+ CopyMem ( pBuffer,\r
+ pContext->pBuffer,\r
+ (UINTN)LengthInBytes );\r
+ DEBUG (( DEBUG_FILE_BUFFER,\r
+ "0x%08x: Buffer consumed %d bytes of file data\r\n",\r
+ pContext->pBuffer,\r
+ LengthInBytes ));\r
+\r
+ //\r
+ // Account for the file data consumed\r
+ //\r
+ pContext->ValidBytes -= LengthInBytes;\r
+ pContext->BytesToSend -= LengthInBytes;\r
+ pContext->pBuffer += LengthInBytes;\r
+ if ( pContext->pEnd <= pContext->pBuffer ) {\r
+ pContext->pBuffer = &pContext->FileData[ 0 ];\r
+ }\r
+ }\r
+ \r
+ //\r
+ // Queue the packet for transmission\r
+ //\r
+ PacketQueue ( pContext, pPacket );\r
+ }\r
+\r
+ //\r
+ // Return the read status\r
+ //\r
+ DBG_EXIT ( );\r
+ return bReadError;\r
+}\r
+\r
+\r
+/**\r
+ Free the packet\r
+\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+ @param [in] pPacket Address of a ::TFTP_PACKET structure\r
+\r
+**/\r
+VOID\r
+PacketFree(\r
+ IN TSDT_CONNECTION_CONTEXT * pContext,\r
+ IN TFTP_PACKET * pPacket\r
+ )\r
+{\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Don't free the error packet\r
+ //\r
+ if ( pPacket != &pContext->ErrorPacket ) {\r
+ //\r
+ // Place the packet on the free list\r
+ //\r
+ pPacket->pNext = pContext->pFreeList;\r
+ pContext->pFreeList = pPacket;\r
+ DEBUG (( DEBUG_TX_PACKET,\r
+ "0x%08x: Packet queued to free list\r\n",\r
+ pPacket ));\r
+ }\r
+\r
+ DBG_EXIT ( );\r
+}\r
+\r
+\r
+/**\r
+ Get a packet from the free list for transmission\r
+\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+\r
+ @retval Address of a ::TFTP_PACKET structure\r
+\r
+**/\r
+TFTP_PACKET *\r
+PacketGet (\r
+ IN TSDT_CONNECTION_CONTEXT * pContext\r
+ )\r
+{\r
+ TFTP_PACKET * pPacket;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Get the next packet from the free list\r
+ //\r
+ pPacket = pContext->pFreeList;\r
+ if ( NULL != pPacket ) {\r
+ pContext->pFreeList = pPacket->pNext;\r
+ pPacket->RetryCount = 0;\r
+ DEBUG (( DEBUG_TX_PACKET,\r
+ "0x%08x: Packet removed from free list\r\n",\r
+ pPacket ));\r
+ }\r
+\r
+ //\r
+ // Return the packet\r
+ //\r
+ DBG_EXIT_HEX ( pPacket );\r
+ return pPacket;\r
+}\r
+\r
+\r
+/**\r
+ Queue the packet for transmission\r
+\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+ @param [in] pPacket Address of a ::TFTP_PACKET structure\r
+\r
+ @retval TRUE if a transmission error has occurred\r
+\r
+**/\r
+BOOLEAN\r
+PacketQueue (\r
+ IN TSDT_CONNECTION_CONTEXT * pContext,\r
+ IN TFTP_PACKET * pPacket\r
+ )\r
+{\r
+ BOOLEAN bTransmitError;\r
+ TFTP_PACKET * pTail;\r
+ EFI_STATUS Status;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Account for this data block\r
+ //\r
+ pPacket->BlockNumber = pContext->BlockNumber;\r
+ pContext->BlockNumber += 1;\r
+\r
+ //\r
+ // Queue the packet for transmission\r
+ //\r
+ pTail = pContext->pTxTail;\r
+ if ( NULL == pTail ) {\r
+ pContext->pTxHead = pPacket;\r
+ }\r
+ else {\r
+ pTail->pNext = pPacket;\r
+ }\r
+ pContext->pTxTail = pPacket;\r
+ pPacket->pNext = NULL;\r
+ DEBUG (( DEBUG_TX_PACKET,\r
+ "0x%08x: Packet queued to TX list\r\n",\r
+ pPacket ));\r
+\r
+ //\r
+ // Start the transmission if necessary\r
+ //\r
+ bTransmitError = FALSE;\r
+ if ( pContext->PacketsInWindow < pContext->WindowSize ) {\r
+ Status = PacketTx ( pContext, pPacket );\r
+ bTransmitError = (BOOLEAN)( EFI_ERROR ( Status ));\r
+ }\r
+\r
+ //\r
+ // Return the transmit status\r
+ //\r
+ DBG_EXIT_TF ( bTransmitError );\r
+ return bTransmitError;\r
+}\r
+\r
+\r
+/**\r
+ Remove a packet from the transmit queue\r
+\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+\r
+**/\r
+TFTP_PACKET *\r
+PacketRemove(\r
+ IN TSDT_CONNECTION_CONTEXT * pContext\r
+ )\r
+{\r
+ TFTP_PACKET * pNext;\r
+ TFTP_PACKET * pPacket;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Remove a packet from the transmit queue\r
+ //\r
+ //\r
+ pPacket = pContext->pTxHead;\r
+ if ( NULL != pPacket ) {\r
+ pNext = pPacket->pNext;\r
+ pContext->pTxHead = pNext;\r
+ if ( NULL == pNext ) {\r
+ pContext->pTxTail = NULL;\r
+ }\r
+ DEBUG (( DEBUG_TX_PACKET,\r
+ "0x%08x: Packet removed from TX list\r\n",\r
+ pPacket ));\r
+\r
+ //\r
+ // Remove this packet from the window\r
+ //\r
+ pContext->PacketsInWindow -= 1;\r
+ }\r
+\r
+ //\r
+ // Return the packet\r
+ //\r
+ DBG_EXIT_HEX ( pPacket );\r
+ return pPacket;\r
+}\r
+\r
+\r
+/**\r
+ Transmit the packet\r
+\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+ @param [in] pPacket Address of a ::TFTP_PACKET structure\r
+\r
+ @retval EFI_SUCCESS Message processed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+PacketTx (\r
+ IN TSDT_CONNECTION_CONTEXT * pContext,\r
+ IN TFTP_PACKET * pPacket\r
+ )\r
+{\r
+ ssize_t LengthInBytes;\r
+ EFI_STATUS Status;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Assume success\r
+ //\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Determine if this packet should be transmitted\r
+ //\r
+ if ( PcdGet32 ( Tftp_MaxRetry ) >= pPacket->RetryCount ) {\r
+ pPacket->RetryCount += 1;\r
+\r
+ //\r
+ // Display the operation\r
+ //\r
+ DEBUG (( DEBUG_TX_PACKET,\r
+ "0x%08x: Packet transmiting\r\n",\r
+ pPacket ));\r
+ DEBUG (( DEBUG_TX,\r
+ "0x%08x: pContext sending 0x%08x bytes\r\n",\r
+ pContext,\r
+ pPacket->TxBytes ));\r
+\r
+ //\r
+ // Keep track of when the packet was transmitted\r
+ //\r
+ if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+ pPacket->TxTime = GetPerformanceCounter ( );\r
+ }\r
+\r
+ //\r
+ // Send the TFTP packet\r
+ //\r
+ pContext->PacketsInWindow += 1;\r
+ LengthInBytes = sendto ( pContext->SocketFd,\r
+ &pPacket->TxBuffer[ 0 ],\r
+ pPacket->TxBytes,\r
+ 0,\r
+ (struct sockaddr *)&pContext->RemoteAddress,\r
+ pContext->RemoteAddress.sin6_len );\r
+ if ( -1 == LengthInBytes ) {\r
+ DEBUG (( DEBUG_ERROR | DEBUG_TX,\r
+ "ERROR - Transmit failure, errno: 0x%08x\r\n",\r
+ errno ));\r
+ pContext->PacketsInWindow -= 1;\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+ else {\r
+ //\r
+ // Too many retries\r
+ //\r
+ Status = EFI_NO_RESPONSE;\r
+ DEBUG (( DEBUG_WARN | DEBUG_WINDOW,\r
+ "WARNING - No response from TFTP client\r\n" ));\r
+ }\r
+\r
+ //\r
+ // Return the operation status\r
+ //\r
+ DBG_EXIT_STATUS ( Status );\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Process the work for the sockets.\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] pIndex Address of an index into the pollfd array\r
+\r
+**/\r
+VOID\r
+PortWork (\r
+ IN TSDT_TFTP_SERVER * pTftpServer,\r
+ IN int * pIndex\r
+ )\r
+{\r
+ int Index;\r
+ TSDT_CONNECTION_CONTEXT * pContext;\r
+ struct pollfd * pTftpPort;\r
+ socklen_t RemoteAddressLength;\r
+ int revents;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Locate the port\r
+ //\r
+ Index = *pIndex;\r
+ if ( -1 != Index ) {\r
+ pTftpPort = &pTftpServer->TftpPort[ *pIndex ];\r
+\r
+ //\r
+ // Handle input events\r
+ //\r
+ revents = pTftpPort->revents;\r
+ pTftpPort->revents = 0;\r
+ if ( 0 != ( revents & POLLRDNORM )) {\r
+ //\r
+ // Receive the message from the remote system\r
+ //\r
+ RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );\r
+ pTftpServer->RxBytes = recvfrom ( pTftpPort->fd,\r
+ &pTftpServer->RxBuffer[ 0 ],\r
+ sizeof ( pTftpServer->RxBuffer ),\r
+ 0,\r
+ (struct sockaddr *) &pTftpServer->RemoteAddress,\r
+ &RemoteAddressLength );\r
+ if ( -1 != pTftpServer->RxBytes ) {\r
+ if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+ pTftpServer->RxTime = GetPerformanceCounter ( );\r
+ }\r
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+ DEBUG (( DEBUG_TFTP_PORT,\r
+ "Received %d bytes from %d.%d.%d.%d:%d\r\n",\r
+ pTftpServer->RxBytes,\r
+ pTftpServer->RemoteAddress.v4.sin_addr.s_addr & 0xff,\r
+ ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ) & 0xff,\r
+ ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ) & 0xff,\r
+ ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ) & 0xff,\r
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+ }\r
+ else {\r
+ DEBUG (( DEBUG_TFTP_PORT,\r
+ "Received %d bytes from [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+ pTftpServer->RxBytes,\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+ }\r
+\r
+ //\r
+ // Lookup connection context using the remote system address and port\r
+ // to determine if an existing connection to this remote\r
+ // system exists\r
+ //\r
+ pContext = ContextFind ( pTftpServer );\r
+\r
+ //\r
+ // Process the received message\r
+ //\r
+ TftpProcessRequest ( pTftpServer, pContext, pTftpPort->fd );\r
+ }\r
+ else {\r
+ //\r
+ // Receive error on the TFTP server port\r
+ // Close the server socket\r
+ //\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",\r
+ errno ));\r
+ revents |= POLLHUP;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Handle the close event\r
+ //\r
+ if ( 0 != ( revents & POLLHUP )) {\r
+ //\r
+ // Close the port\r
+ //\r
+ close ( pTftpPort->fd );\r
+ pTftpPort->fd = -1;\r
+ *pIndex = -1;\r
+ pTftpServer->Entries -= 1;\r
+ ASSERT ( 0 <= pTftpServer->Entries );\r
+ }\r
+ }\r
+\r
+ DBG_EXIT ( );\r
+}\r
+\r
+\r
+/**\r
+ Build and send an error packet\r
+\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+ @param [in] Error Error number for the packet\r
+ @param [in] pError Zero terminated error string address\r
+\r
+ @retval EFI_SUCCESS Message processed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+SendError (\r
+ IN TSDT_CONNECTION_CONTEXT * pContext,\r
+ IN UINT16 Error,\r
+ IN UINT8 * pError\r
+ )\r
+{\r
+ UINT8 Character;\r
+ UINT8 * pBuffer;\r
+ TFTP_PACKET * pPacket;\r
+ EFI_STATUS Status;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Build the error packet\r
+ //\r
+ pPacket = &pContext->ErrorPacket;\r
+ pBuffer = &pPacket->TxBuffer[ 0 ];\r
+ pBuffer[ 0 ] = 0;\r
+ pBuffer[ 1 ] = TFTP_OP_ERROR;\r
+ pBuffer[ 2 ] = (UINT8)( Error >> 8 );\r
+ pBuffer[ 3 ] = (UINT8)Error;\r
+\r
+ //\r
+ // Copy the zero terminated string into the buffer\r
+ //\r
+ pBuffer += 4;\r
+ do {\r
+ Character = *pError++;\r
+ *pBuffer++ = Character;\r
+ } while ( 0 != Character );\r
+\r
+ //\r
+ // Send the error message\r
+ //\r
+ pPacket->TxBytes = pBuffer - &pPacket->TxBuffer[ 0 ];\r
+ Status = PacketTx ( pContext, pPacket );\r
+\r
+ //\r
+ // Return the operation status\r
+ //\r
+ DBG_EXIT_STATUS ( Status );\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Scan the list of sockets and process any pending work\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+\r
+**/\r
+VOID\r
+SocketPoll (\r
+ IN TSDT_TFTP_SERVER * pTftpServer\r
+ )\r
+{\r
+ int FDCount;\r
+\r
+ DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));\r
+\r
+ //\r
+ // Determine if any ports are active\r
+ //\r
+ if ( 0 != pTftpServer->Entries ) {\r
+ FDCount = poll ( &pTftpServer->TftpPort[ 0 ],\r
+ pTftpServer->Entries,\r
+ CLIENT_POLL_DELAY );\r
+ if ( 0 < FDCount ) {\r
+ //\r
+ // Process this port\r
+ //\r
+ PortWork ( pTftpServer, &pTftpServer->Udpv4Index );\r
+ PortWork ( pTftpServer, &pTftpServer->Udpv6Index );\r
+ }\r
+ }\r
+\r
+ DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));\r
+}\r
+\r
+\r
+/**\r
+ Process the ACK\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] pContext Connection context structure address\r
+\r
+ @retval TRUE if the context should be closed\r
+\r
+**/\r
+BOOLEAN\r
+TftpAck (\r
+ IN TSDT_TFTP_SERVER * pTftpServer,\r
+ IN TSDT_CONNECTION_CONTEXT * pContext\r
+ )\r
+{\r
+ INTN AckNumber;\r
+ BOOLEAN bCloseContext;\r
+ UINT16 BlockNumber;\r
+ UINT8 * pBuffer;\r
+ TFTP_PACKET * pPacket;\r
+ EFI_STATUS Status;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Use break instead of goto\r
+ //\r
+ bCloseContext = FALSE;\r
+ for ( ; ; ) {\r
+ //\r
+ // Validate the parameters\r
+ //\r
+ if ( NULL == pContext ) {\r
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
+ (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+ }\r
+ else {\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+ }\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Verify that the ACK was expected\r
+ //\r
+ pPacket = pContext->pTxHead;\r
+ if ( NULL == pPacket ) {\r
+ //\r
+ // ACK not expected!\r
+ //\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",\r
+ pContext ));\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Get the ACKed block number\r
+ //\r
+ pBuffer = &pTftpServer->RxBuffer[ 0 ];\r
+ BlockNumber = HTONS ( *(UINT16 *)&pBuffer[ 2 ]);\r
+\r
+ //\r
+ // Determine if this is the correct ACK\r
+ //\r
+ DEBUG (( DEBUG_TFTP_ACK,\r
+ "ACK for block 0x%04x received\r\n",\r
+ BlockNumber ));\r
+ AckNumber = BlockNumber - pPacket->BlockNumber;\r
+ if (( 0 > AckNumber ) || ( AckNumber >= (INTN)pContext->PacketsInWindow )){\r
+ DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,\r
+ "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",\r
+ pPacket->BlockNumber,\r
+ BlockNumber ));\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Release the ACKed packets\r
+ //\r
+ do {\r
+ //\r
+ // Remove the packet from the transmit list and window\r
+ //\r
+ pPacket = PacketRemove ( pContext );\r
+\r
+ //\r
+ // Get the block number of this packet\r
+ //\r
+ AckNumber = pPacket->BlockNumber;\r
+\r
+ //\r
+ // Increase the size of the transmit window\r
+ //\r
+ if ( PcdGetBool ( Tftp_HighSpeed )\r
+ && ( AckNumber == BlockNumber )) {\r
+ WindowAck ( pTftpServer, pContext, pPacket );\r
+ }\r
+\r
+ //\r
+ // Free this packet\r
+ //\r
+ PacketFree ( pContext, pPacket );\r
+ } while (( NULL != pContext->pTxHead ) && ( AckNumber != BlockNumber ));\r
+\r
+ //\r
+ // Fill the window with packets\r
+ //\r
+ pPacket = pContext->pTxHead;\r
+ while (( NULL != pPacket )\r
+ && ( pContext->PacketsInWindow < pContext->WindowSize )\r
+ && ( !bCloseContext )) {\r
+ Status = PacketTx ( pContext, pPacket );\r
+ bCloseContext = (BOOLEAN)( EFI_ERROR ( Status ));\r
+ pPacket = pPacket->pNext;\r
+ }\r
+ \r
+ //\r
+ // Get more packets ready for transmission\r
+ //\r
+ PacketFill ( pContext );\r
+\r
+ //\r
+ // Close the context when the last packet is ACKed\r
+ //\r
+ if ( 0 == pContext->PacketsInWindow ) {\r
+ bCloseContext = TRUE;\r
+\r
+ //\r
+ // Display the bandwidth\r
+ //\r
+ if ( PcdGetBool ( Tftp_Bandwidth )) {\r
+ UINT64 Bandwidth;\r
+ UINT64 DeltaTime;\r
+ UINT64 NanoSeconds;\r
+ UINT32 Value;\r
+\r
+ //\r
+ // Compute the download time\r
+ //\r
+ DeltaTime = GetPerformanceCounter ( );\r
+ if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
+ DeltaTime = DeltaTime - pContext->TimeStart;\r
+ }\r
+ else {\r
+ DeltaTime = pContext->TimeStart - DeltaTime;\r
+ }\r
+ NanoSeconds = GetTimeInNanoSecond ( DeltaTime );\r
+ Bandwidth = pContext->LengthInBytes;\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "File Length %Ld, Transfer Time: %d.%03d Sec\r\n",\r
+ Bandwidth,\r
+ DivU64x32 ( NanoSeconds, 1000 * 1000 * 1000 ),\r
+ ((UINT32)DivU64x32 ( NanoSeconds, 1000 * 1000 )) % 1000 ));\r
+\r
+ //\r
+ // Display the round trip time\r
+ //\r
+ Bandwidth = MultU64x32 ( Bandwidth, 8 * 1000 * 1000 );\r
+ Bandwidth /= NanoSeconds;\r
+ if ( 1000 > Bandwidth ) {\r
+ Value = (UINT32)Bandwidth;\r
+ Print ( L"Bandwidth: %d Kbits/Sec\r\n",\r
+ Value );\r
+ }\r
+ else if (( 1000 * 1000 ) > Bandwidth ) {\r
+ Value = (UINT32)Bandwidth;\r
+ Print ( L"Bandwidth: %d.%03d Mbits/Sec\r\n",\r
+ Value / 1000,\r
+ Value % 1000 );\r
+ }\r
+ else {\r
+ Value = (UINT32)DivU64x32 ( Bandwidth, 1000 );\r
+ Print ( L"Bandwidth: %d.%03d Gbits/Sec\r\n",\r
+ Value / 1000,\r
+ Value % 1000 );\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Return the operation status\r
+ //\r
+ DBG_EXIT ( );\r
+ return bCloseContext;\r
+}\r
+\r
+\r
+/**\r
+ Get the next TFTP option\r
+\r
+ @param [in] pOption Address of a zero terminated option string\r
+ @param [in] pEnd End of buffer address\r
+ @param [in] ppNextOption Address to receive the address of the next\r
+ zero terminated option string\r
+\r
+ @retval EFI_SUCCESS Message processed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+TftpOptionGet (\r
+ IN UINT8 * pOption,\r
+ IN UINT8 * pEnd,\r
+ IN UINT8 ** ppNextOption\r
+ )\r
+{\r
+ UINT8 * pNextOption;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Locate the end of the option\r
+ //\r
+ pNextOption = pOption;\r
+ while (( pEnd > pNextOption ) && ( 0 != *pNextOption )) {\r
+ pNextOption += 1;\r
+ }\r
+ if ( pEnd <= pNextOption ) {\r
+ //\r
+ // Error - end of buffer reached\r
+ //\r
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
+ "ERROR - Option without zero termination received!\r\n" ));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ }\r
+ else {\r
+ //\r
+ // Zero terminated option found\r
+ //\r
+ pNextOption += 1;\r
+\r
+ //\r
+ // Display the zero terminated ASCII option string\r
+ //\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "Option: %a\r\n",\r
+ pOption ));\r
+ Status = EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Return the next option address\r
+ //\r
+ *ppNextOption = pNextOption;\r
+\r
+ //\r
+ // Return the operation status\r
+ //\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Place an option value into the option acknowledgement\r
+\r
+ @param [in] pOack Option acknowledgement address\r
+ @param [in] Value Value to translate into ASCII decimal\r
+\r
+ @return Option acknowledgement address\r
+\r
+**/\r
+UINT8 *\r
+TftpOptionSet (\r
+ IN UINT8 * pOack,\r
+ IN UINT64 Value\r
+ )\r
+{\r
+ UINT64 NextValue;\r
+\r
+ //\r
+ // Determine the next value\r
+ //\r
+ NextValue = Value / 10;\r
+\r
+ //\r
+ // Supress leading zeros\r
+ //\r
+ if ( 0 != NextValue ) {\r
+ pOack = TftpOptionSet ( pOack, NextValue );\r
+ }\r
+\r
+ //\r
+ // Output this digit\r
+ //\r
+ *pOack++ = (UINT8)( Value - ( NextValue * 10 ) + '0' );\r
+\r
+ //\r
+ // Return the next option acknowledgement location\r
+ //\r
+ return pOack;\r
+}\r
+\r
+\r
+/**\r
+ Process the TFTP request\r
+\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+ @param [in] pOption Address of the first zero terminated option string\r
+ @param [in] pEnd End of buffer address\r
+\r
+**/\r
+VOID\r
+TftpOptions (\r
+ IN TSDT_CONNECTION_CONTEXT * pContext,\r
+ IN UINT8 * pOption,\r
+ IN UINT8 * pEnd\r
+ )\r
+{\r
+ UINT8 * pNextOption;\r
+ UINT8 * pOack;\r
+ TFTP_PACKET * pPacket;\r
+ UINT8 * pTemp;\r
+ UINT8 * pValue;\r
+ EFI_STATUS Status;\r
+ INT32 Value;\r
+\r
+ //\r
+ // Get a packet\r
+ //\r
+ pPacket = PacketGet ( pContext );\r
+\r
+ //\r
+ // Start the OACK packet\r
+ // Let the OACK handle the parsing errors\r
+ // See http://tools.ietf.org/html/rfc2347\r
+ //\r
+ pOack = &pPacket->TxBuffer[ 0 ];\r
+ *pOack++ = 0;\r
+ *pOack++ = TFTP_OP_OACK;\r
+ pPacket->TxBytes = 2;\r
+ pPacket->BlockNumber = 0;\r
+\r
+ //\r
+ // Walk the list of options\r
+ //\r
+ do {\r
+ //\r
+ // Get the next option, skip junk at end of message\r
+ //\r
+ Status = TftpOptionGet ( pOption, pEnd, &pNextOption );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ //\r
+ // Process the option\r
+ //\r
+\r
+ //\r
+ // blksize - See http://tools.ietf.org/html/rfc2348\r
+ //\r
+ pValue = pNextOption;\r
+ if ( 0 == strcasecmp ((char *)pOption, "blksize" )) {\r
+ //\r
+ // Get the value\r
+ //\r
+ Status = TftpOptionGet ( pValue, pEnd, &pNextOption );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ //\r
+ // Validate the block size, skip non-numeric block sizes\r
+ //\r
+ Status = TftpOptionValue ( pValue, &Value );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ //\r
+ // Propose a smaller block size if necessary\r
+ //\r
+ if ( Value > TFTP_MAX_BLOCK_SIZE ) {\r
+ Value = TFTP_MAX_BLOCK_SIZE;\r
+ }\r
+\r
+ //\r
+ // Set the new block size\r
+ //\r
+ pContext->BlockSize = Value;\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "Using block size of %d bytes\r\n",\r
+ pContext->BlockSize ));\r
+\r
+ //\r
+ // Update the OACK\r
+ //\r
+ pTemp = pOack;\r
+ *pOack++ = 'b';\r
+ *pOack++ = 'l';\r
+ *pOack++ = 'k';\r
+ *pOack++ = 's';\r
+ *pOack++ = 'i';\r
+ *pOack++ = 'z';\r
+ *pOack++ = 'e';\r
+ *pOack++ = 0;\r
+ pOack = TftpOptionSet ( pOack, pContext->BlockSize );\r
+ *pOack++ = 0;\r
+ pPacket->TxBytes += pOack - pTemp;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // timeout - See http://tools.ietf.org/html/rfc2349\r
+ //\r
+ else if ( 0 == strcasecmp ((char *)pOption, "timeout" )) {\r
+ //\r
+ // Get the value\r
+ //\r
+ Status = TftpOptionGet ( pValue, pEnd, &pNextOption );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ Status = TftpOptionValue ( pValue, &Value );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ //\r
+ // Set the timeout value\r
+ //\r
+ pContext->MaxTimeout = Value;\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "Using timeout of %d seconds\r\n",\r
+ pContext->MaxTimeout ));\r
+\r
+ //\r
+ // Update the OACK\r
+ //\r
+ pTemp = pOack;\r
+ *pOack++ = 't';\r
+ *pOack++ = 'i';\r
+ *pOack++ = 'm';\r
+ *pOack++ = 'e';\r
+ *pOack++ = 'o';\r
+ *pOack++ = 'u';\r
+ *pOack++ = 't';\r
+ *pOack++ = 0;\r
+ pOack = TftpOptionSet ( pOack, pContext->MaxTimeout );\r
+ *pOack++ = 0;\r
+ pPacket->TxBytes += pOack - pTemp;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // tsize - See http://tools.ietf.org/html/rfc2349\r
+ //\r
+ else if ( 0 == strcasecmp ((char *)pOption, "tsize" )) {\r
+ //\r
+ // Get the value\r
+ //\r
+ Status = TftpOptionGet ( pValue, pEnd, &pNextOption );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ Status = TftpOptionValue ( pValue, &Value );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ //\r
+ // Return the file size\r
+ //\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "Returning file size of %Ld bytes\r\n",\r
+ pContext->LengthInBytes ));\r
+\r
+ //\r
+ // Update the OACK\r
+ //\r
+ pTemp = pOack;\r
+ *pOack++ = 't';\r
+ *pOack++ = 's';\r
+ *pOack++ = 'i';\r
+ *pOack++ = 'z';\r
+ *pOack++ = 'e';\r
+ *pOack++ = 0;\r
+ pOack = TftpOptionSet ( pOack, pContext->LengthInBytes );\r
+ *pOack++ = 0;\r
+ pPacket->TxBytes += pOack - pTemp;\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ //\r
+ // Unknown option - Ignore it\r
+ //\r
+ DEBUG (( DEBUG_WARN | DEBUG_TFTP_REQUEST,\r
+ "WARNING - Skipping unknown option: %a\r\n",\r
+ pOption ));\r
+ }\r
+ }\r
+\r
+ //\r
+ // Set the next option\r
+ //\r
+ pOption = pNextOption;\r
+ } while ( pEnd > pOption );\r
+\r
+ //\r
+ // Transmit the OACK if necessary\r
+ //\r
+ if ( 2 < pPacket->TxBytes ) {\r
+ PacketQueue ( pContext, pPacket );\r
+ }\r
+ else {\r
+ PacketFree ( pContext, pPacket );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Process the TFTP request\r
+\r
+ @param [in] pOption Address of the first zero terminated option string\r
+ @param [in] pValue Address to receive the value\r
+\r
+ @retval EFI_SUCCESS Option translated into a value\r
+\r
+**/\r
+EFI_STATUS\r
+TftpOptionValue (\r
+ IN UINT8 * pOption,\r
+ IN INT32 * pValue\r
+ )\r
+{\r
+ UINT8 Digit;\r
+ EFI_STATUS Status;\r
+ INT32 Value;\r
+\r
+ //\r
+ // Assume success\r
+ //\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Walk the characters in the option\r
+ //\r
+ Value = 0;\r
+ while ( 0 != *pOption ) {\r
+ //\r
+ // Convert the next digit to binary\r
+ //\r
+ Digit = *pOption++;\r
+ if (( '0' <= Digit ) && ( '9' >= Digit )) {\r
+ Value *= 10;\r
+ Value += Digit - '0';\r
+ }\r
+ else {\r
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
+ "ERROR - Invalid character '0x%02x' in the value\r\n",\r
+ Digit ));\r
+ Status = EFI_INVALID_PARAMETER;\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Return the value\r
+ //\r
+ *pValue = Value;\r
+\r
+ //\r
+ // Return the conversion status\r
+ //\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Process the TFTP request\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+ @param [in] SocketFd Socket file descriptor\r
+\r
+**/\r
+VOID\r
+TftpProcessRequest (\r
+ IN TSDT_TFTP_SERVER * pTftpServer,\r
+ IN TSDT_CONNECTION_CONTEXT * pContext,\r
+ IN int SocketFd\r
+ )\r
+{\r
+ BOOLEAN bCloseContext;\r
+ UINT16 Opcode;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Get the opcode\r
+ //\r
+ Opcode = HTONS ( *(UINT16 *)&pTftpServer->RxBuffer[ 0 ]);\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "TFTP Opcode: 0x%08x\r\n",\r
+ Opcode ));\r
+\r
+ //\r
+ // Validate the parameters\r
+ //\r
+ bCloseContext = FALSE;\r
+ switch ( Opcode ) {\r
+ default:\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "ERROR - Unknown TFTP opcode: %d\r\n",\r
+ Opcode ));\r
+ break;\r
+\r
+ case TFTP_OP_ACK:\r
+ bCloseContext = TftpAck ( pTftpServer, pContext );\r
+ break;\r
+\r
+ case TFTP_OP_READ_REQUEST:\r
+ bCloseContext = TftpRead ( pTftpServer, pContext, SocketFd );\r
+ break;\r
+\r
+\r
+\r
+ \r
+ case TFTP_OP_DATA:\r
+ if ( NULL == pContext ) {\r
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
+ (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+ }\r
+ else {\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+ }\r
+ break;\r
+ }\r
+ if ( 0 != pContext->PacketsInWindow ) {\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",\r
+ pContext ));\r
+ break;\r
+ }\r
+ if ( pTftpServer->RxBytes > (ssize_t)( pContext->BlockSize + 2 + 2 )) {\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - Receive data length of %d > %d bytes (maximum block size) for pContext 0x%08x\r\n",\r
+ pTftpServer->RxBytes - 2 - 2,\r
+ pContext->BlockSize,\r
+ pContext ));\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case TFTP_OP_ERROR:\r
+ if ( NULL == pContext ) {\r
+ if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
+ (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
+ (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
+ htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+ }\r
+ else {\r
+ DEBUG (( DEBUG_ERROR,\r
+ "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+ pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+ htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+ }\r
+ }\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Determine if the context should be closed\r
+ //\r
+ if ( bCloseContext ) {\r
+ ContextRemove ( pTftpServer, pContext );\r
+ }\r
+\r
+ DBG_EXIT ( );\r
+}\r
+\r
+\r
+/**\r
+ Process the read request\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+ @param [in] SocketFd Socket file descriptor\r
+\r
+ @retval TRUE if the context should be closed\r
+\r
+**/\r
+BOOLEAN\r
+TftpRead (\r
+ IN TSDT_TFTP_SERVER * pTftpServer,\r
+ IN TSDT_CONNECTION_CONTEXT * pContext,\r
+ IN int SocketFd\r
+ )\r
+{\r
+ BOOLEAN bCloseContext;\r
+ struct stat FileStatus;\r
+ UINT8 * pBuffer;\r
+ UINT8 * pEnd;\r
+ UINT8 * pFileName;\r
+ UINT8 * pMode;\r
+ UINT8 * pOption;\r
+ CHAR8 * pReadMode;\r
+ UINT64 TimeStart;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Log the receive time\r
+ //\r
+ TimeStart = 0;\r
+ if ( PcdGetBool ( Tftp_Bandwidth )) {\r
+ TimeStart = GetPerformanceCounter ( );\r
+ }\r
+\r
+ //\r
+ // Close the context if necessary\r
+ //\r
+ bCloseContext = FALSE;\r
+ if ( NULL != pContext ) {\r
+ ContextRemove ( pTftpServer, pContext );\r
+ }\r
+\r
+ //\r
+ // Use break instead of goto\r
+ //\r
+ for ( ; ; ) {\r
+ //\r
+ // Create the connection context\r
+ //\r
+ pContext = ContextAdd ( pTftpServer, SocketFd );\r
+ if ( NULL == pContext ) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Set the start time\r
+ //\r
+ if ( PcdGetBool ( Tftp_Bandwidth )) {\r
+ pContext->TimeStart = TimeStart;\r
+ }\r
+\r
+ //\r
+ // Locate the mode\r
+ //\r
+ pBuffer = &pTftpServer->RxBuffer[ 0 ];\r
+ pEnd = &pBuffer[ pTftpServer->RxBytes ];\r
+ pFileName = &pBuffer[ 2 ];\r
+ pMode = pFileName;\r
+ while (( pEnd > pMode ) && ( 0 != *pMode )) {\r
+ pMode += 1;\r
+ }\r
+ if ( pEnd <= pMode ) {\r
+ //\r
+ // Mode not found\r
+ //\r
+ DEBUG (( DEBUG_ERROR | DEBUG_RX,\r
+ "ERROR - File mode not found\r\n" ));\r
+ //\r
+ // Tell the client of the error\r
+ //\r
+ SendError ( pContext,\r
+ TFTP_ERROR_SEE_MSG,\r
+ (UINT8 *)"File open mode not found" );\r
+ break;\r
+ }\r
+ pMode += 1;\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "TFTP - FileName: %a\r\n",\r
+ pFileName ));\r
+\r
+ //\r
+ // Locate the options\r
+ //\r
+ pOption = pMode;\r
+ while (( pEnd > pOption ) && ( 0 != *pOption )) {\r
+ pOption += 1;\r
+ }\r
+ if ( pEnd <= pOption ) {\r
+ //\r
+ // End of mode not found\r
+ //\r
+ DEBUG (( DEBUG_ERROR | DEBUG_RX,\r
+ "ERROR - File mode not valid\r\n" ));\r
+ //\r
+ // Tell the client of the error\r
+ //\r
+ SendError ( pContext,\r
+ TFTP_ERROR_SEE_MSG,\r
+ (UINT8 *)"File open mode not valid" );\r
+ break;\r
+ }\r
+ pOption += 1;\r
+ DEBUG (( DEBUG_TFTP_REQUEST,\r
+ "TFTP - Mode: %a\r\n",\r
+ pMode ));\r
+\r
+ //\r
+ // Verify the mode is supported\r
+ //\r
+ pReadMode = "r";\r
+ if ( 0 == strcasecmp ((char *)pMode, "octet" )) {\r
+ //\r
+ // Read the file as binary input\r
+ //\r
+ pReadMode = "rb";\r
+ }\r
+\r
+ //\r
+ // Determine the file length\r
+ //\r
+ pContext->File = fopen ( pFileName, pReadMode );\r
+ if (( NULL == pContext->File )\r
+ || ( -1 == stat ( pFileName, &FileStatus ))) {\r
+ //\r
+ // File not found\r
+ //\r
+ DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
+ ( NULL == pContext->File )\r
+ ? "ERROR - File not found!\r\n"\r
+ : "ERROR - Unable to determine file %a size!\r\n",\r
+ pFileName ));\r
+\r
+ //\r
+ // Tell the client of the error\r
+ //\r
+ SendError ( pContext,\r
+ TFTP_ERROR_NOT_FOUND,\r
+ (UINT8 *)"File not found" );\r
+ break;\r
+ }\r
+ pContext->LengthInBytes = FileStatus.st_size;\r
+ pContext->BytesRemaining = pContext->LengthInBytes;\r
+ pContext->BytesToSend = pContext->LengthInBytes;\r
+\r
+ //\r
+ // Display the file size\r
+ //\r
+ DEBUG_CODE_BEGIN ( );\r
+ UINT32 Value;\r
+\r
+ if ( 1024 > pContext->LengthInBytes ) {\r
+ Value = (UINT32)pContext->LengthInBytes;\r
+ DEBUG (( DEBUG_FILE_BUFFER,\r
+ "%a size: %d Bytes\r\n",\r
+ pFileName,\r
+ Value ));\r
+ }\r
+ else if (( 1024 * 1024 ) > pContext->LengthInBytes ) {\r
+ Value = (UINT32)pContext->LengthInBytes;\r
+ DEBUG (( DEBUG_FILE_BUFFER,\r
+ "%a size: %d.%03d KiBytes (%Ld Bytes)\r\n",\r
+ pFileName,\r
+ Value / 1024,\r
+ (( Value % 1024 ) * 1000 ) / 1024,\r
+ pContext->LengthInBytes ));\r
+ }\r
+ else if (( 1024 * 1024 * 1024 ) > pContext->LengthInBytes ) {\r
+ Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 );\r
+ DEBUG (( DEBUG_FILE_BUFFER,\r
+ "%a size: %d.%03d MiBytes (%Ld Bytes)\r\n",\r
+ pFileName,\r
+ Value / 1024,\r
+ (( Value % 1024 ) * 1000 ) / 1024,\r
+ pContext->LengthInBytes ));\r
+ }\r
+ else {\r
+ Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 * 1024 );\r
+ DEBUG (( DEBUG_FILE_BUFFER,\r
+ "%a size: %d.%03d GiBytes (%Ld Bytes)\r\n",\r
+ pFileName,\r
+ Value / 1024,\r
+ (( Value % 1024 ) * 1000 ) / 1024,\r
+ pContext->LengthInBytes ));\r
+ }\r
+ DEBUG_CODE_END ( );\r
+\r
+ //\r
+ // Process the options\r
+ //\r
+ if ( pEnd > pOption ) {\r
+ TftpOptions ( pContext, pOption, pEnd );\r
+ }\r
+ else {\r
+ //\r
+ // Skip the open ACK\r
+ //\r
+ pContext->BlockNumber = 1;\r
+ }\r
+\r
+ //\r
+ // Send the first packet (OACK or data block)\r
+ //\r
+ bCloseContext = PacketFill ( pContext );\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Return the close status\r
+ //\r
+ DBG_EXIT ( );\r
+ return bCloseContext;\r
+}\r
+\r
+\r
+/**\r
+ Create the port for the TFTP server\r
+\r
+ This routine polls the network layer to create the TFTP port for the\r
+ TFTP server. More than one attempt may be necessary since it may take\r
+ some time to get the IP address and initialize the upper layers of\r
+ the network stack.\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] AddressFamily The address family to use for the conection.\r
+ @param [in] pIndex Address of the index into the port array\r
+\r
+**/\r
+VOID\r
+TftpServerSocket (\r
+ IN TSDT_TFTP_SERVER * pTftpServer,\r
+ IN sa_family_t AddressFamily,\r
+ IN int * pIndex\r
+ )\r
+{\r
+ int SocketStatus;\r
+ struct pollfd * pTftpPort;\r
+ UINT16 TftpPort;\r
+ union {\r
+ struct sockaddr_in v4;\r
+ struct sockaddr_in6 v6;\r
+ } TftpServerAddress;\r
+\r
+ DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerListen\r\n" ));\r
+\r
+ //\r
+ // Determine if the socket is already initialized\r
+ //\r
+ if ( -1 == *pIndex ) {\r
+ //\r
+ // Attempt to create the socket for the TFTP server\r
+ //\r
+ pTftpPort = &pTftpServer->TftpPort[ pTftpServer->Entries ];\r
+ pTftpPort->fd = socket ( AddressFamily,\r
+ SOCK_DGRAM,\r
+ IPPROTO_UDP );\r
+ if ( -1 != pTftpPort->fd ) {\r
+ //\r
+ // Initialize the poll structure\r
+ //\r
+ pTftpPort->events = POLLRDNORM | POLLHUP;\r
+ pTftpPort->revents = 0;\r
+\r
+ //\r
+ // Set the socket address\r
+ //\r
+ TftpPort = 69;\r
+ ZeroMem ( &TftpServerAddress, sizeof ( TftpServerAddress ));\r
+ TftpServerAddress.v4.sin_port = htons ( TftpPort );\r
+ if ( AF_INET == AddressFamily ) {\r
+ TftpServerAddress.v4.sin_len = sizeof ( TftpServerAddress.v4 );\r
+ TftpServerAddress.v4.sin_family = AF_INET;\r
+ }\r
+ else {\r
+ TftpServerAddress.v6.sin6_len = sizeof ( TftpServerAddress.v6 );\r
+ TftpServerAddress.v6.sin6_family = AF_INET6;\r
+ }\r
+\r
+ //\r
+ // Bind the socket to the TFTP port\r
+ //\r
+ SocketStatus = bind ( pTftpPort->fd,\r
+ (struct sockaddr *) &TftpServerAddress,\r
+ TftpServerAddress.v6.sin6_len );\r
+ if ( -1 != SocketStatus ) {\r
+ DEBUG (( DEBUG_TFTP_PORT,\r
+ "0x%08x: Socket bound to port %d\r\n",\r
+ pTftpPort->fd,\r
+ TftpPort ));\r
+\r
+ //\r
+ // Account for this connection\r
+ //\r
+ *pIndex = pTftpServer->Entries;\r
+ pTftpServer->Entries += 1;\r
+ ASSERT ( DIM ( pTftpServer->TftpPort ) >= pTftpServer->Entries );\r
+ }\r
+\r
+ //\r
+ // Release the socket if necessary\r
+ //\r
+ if ( -1 == SocketStatus ) {\r
+ close ( pTftpPort->fd );\r
+ pTftpPort->fd = -1;\r
+ }\r
+ }\r
+ }\r
+\r
+ DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerListen\r\n" ));\r
+}\r
+\r
+\r
+/**\r
+ Update the window due to the ACK\r
+\r
+ @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+ @param [in] pPacket Address of a ::TFTP_PACKET structure\r
+\r
+**/\r
+VOID\r
+WindowAck (\r
+ IN TSDT_TFTP_SERVER * pTftpServer,\r
+ IN TSDT_CONNECTION_CONTEXT * pContext,\r
+ IN TFTP_PACKET * pPacket\r
+ )\r
+{\r
+ if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+ UINT64 DeltaTime;\r
+ UINT64 NanoSeconds;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Compute the round trip time\r
+ //\r
+ if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
+ DeltaTime = pTftpServer->RxTime - pPacket->TxTime;\r
+ }\r
+ else {\r
+ DeltaTime = pPacket->TxTime - pTftpServer->RxTime;\r
+ }\r
+\r
+ //\r
+ // Adjust the round trip time\r
+ //\r
+ NanoSeconds = GetTimeInNanoSecond ( DeltaTime );\r
+ DeltaTime = RShiftU64 ( pContext->Rtt2x, ACK_SHIFT );\r
+ pContext->Rtt2x += NanoSeconds + NanoSeconds - DeltaTime;\r
+ if ( pContext->Rtt2x > pContext->MaxTimeout ) {\r
+ pContext->Rtt2x = pContext->MaxTimeout;\r
+ }\r
+\r
+ //\r
+ // Account for the ACK\r
+ //\r
+ if ( pContext->WindowSize < MAX_PACKETS ) {\r
+ pContext->AckCount -= 1;\r
+ if ( 0 == pContext->AckCount ) {\r
+ //\r
+ // Increase the window\r
+ //\r
+ pContext->WindowSize += 1;\r
+\r
+ //\r
+ // Set the ACK count\r
+ //\r
+ if ( pContext->WindowSize < pContext->Threshold ) {\r
+ pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );\r
+ }\r
+ else {\r
+ pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;\r
+ }\r
+\r
+ //\r
+ // Display the round trip time\r
+ //\r
+ DEBUG_CODE_BEGIN ( );\r
+ UINT32 Value;\r
+ \r
+ DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );\r
+ if ( 1000 > DeltaTime ) {\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",\r
+ pContext->WindowSize,\r
+ pContext->Threshold,\r
+ pContext->AckCount,\r
+ DeltaTime ));\r
+ }\r
+ else if (( 1000 * 1000 ) > DeltaTime ) {\r
+ Value = (UINT32)DeltaTime;\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",\r
+ pContext->WindowSize,\r
+ pContext->Threshold,\r
+ pContext->AckCount,\r
+ Value / 1000,\r
+ Value % 1000 ));\r
+ }\r
+ else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {\r
+ Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",\r
+ pContext->WindowSize,\r
+ pContext->Threshold,\r
+ pContext->AckCount,\r
+ Value / 1000,\r
+ Value % 1000 ));\r
+ }\r
+ else {\r
+ Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",\r
+ pContext->WindowSize,\r
+ pContext->Threshold,\r
+ pContext->AckCount,\r
+ Value / 1000,\r
+ Value % 1000 ));\r
+ }\r
+ DEBUG_CODE_END ( );\r
+ }\r
+ }\r
+\r
+ DBG_EXIT ( );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ A timeout has occurred, close the window\r
+\r
+ @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+\r
+**/\r
+VOID\r
+WindowTimeout (\r
+ IN TSDT_CONNECTION_CONTEXT * pContext\r
+ )\r
+{\r
+ if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+ TFTP_PACKET * pPacket;\r
+\r
+ DBG_ENTER ( );\r
+\r
+ //\r
+ // Set the threshold at half the previous window size\r
+ //\r
+ pContext->Threshold = ( pContext->WindowSize + 1 ) >> 1;\r
+\r
+ //\r
+ // Close the transmit window\r
+ //\r
+ pContext->WindowSize = 1;\r
+ pContext->PacketsInWindow = 0;\r
+\r
+ //\r
+ // Double the round trip time\r
+ //\r
+ pContext->Rtt2x = LShiftU64 ( pContext->Rtt2x, 1 );\r
+ if ( pContext->Rtt2x > pContext->MaxTimeout ) {\r
+ pContext->Rtt2x = pContext->MaxTimeout;\r
+ }\r
+\r
+ //\r
+ // Set the ACK count\r
+ //\r
+ if ( pContext->WindowSize < pContext->Threshold ) {\r
+ pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );\r
+ }\r
+ else {\r
+ pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;\r
+ }\r
+\r
+ //\r
+ // Display the round trip time\r
+ //\r
+ DEBUG_CODE_BEGIN ( );\r
+ UINT64 DeltaTime;\r
+ UINT32 Value;\r
+ \r
+ DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );\r
+ if ( 1000 > DeltaTime ) {\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",\r
+ pContext->WindowSize,\r
+ pContext->Threshold,\r
+ pContext->AckCount,\r
+ DeltaTime ));\r
+ }\r
+ else if (( 1000 * 1000 ) > DeltaTime ) {\r
+ Value = (UINT32)DeltaTime;\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",\r
+ pContext->WindowSize,\r
+ pContext->Threshold,\r
+ pContext->AckCount,\r
+ Value / 1000,\r
+ Value % 1000 ));\r
+ }\r
+ else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {\r
+ Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",\r
+ pContext->WindowSize,\r
+ pContext->Threshold,\r
+ pContext->AckCount,\r
+ Value / 1000,\r
+ Value % 1000 ));\r
+ }\r
+ else {\r
+ Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",\r
+ pContext->WindowSize,\r
+ pContext->Threshold,\r
+ pContext->AckCount,\r
+ Value / 1000,\r
+ Value % 1000 ));\r
+ }\r
+ DEBUG_CODE_END ( );\r
+\r
+ //\r
+ // Retransmit the first packet in the window\r
+ //\r
+ pPacket = pContext->pTxHead;\r
+ if ( NULL != pPacket ) {\r
+ PacketTx ( pContext, pPacket );\r
+ }\r
+ \r
+ DBG_EXIT ( );\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Entry point for the TFTP server application.\r
+\r
+ @param [in] Argc The number of arguments\r
+ @param [in] Argv The argument value array\r
+\r
+ @retval 0 The application exited normally.\r
+ @retval Other An error occurred.\r
+**/\r
+int\r
+main (\r
+ IN int Argc,\r
+ IN char **Argv\r
+ )\r
+{\r
+ UINTN Index;\r
+ TSDT_TFTP_SERVER * pTftpServer;\r
+ EFI_STATUS Status;\r
+ UINT64 TriggerTime;\r
+\r
+ //\r
+ // Get the performance counter characteristics\r
+ //\r
+ pTftpServer = &mTftpServer;\r
+ if ( PcdGetBool ( Tftp_HighSpeed )\r
+ || PcdGetBool ( Tftp_Bandwidth )) {\r
+ pTftpServer->ClockFrequency = GetPerformanceCounterProperties ( &pTftpServer->Time1,\r
+ &pTftpServer->Time2 );\r
+ }\r
+\r
+ //\r
+ // Create a timer event to start TFTP port\r
+ //\r
+ Status = gBS->CreateEvent ( EVT_TIMER,\r
+ TPL_TFTP_SERVER,\r
+ NULL,\r
+ NULL,\r
+ &pTftpServer->TimerEvent );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ //\r
+ // Compute the poll interval\r
+ //\r
+ TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );\r
+ Status = gBS->SetTimer ( pTftpServer->TimerEvent,\r
+ TimerPeriodic,\r
+ TriggerTime );\r
+ if ( !EFI_ERROR ( Status )) {\r
+ DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));\r
+\r
+ //\r
+ // Run the TFTP server forever\r
+ //\r
+ pTftpServer->Udpv4Index = -1;\r
+ pTftpServer->Udpv6Index = -1;\r
+ do {\r
+ //\r
+ // Poll the network layer to create the TFTP port\r
+ // for the tftp server. More than one attempt may\r
+ // be necessary since it may take some time to get\r
+ // the IP address and initialize the upper layers\r
+ // of the network stack.\r
+ //\r
+ if ( DIM ( pTftpServer->TftpPort ) != pTftpServer->Entries ) {\r
+ do {\r
+ //\r
+ // Wait a while before polling for a connection\r
+ //\r
+ if ( EFI_SUCCESS != gBS->CheckEvent ( pTftpServer->TimerEvent )) {\r
+ if ( 0 == pTftpServer->Entries ) {\r
+ break;\r
+ }\r
+ gBS->WaitForEvent ( 1, &pTftpServer->TimerEvent, &Index );\r
+ }\r
+\r
+ //\r
+ // Poll for a network connection\r
+ //\r
+ TftpServerSocket ( pTftpServer,\r
+ AF_INET,\r
+ &pTftpServer->Udpv4Index );\r
+ TftpServerSocket ( pTftpServer,\r
+ AF_INET6,\r
+ &pTftpServer->Udpv6Index );\r
+ } while ( 0 == pTftpServer->Entries );\r
+ }\r
+\r
+ //\r
+ // Poll the socket for activity\r
+ //\r
+ do {\r
+ SocketPoll ( pTftpServer );\r
+\r
+ //\r
+ // Normal TFTP lets the client request the retransmit by\r
+ // sending another ACK for the previous packet\r
+ //\r
+ if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+ UINT64 CurrentTime;\r
+ UINT64 ElapsedTime;\r
+ TSDT_CONNECTION_CONTEXT * pContext;\r
+ TFTP_PACKET * pPacket;\r
+\r
+ //\r
+ // High speed TFTP uses an agressive retransmit to\r
+ // get the TFTP client moving again when the ACK or\r
+ // previous data packet was lost.\r
+ //\r
+ // Get the current time\r
+ //\r
+ CurrentTime = GetPerformanceCounter ( );\r
+\r
+ //\r
+ // Walk the list of contexts\r
+ //\r
+ pContext = pTftpServer->pContextList;\r
+ while ( NULL != pContext )\r
+ {\r
+ //\r
+ // Check for a transmit timeout\r
+ //\r
+ pPacket = pContext->pTxHead;\r
+ if ( NULL != pPacket ) {\r
+ //\r
+ // Compute the elapsed time\r
+ //\r
+ if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
+ ElapsedTime = CurrentTime - pPacket->TxTime;\r
+ }\r
+ else {\r
+ ElapsedTime = pPacket->TxTime - CurrentTime;\r
+ }\r
+ ElapsedTime = GetTimeInNanoSecond ( ElapsedTime );\r
+\r
+ //\r
+ // Determine if a retransmission is necessary\r
+ //\r
+ if ( ElapsedTime >= pContext->Rtt2x ) {\r
+ DEBUG (( DEBUG_WINDOW,\r
+ "0x%08x: Context TX timeout for packet 0x%08x, Window: %d\r\n",\r
+ pContext,\r
+ pPacket,\r
+ pContext->WindowSize ));\r
+ WindowTimeout ( pContext );\r
+ }\r
+ }\r
+\r
+ //\r
+ // Set the next context\r
+ //\r
+ pContext = pContext->pNext;\r
+ }\r
+ }\r
+ } while ( DIM ( pTftpServer->TftpPort ) == pTftpServer->Entries );\r
+ } while ( !mbTftpServerExit );\r
+\r
+ //\r
+ // Done with the timer event\r
+ //\r
+ gBS->SetTimer ( pTftpServer->TimerEvent,\r
+ TimerCancel,\r
+ 0 );\r
+ }\r
+ gBS->CloseEvent ( pTftpServer->TimerEvent );\r
+ }\r
+\r
+ //\r
+ // Return the final status\r
+ //\r
+ DBG_EXIT_STATUS ( Status );\r
+ return Status;\r
+}\r