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