/** @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; | |
} |