2 This is a simple TFTP server application
4 Copyright (c) 2011, 2012, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include <TftpServer.h>
17 TSDT_TFTP_SERVER mTftpServer
; ///< TFTP server's control structure
18 volatile BOOLEAN mbTftpServerExit
; ///< Set TRUE to cause TFTP server to exit
22 Read file data into a buffer
24 @param [in] pContext Connection context structure address
26 @retval TRUE if a read error occurred
31 IN TSDT_CONNECTION_CONTEXT
* pContext
41 // Use break instead of goto
46 // Determine if there is any work to do
48 LengthInBytes
= DIM ( pContext
->FileData
) >> 1;
49 if (( pContext
->ValidBytes
> LengthInBytes
)
50 || ( 0 == pContext
->BytesRemaining
)) {
55 // Determine the number of bytes to read
57 if ( LengthInBytes
> pContext
->BytesRemaining
) {
58 LengthInBytes
= pContext
->BytesRemaining
;
62 // Read in the next portion of the file
64 BytesRead
= fread ( pContext
->pFill
,
66 (size_t)LengthInBytes
,
68 if ( -1 == BytesRead
) {
74 // Account for the file data read
76 pContext
->BytesRemaining
-= BytesRead
;
77 pContext
->ValidBytes
+= BytesRead
;
78 DEBUG (( DEBUG_FILE_BUFFER
,
79 "0x%08x: Buffer filled with %Ld bytes, %Ld bytes ramaining\r\n",
82 pContext
->BytesRemaining
));
85 // Set the next buffer location
87 pContext
->pFill
+= BytesRead
;
88 if ( pContext
->pEnd
<= pContext
->pFill
) {
89 pContext
->pFill
= &pContext
->FileData
[ 0 ];
93 // Verify that the end of the buffer is reached
95 ASSERT ( 0 == ( DIM ( pContext
->FileData
) & 1 ));
100 // Return the read status
108 Add a connection context to the list of connection contexts.
110 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
111 @param [in] SocketFd Socket file descriptor
113 @retval Context structure address, NULL if allocation fails
116 TSDT_CONNECTION_CONTEXT
*
118 IN TSDT_TFTP_SERVER
* pTftpServer
,
122 TSDT_CONNECTION_CONTEXT
* pContext
;
124 TFTP_PACKET
* pPacket
;
129 // Allocate a new context
131 pContext
= (TSDT_CONNECTION_CONTEXT
*)AllocateZeroPool ( sizeof ( *pContext
));
132 if ( NULL
!= pContext
) {
134 // Initialize the context
136 pContext
->SocketFd
= SocketFd
;
137 CopyMem ( &pContext
->RemoteAddress
,
138 &pTftpServer
->RemoteAddress
,
139 sizeof ( pContext
->RemoteAddress
));
140 pContext
->BlockSize
= 512;
145 pContext
->pFill
= &pContext
->FileData
[ 0 ];
146 pContext
->pEnd
= &pContext
->FileData
[ sizeof ( pContext
->FileData
)];
147 pContext
->pBuffer
= pContext
->pFill
;
152 pContext
->MaxTimeout
= MultU64x32 ( PcdGet32 ( Tftp_MaxTimeoutInSec
),
153 2 * 1000 * 1000 * 1000 );
154 pContext
->Rtt2x
= pContext
->MaxTimeout
;
155 pContext
->WindowSize
= MAX_PACKETS
;
156 WindowTimeout ( pContext
);
159 // Place the packets on the free list
161 pPacket
= &pContext
->Tx
[ 0 ];
162 pEnd
= &pPacket
[ DIM ( pContext
->Tx
)];
163 while ( pEnd
> pPacket
) {
164 PacketFree ( pContext
, pPacket
);
169 // Display the new context
171 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
172 DEBUG (( DEBUG_PORT_WORK
,
173 "0x%08x: Context for %d.%d.%d.%d:%d\r\n",
175 (UINT8
)pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
,
176 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ),
177 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ),
178 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ),
179 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
182 DEBUG (( DEBUG_PORT_WORK
,
183 "0x%08x: Context for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
185 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
186 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
187 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
188 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
189 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
190 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
191 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
192 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
193 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
194 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
195 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
196 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
197 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
198 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
199 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
200 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
201 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
205 // Add the context to the context list
207 pContext
->pNext
= pTftpServer
->pContextList
;
208 pTftpServer
->pContextList
= pContext
;
212 // Return the connection context
214 DBG_EXIT_STATUS ( pContext
);
220 Locate a remote connection context.
222 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
223 @param [in] pIpAddress The start of the remote IP address in network order
224 @param [in] Port The remote port number
226 @retval Context structure address, NULL if not found
229 TSDT_CONNECTION_CONTEXT
*
231 IN TSDT_TFTP_SERVER
* pTftpServer
234 TSDT_CONNECTION_CONTEXT
* pContext
;
239 // Walk the list of connection contexts
241 pContext
= pTftpServer
->pContextList
;
242 while ( NULL
!= pContext
) {
244 // Attempt to locate the remote network connection
246 if ( 0 == memcmp ( &pTftpServer
->RemoteAddress
,
247 &pContext
->RemoteAddress
,
248 pTftpServer
->RemoteAddress
.v6
.sin6_len
)) {
250 // The connection was found
252 DEBUG (( DEBUG_TFTP_REQUEST
,
253 "0x%08x: pContext found\r\n",
259 // Set the next context
261 pContext
= pContext
->pNext
;
265 // Return the connection context structure address
267 DBG_EXIT_HEX ( pContext
);
273 Remove a context from the list.
275 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
276 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
281 IN TSDT_TFTP_SERVER
* pTftpServer
,
282 IN TSDT_CONNECTION_CONTEXT
* pContext
285 TSDT_CONNECTION_CONTEXT
* pNextContext
;
286 TSDT_CONNECTION_CONTEXT
* pPreviousContext
;
291 // Attempt to locate the context in the list
293 pPreviousContext
= NULL
;
294 pNextContext
= pTftpServer
->pContextList
;
295 while ( NULL
!= pNextContext
) {
297 // Determine if the context was found
299 if ( pNextContext
== pContext
) {
301 // Remove the context from the list
303 if ( NULL
== pPreviousContext
) {
304 pTftpServer
->pContextList
= pContext
->pNext
;
307 pPreviousContext
->pNext
= pContext
->pNext
;
313 // Set the next context
315 pPreviousContext
= pNextContext
;
316 pNextContext
= pNextContext
->pNext
;
320 // Determine if the context was found
322 if ( NULL
!= pContext
) {
324 // Return the resources
326 gBS
->FreePool ( pContext
);
334 Queue data packets for transmission
336 @param [in] pContext Connection context structure address
338 @retval TRUE if a read error occurred
343 IN TSDT_CONNECTION_CONTEXT
* pContext
347 UINT64 LengthInBytes
;
349 TFTP_PACKET
* pPacket
;
354 // Use break instead of goto
359 // Fill the buffer if necessary
361 bReadError
= BufferFill ( pContext
);
364 // File access mode not supported
366 DEBUG (( DEBUG_ERROR
| DEBUG_TFTP_REQUEST
,
367 "ERROR - File read failure!\r\n" ));
370 // Tell the client of the error
372 SendError ( pContext
,
374 (UINT8
*)"Read failure" );
379 // Determine if any packets can be filled
381 if ( pContext
->bEofSent
382 || ( NULL
== pContext
->pFreeList
)) {
384 // All of the packets are filled
390 // Set the TFTP opcode and block number
392 pPacket
= PacketGet ( pContext
);
393 pBuffer
= &pPacket
->TxBuffer
[ 0 ];
395 *pBuffer
++ = TFTP_OP_DATA
;
396 *pBuffer
++ = (UINT8
)( pContext
->BlockNumber
>> 8 );
397 *pBuffer
++ = (UINT8
)pContext
->BlockNumber
;
400 // Determine how much data needs to be sent
402 LengthInBytes
= pContext
->BlockSize
;
403 if (( pContext
->BytesToSend
< TFTP_MAX_BLOCK_SIZE
)
404 && ( LengthInBytes
> pContext
->BytesToSend
)) {
405 LengthInBytes
= pContext
->BytesToSend
;
406 pContext
->bEofSent
= TRUE
;
408 DEBUG (( DEBUG_TX_PACKET
,
409 "0x%08x: Packet, Block %d filled with %d bytes\r\n",
411 pContext
->BlockNumber
,
412 (UINT32
)LengthInBytes
));
415 // Copy the file data into the packet
417 pPacket
->TxBytes
= (ssize_t
)( 2 + 2 + LengthInBytes
);
418 if ( 0 < LengthInBytes
) {
421 (UINTN
)LengthInBytes
);
422 DEBUG (( DEBUG_FILE_BUFFER
,
423 "0x%08x: Buffer consumed %d bytes of file data\r\n",
428 // Account for the file data consumed
430 pContext
->ValidBytes
-= LengthInBytes
;
431 pContext
->BytesToSend
-= LengthInBytes
;
432 pContext
->pBuffer
+= LengthInBytes
;
433 if ( pContext
->pEnd
<= pContext
->pBuffer
) {
434 pContext
->pBuffer
= &pContext
->FileData
[ 0 ];
439 // Queue the packet for transmission
441 PacketQueue ( pContext
, pPacket
);
445 // Return the read status
455 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
456 @param [in] pPacket Address of a ::TFTP_PACKET structure
461 IN TSDT_CONNECTION_CONTEXT
* pContext
,
462 IN TFTP_PACKET
* pPacket
468 // Don't free the error packet
470 if ( pPacket
!= &pContext
->ErrorPacket
) {
472 // Place the packet on the free list
474 pPacket
->pNext
= pContext
->pFreeList
;
475 pContext
->pFreeList
= pPacket
;
476 DEBUG (( DEBUG_TX_PACKET
,
477 "0x%08x: Packet queued to free list\r\n",
486 Get a packet from the free list for transmission
488 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
490 @retval Address of a ::TFTP_PACKET structure
495 IN TSDT_CONNECTION_CONTEXT
* pContext
498 TFTP_PACKET
* pPacket
;
503 // Get the next packet from the free list
505 pPacket
= pContext
->pFreeList
;
506 if ( NULL
!= pPacket
) {
507 pContext
->pFreeList
= pPacket
->pNext
;
508 pPacket
->RetryCount
= 0;
509 DEBUG (( DEBUG_TX_PACKET
,
510 "0x%08x: Packet removed from free list\r\n",
517 DBG_EXIT_HEX ( pPacket
);
523 Queue the packet for transmission
525 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
526 @param [in] pPacket Address of a ::TFTP_PACKET structure
528 @retval TRUE if a transmission error has occurred
533 IN TSDT_CONNECTION_CONTEXT
* pContext
,
534 IN TFTP_PACKET
* pPacket
537 BOOLEAN bTransmitError
;
544 // Account for this data block
546 pPacket
->BlockNumber
= pContext
->BlockNumber
;
547 pContext
->BlockNumber
+= 1;
550 // Queue the packet for transmission
552 pTail
= pContext
->pTxTail
;
553 if ( NULL
== pTail
) {
554 pContext
->pTxHead
= pPacket
;
557 pTail
->pNext
= pPacket
;
559 pContext
->pTxTail
= pPacket
;
560 pPacket
->pNext
= NULL
;
561 DEBUG (( DEBUG_TX_PACKET
,
562 "0x%08x: Packet queued to TX list\r\n",
566 // Start the transmission if necessary
568 bTransmitError
= FALSE
;
569 if ( pContext
->PacketsInWindow
< pContext
->WindowSize
) {
570 Status
= PacketTx ( pContext
, pPacket
);
571 bTransmitError
= (BOOLEAN
)( EFI_ERROR ( Status
));
575 // Return the transmit status
577 DBG_EXIT_TF ( bTransmitError
);
578 return bTransmitError
;
583 Remove a packet from the transmit queue
585 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
590 IN TSDT_CONNECTION_CONTEXT
* pContext
594 TFTP_PACKET
* pPacket
;
599 // Remove a packet from the transmit queue
602 pPacket
= pContext
->pTxHead
;
603 if ( NULL
!= pPacket
) {
604 pNext
= pPacket
->pNext
;
605 pContext
->pTxHead
= pNext
;
606 if ( NULL
== pNext
) {
607 pContext
->pTxTail
= NULL
;
609 DEBUG (( DEBUG_TX_PACKET
,
610 "0x%08x: Packet removed from TX list\r\n",
614 // Remove this packet from the window
616 pContext
->PacketsInWindow
-= 1;
622 DBG_EXIT_HEX ( pPacket
);
630 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
631 @param [in] pPacket Address of a ::TFTP_PACKET structure
633 @retval EFI_SUCCESS Message processed successfully
638 IN TSDT_CONNECTION_CONTEXT
* pContext
,
639 IN TFTP_PACKET
* pPacket
642 ssize_t LengthInBytes
;
650 Status
= EFI_SUCCESS
;
653 // Determine if this packet should be transmitted
655 if ( PcdGet32 ( Tftp_MaxRetry
) >= pPacket
->RetryCount
) {
656 pPacket
->RetryCount
+= 1;
659 // Display the operation
661 DEBUG (( DEBUG_TX_PACKET
,
662 "0x%08x: Packet transmiting\r\n",
665 "0x%08x: pContext sending 0x%08x bytes\r\n",
670 // Keep track of when the packet was transmitted
672 if ( PcdGetBool ( Tftp_HighSpeed
)) {
673 pPacket
->TxTime
= GetPerformanceCounter ( );
677 // Send the TFTP packet
679 pContext
->PacketsInWindow
+= 1;
680 LengthInBytes
= sendto ( pContext
->SocketFd
,
681 &pPacket
->TxBuffer
[ 0 ],
684 (struct sockaddr
*)&pContext
->RemoteAddress
,
685 pContext
->RemoteAddress
.sin6_len
);
686 if ( -1 == LengthInBytes
) {
687 DEBUG (( DEBUG_ERROR
| DEBUG_TX
,
688 "ERROR - Transmit failure, errno: 0x%08x\r\n",
690 pContext
->PacketsInWindow
-= 1;
691 Status
= EFI_DEVICE_ERROR
;
698 Status
= EFI_NO_RESPONSE
;
699 DEBUG (( DEBUG_WARN
| DEBUG_WINDOW
,
700 "WARNING - No response from TFTP client\r\n" ));
704 // Return the operation status
706 DBG_EXIT_STATUS ( Status
);
712 Process the work for the sockets.
714 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
715 @param [in] pIndex Address of an index into the pollfd array
720 IN TSDT_TFTP_SERVER
* pTftpServer
,
725 TSDT_CONNECTION_CONTEXT
* pContext
;
726 struct pollfd
* pTftpPort
;
727 socklen_t RemoteAddressLength
;
737 pTftpPort
= &pTftpServer
->TftpPort
[ *pIndex
];
740 // Handle input events
742 revents
= pTftpPort
->revents
;
743 pTftpPort
->revents
= 0;
744 if ( 0 != ( revents
& POLLRDNORM
)) {
746 // Receive the message from the remote system
748 RemoteAddressLength
= sizeof ( pTftpServer
->RemoteAddress
);
749 pTftpServer
->RxBytes
= recvfrom ( pTftpPort
->fd
,
750 &pTftpServer
->RxBuffer
[ 0 ],
751 sizeof ( pTftpServer
->RxBuffer
),
753 (struct sockaddr
*) &pTftpServer
->RemoteAddress
,
754 &RemoteAddressLength
);
755 if ( -1 != pTftpServer
->RxBytes
) {
756 if ( PcdGetBool ( Tftp_HighSpeed
)) {
757 pTftpServer
->RxTime
= GetPerformanceCounter ( );
759 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
760 DEBUG (( DEBUG_TFTP_PORT
,
761 "Received %d bytes from %d.%d.%d.%d:%d\r\n",
762 pTftpServer
->RxBytes
,
763 pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
& 0xff,
764 ( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ) & 0xff,
765 ( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ) & 0xff,
766 ( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ) & 0xff,
767 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
770 DEBUG (( DEBUG_TFTP_PORT
,
771 "Received %d bytes from [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
772 pTftpServer
->RxBytes
,
773 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
774 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
775 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
776 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
777 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
778 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
779 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
780 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
781 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
782 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
783 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
784 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
785 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
786 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
787 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
788 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
789 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
793 // Lookup connection context using the remote system address and port
794 // to determine if an existing connection to this remote
797 pContext
= ContextFind ( pTftpServer
);
800 // Process the received message
802 TftpProcessRequest ( pTftpServer
, pContext
, pTftpPort
->fd
);
806 // Receive error on the TFTP server port
807 // Close the server socket
809 DEBUG (( DEBUG_ERROR
,
810 "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",
817 // Handle the close event
819 if ( 0 != ( revents
& POLLHUP
)) {
823 close ( pTftpPort
->fd
);
826 pTftpServer
->Entries
-= 1;
827 ASSERT ( 0 <= pTftpServer
->Entries
);
836 Build and send an error packet
838 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
839 @param [in] Error Error number for the packet
840 @param [in] pError Zero terminated error string address
842 @retval EFI_SUCCESS Message processed successfully
847 IN TSDT_CONNECTION_CONTEXT
* pContext
,
854 TFTP_PACKET
* pPacket
;
860 // Build the error packet
862 pPacket
= &pContext
->ErrorPacket
;
863 pBuffer
= &pPacket
->TxBuffer
[ 0 ];
865 pBuffer
[ 1 ] = TFTP_OP_ERROR
;
866 pBuffer
[ 2 ] = (UINT8
)( Error
>> 8 );
867 pBuffer
[ 3 ] = (UINT8
)Error
;
870 // Copy the zero terminated string into the buffer
874 Character
= *pError
++;
875 *pBuffer
++ = Character
;
876 } while ( 0 != Character
);
879 // Send the error message
881 pPacket
->TxBytes
= pBuffer
- &pPacket
->TxBuffer
[ 0 ];
882 Status
= PacketTx ( pContext
, pPacket
);
885 // Return the operation status
887 DBG_EXIT_STATUS ( Status
);
893 Scan the list of sockets and process any pending work
895 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
900 IN TSDT_TFTP_SERVER
* pTftpServer
905 DEBUG (( DEBUG_SOCKET_POLL
, "Entering SocketPoll\r\n" ));
908 // Determine if any ports are active
910 if ( 0 != pTftpServer
->Entries
) {
911 FDCount
= poll ( &pTftpServer
->TftpPort
[ 0 ],
912 pTftpServer
->Entries
,
918 PortWork ( pTftpServer
, &pTftpServer
->Udpv4Index
);
919 PortWork ( pTftpServer
, &pTftpServer
->Udpv6Index
);
923 DEBUG (( DEBUG_SOCKET_POLL
, "Exiting SocketPoll\r\n" ));
930 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
931 @param [in] pContext Connection context structure address
933 @retval TRUE if the context should be closed
938 IN TSDT_TFTP_SERVER
* pTftpServer
,
939 IN TSDT_CONNECTION_CONTEXT
* pContext
943 BOOLEAN bCloseContext
;
946 TFTP_PACKET
* pPacket
;
952 // Use break instead of goto
954 bCloseContext
= FALSE
;
957 // Validate the parameters
959 if ( NULL
== pContext
) {
960 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
961 DEBUG (( DEBUG_ERROR
,
962 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
963 (UINT8
)pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
,
964 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ),
965 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ),
966 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ),
967 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
970 DEBUG (( DEBUG_ERROR
,
971 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
972 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
973 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
974 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
975 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
976 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
977 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
978 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
979 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
980 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
981 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
982 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
983 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
984 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
985 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
986 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
987 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
988 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
994 // Verify that the ACK was expected
996 pPacket
= pContext
->pTxHead
;
997 if ( NULL
== pPacket
) {
1001 DEBUG (( DEBUG_ERROR
,
1002 "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",
1008 // Get the ACKed block number
1010 pBuffer
= &pTftpServer
->RxBuffer
[ 0 ];
1011 BlockNumber
= HTONS ( *(UINT16
*)&pBuffer
[ 2 ]);
1014 // Determine if this is the correct ACK
1016 DEBUG (( DEBUG_TFTP_ACK
,
1017 "ACK for block 0x%04x received\r\n",
1019 AckNumber
= BlockNumber
- pPacket
->BlockNumber
;
1020 if (( 0 > AckNumber
) || ( AckNumber
>= (INTN
)pContext
->PacketsInWindow
)){
1021 DEBUG (( DEBUG_WARN
| DEBUG_TFTP_ACK
,
1022 "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",
1023 pPacket
->BlockNumber
,
1029 // Release the ACKed packets
1033 // Remove the packet from the transmit list and window
1035 pPacket
= PacketRemove ( pContext
);
1038 // Get the block number of this packet
1040 AckNumber
= pPacket
->BlockNumber
;
1043 // Increase the size of the transmit window
1045 if ( PcdGetBool ( Tftp_HighSpeed
)
1046 && ( AckNumber
== BlockNumber
)) {
1047 WindowAck ( pTftpServer
, pContext
, pPacket
);
1053 PacketFree ( pContext
, pPacket
);
1054 } while (( NULL
!= pContext
->pTxHead
) && ( AckNumber
!= BlockNumber
));
1057 // Fill the window with packets
1059 pPacket
= pContext
->pTxHead
;
1060 while (( NULL
!= pPacket
)
1061 && ( pContext
->PacketsInWindow
< pContext
->WindowSize
)
1062 && ( !bCloseContext
)) {
1063 Status
= PacketTx ( pContext
, pPacket
);
1064 bCloseContext
= (BOOLEAN
)( EFI_ERROR ( Status
));
1065 pPacket
= pPacket
->pNext
;
1069 // Get more packets ready for transmission
1071 PacketFill ( pContext
);
1074 // Close the context when the last packet is ACKed
1076 if ( 0 == pContext
->PacketsInWindow
) {
1077 bCloseContext
= TRUE
;
1080 // Display the bandwidth
1082 if ( PcdGetBool ( Tftp_Bandwidth
)) {
1089 // Compute the download time
1091 DeltaTime
= GetPerformanceCounter ( );
1092 if ( pTftpServer
->Time2
> pTftpServer
->Time1
) {
1093 DeltaTime
= DeltaTime
- pContext
->TimeStart
;
1096 DeltaTime
= pContext
->TimeStart
- DeltaTime
;
1098 NanoSeconds
= GetTimeInNanoSecond ( DeltaTime
);
1099 Bandwidth
= pContext
->LengthInBytes
;
1100 DEBUG (( DEBUG_WINDOW
,
1101 "File Length %Ld, Transfer Time: %d.%03d Sec\r\n",
1103 DivU64x32 ( NanoSeconds
, 1000 * 1000 * 1000 ),
1104 ((UINT32
)DivU64x32 ( NanoSeconds
, 1000 * 1000 )) % 1000 ));
1107 // Display the round trip time
1109 Bandwidth
= MultU64x32 ( Bandwidth
, 8 * 1000 * 1000 );
1110 Bandwidth
/= NanoSeconds
;
1111 if ( 1000 > Bandwidth
) {
1112 Value
= (UINT32
)Bandwidth
;
1113 Print ( L
"Bandwidth: %d Kbits/Sec\r\n",
1116 else if (( 1000 * 1000 ) > Bandwidth
) {
1117 Value
= (UINT32
)Bandwidth
;
1118 Print ( L
"Bandwidth: %d.%03d Mbits/Sec\r\n",
1123 Value
= (UINT32
)DivU64x32 ( Bandwidth
, 1000 );
1124 Print ( L
"Bandwidth: %d.%03d Gbits/Sec\r\n",
1134 // Return the operation status
1137 return bCloseContext
;
1142 Get the next TFTP option
1144 @param [in] pOption Address of a zero terminated option string
1145 @param [in] pEnd End of buffer address
1146 @param [in] ppNextOption Address to receive the address of the next
1147 zero terminated option string
1149 @retval EFI_SUCCESS Message processed successfully
1156 IN UINT8
** ppNextOption
1159 UINT8
* pNextOption
;
1163 // Locate the end of the option
1165 pNextOption
= pOption
;
1166 while (( pEnd
> pNextOption
) && ( 0 != *pNextOption
)) {
1169 if ( pEnd
<= pNextOption
) {
1171 // Error - end of buffer reached
1173 DEBUG (( DEBUG_ERROR
| DEBUG_TFTP_REQUEST
,
1174 "ERROR - Option without zero termination received!\r\n" ));
1175 Status
= EFI_INVALID_PARAMETER
;
1179 // Zero terminated option found
1184 // Display the zero terminated ASCII option string
1186 DEBUG (( DEBUG_TFTP_REQUEST
,
1189 Status
= EFI_SUCCESS
;
1193 // Return the next option address
1195 *ppNextOption
= pNextOption
;
1198 // Return the operation status
1205 Place an option value into the option acknowledgement
1207 @param [in] pOack Option acknowledgement address
1208 @param [in] Value Value to translate into ASCII decimal
1210 @return Option acknowledgement address
1222 // Determine the next value
1224 NextValue
= Value
/ 10;
1227 // Supress leading zeros
1229 if ( 0 != NextValue
) {
1230 pOack
= TftpOptionSet ( pOack
, NextValue
);
1234 // Output this digit
1236 *pOack
++ = (UINT8
)( Value
- ( NextValue
* 10 ) + '0' );
1239 // Return the next option acknowledgement location
1246 Process the TFTP request
1248 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
1249 @param [in] pOption Address of the first zero terminated option string
1250 @param [in] pEnd End of buffer address
1255 IN TSDT_CONNECTION_CONTEXT
* pContext
,
1260 UINT8
* pNextOption
;
1262 TFTP_PACKET
* pPacket
;
1271 pPacket
= PacketGet ( pContext
);
1274 // Start the OACK packet
1275 // Let the OACK handle the parsing errors
1276 // See http://tools.ietf.org/html/rfc2347
1278 pOack
= &pPacket
->TxBuffer
[ 0 ];
1280 *pOack
++ = TFTP_OP_OACK
;
1281 pPacket
->TxBytes
= 2;
1282 pPacket
->BlockNumber
= 0;
1285 // Walk the list of options
1289 // Get the next option, skip junk at end of message
1291 Status
= TftpOptionGet ( pOption
, pEnd
, &pNextOption
);
1292 if ( !EFI_ERROR ( Status
)) {
1294 // Process the option
1298 // blksize - See http://tools.ietf.org/html/rfc2348
1300 pValue
= pNextOption
;
1301 if ( 0 == strcasecmp ((char *)pOption
, "blksize" )) {
1305 Status
= TftpOptionGet ( pValue
, pEnd
, &pNextOption
);
1306 if ( !EFI_ERROR ( Status
)) {
1308 // Validate the block size, skip non-numeric block sizes
1310 Status
= TftpOptionValue ( pValue
, &Value
);
1311 if ( !EFI_ERROR ( Status
)) {
1313 // Propose a smaller block size if necessary
1315 if ( Value
> TFTP_MAX_BLOCK_SIZE
) {
1316 Value
= TFTP_MAX_BLOCK_SIZE
;
1320 // Set the new block size
1322 pContext
->BlockSize
= Value
;
1323 DEBUG (( DEBUG_TFTP_REQUEST
,
1324 "Using block size of %d bytes\r\n",
1325 pContext
->BlockSize
));
1339 pOack
= TftpOptionSet ( pOack
, pContext
->BlockSize
);
1341 pPacket
->TxBytes
+= pOack
- pTemp
;
1347 // timeout - See http://tools.ietf.org/html/rfc2349
1349 else if ( 0 == strcasecmp ((char *)pOption
, "timeout" )) {
1353 Status
= TftpOptionGet ( pValue
, pEnd
, &pNextOption
);
1354 if ( !EFI_ERROR ( Status
)) {
1355 Status
= TftpOptionValue ( pValue
, &Value
);
1356 if ( !EFI_ERROR ( Status
)) {
1358 // Set the timeout value
1360 pContext
->MaxTimeout
= Value
;
1361 DEBUG (( DEBUG_TFTP_REQUEST
,
1362 "Using timeout of %d seconds\r\n",
1363 pContext
->MaxTimeout
));
1377 pOack
= TftpOptionSet ( pOack
, pContext
->MaxTimeout
);
1379 pPacket
->TxBytes
+= pOack
- pTemp
;
1385 // tsize - See http://tools.ietf.org/html/rfc2349
1387 else if ( 0 == strcasecmp ((char *)pOption
, "tsize" )) {
1391 Status
= TftpOptionGet ( pValue
, pEnd
, &pNextOption
);
1392 if ( !EFI_ERROR ( Status
)) {
1393 Status
= TftpOptionValue ( pValue
, &Value
);
1394 if ( !EFI_ERROR ( Status
)) {
1396 // Return the file size
1398 DEBUG (( DEBUG_TFTP_REQUEST
,
1399 "Returning file size of %Ld bytes\r\n",
1400 pContext
->LengthInBytes
));
1412 pOack
= TftpOptionSet ( pOack
, pContext
->LengthInBytes
);
1414 pPacket
->TxBytes
+= pOack
- pTemp
;
1420 // Unknown option - Ignore it
1422 DEBUG (( DEBUG_WARN
| DEBUG_TFTP_REQUEST
,
1423 "WARNING - Skipping unknown option: %a\r\n",
1429 // Set the next option
1431 pOption
= pNextOption
;
1432 } while ( pEnd
> pOption
);
1435 // Transmit the OACK if necessary
1437 if ( 2 < pPacket
->TxBytes
) {
1438 PacketQueue ( pContext
, pPacket
);
1441 PacketFree ( pContext
, pPacket
);
1447 Process the TFTP request
1449 @param [in] pOption Address of the first zero terminated option string
1450 @param [in] pValue Address to receive the value
1452 @retval EFI_SUCCESS Option translated into a value
1468 Status
= EFI_SUCCESS
;
1471 // Walk the characters in the option
1474 while ( 0 != *pOption
) {
1476 // Convert the next digit to binary
1479 if (( '0' <= Digit
) && ( '9' >= Digit
)) {
1481 Value
+= Digit
- '0';
1484 DEBUG (( DEBUG_ERROR
| DEBUG_TFTP_REQUEST
,
1485 "ERROR - Invalid character '0x%02x' in the value\r\n",
1487 Status
= EFI_INVALID_PARAMETER
;
1498 // Return the conversion status
1505 Process the TFTP request
1507 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
1508 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
1509 @param [in] SocketFd Socket file descriptor
1513 TftpProcessRequest (
1514 IN TSDT_TFTP_SERVER
* pTftpServer
,
1515 IN TSDT_CONNECTION_CONTEXT
* pContext
,
1519 BOOLEAN bCloseContext
;
1527 Opcode
= HTONS ( *(UINT16
*)&pTftpServer
->RxBuffer
[ 0 ]);
1528 DEBUG (( DEBUG_TFTP_REQUEST
,
1529 "TFTP Opcode: 0x%08x\r\n",
1533 // Validate the parameters
1535 bCloseContext
= FALSE
;
1538 DEBUG (( DEBUG_TFTP_REQUEST
,
1539 "ERROR - Unknown TFTP opcode: %d\r\n",
1544 bCloseContext
= TftpAck ( pTftpServer
, pContext
);
1547 case TFTP_OP_READ_REQUEST
:
1548 bCloseContext
= TftpRead ( pTftpServer
, pContext
, SocketFd
);
1555 if ( NULL
== pContext
) {
1556 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
1557 DEBUG (( DEBUG_ERROR
,
1558 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
1559 (UINT8
)pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
,
1560 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ),
1561 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ),
1562 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ),
1563 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
1566 DEBUG (( DEBUG_ERROR
,
1567 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
1568 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
1569 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
1570 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
1571 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
1572 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
1573 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
1574 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
1575 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
1576 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
1577 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
1578 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
1579 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
1580 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
1581 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
1582 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
1583 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
1584 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
1588 if ( 0 != pContext
->PacketsInWindow
) {
1589 DEBUG (( DEBUG_ERROR
,
1590 "ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",
1594 if ( pTftpServer
->RxBytes
> (ssize_t
)( pContext
->BlockSize
+ 2 + 2 )) {
1595 DEBUG (( DEBUG_ERROR
,
1596 "ERROR - Receive data length of %d > %d bytes (maximum block size) for pContext 0x%08x\r\n",
1597 pTftpServer
->RxBytes
- 2 - 2,
1598 pContext
->BlockSize
,
1605 if ( NULL
== pContext
) {
1606 if ( AF_INET
== pTftpServer
->RemoteAddress
.v4
.sin_family
) {
1607 DEBUG (( DEBUG_ERROR
,
1608 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
1609 (UINT8
)pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
,
1610 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 8 ),
1611 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 16 ),
1612 (UINT8
)( pTftpServer
->RemoteAddress
.v4
.sin_addr
.s_addr
>> 24 ),
1613 htons ( pTftpServer
->RemoteAddress
.v4
.sin_port
)));
1616 DEBUG (( DEBUG_ERROR
,
1617 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
1618 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 0 ],
1619 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 1 ],
1620 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 2 ],
1621 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 3 ],
1622 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 4 ],
1623 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 5 ],
1624 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 6 ],
1625 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 7 ],
1626 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 8 ],
1627 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 9 ],
1628 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 10 ],
1629 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 11 ],
1630 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 12 ],
1631 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 13 ],
1632 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 14 ],
1633 pTftpServer
->RemoteAddress
.v6
.sin6_addr
.__u6_addr
.__u6_addr8
[ 15 ],
1634 htons ( pTftpServer
->RemoteAddress
.v6
.sin6_port
)));
1641 // Determine if the context should be closed
1643 if ( bCloseContext
) {
1644 ContextRemove ( pTftpServer
, pContext
);
1652 Process the read request
1654 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
1655 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
1656 @param [in] SocketFd Socket file descriptor
1658 @retval TRUE if the context should be closed
1663 IN TSDT_TFTP_SERVER
* pTftpServer
,
1664 IN TSDT_CONNECTION_CONTEXT
* pContext
,
1668 BOOLEAN bCloseContext
;
1669 struct stat FileStatus
;
1681 // Log the receive time
1684 if ( PcdGetBool ( Tftp_Bandwidth
)) {
1685 TimeStart
= GetPerformanceCounter ( );
1689 // Close the context if necessary
1691 bCloseContext
= FALSE
;
1692 if ( NULL
!= pContext
) {
1693 ContextRemove ( pTftpServer
, pContext
);
1697 // Use break instead of goto
1701 // Create the connection context
1703 pContext
= ContextAdd ( pTftpServer
, SocketFd
);
1704 if ( NULL
== pContext
) {
1709 // Set the start time
1711 if ( PcdGetBool ( Tftp_Bandwidth
)) {
1712 pContext
->TimeStart
= TimeStart
;
1718 pBuffer
= &pTftpServer
->RxBuffer
[ 0 ];
1719 pEnd
= &pBuffer
[ pTftpServer
->RxBytes
];
1720 pFileName
= &pBuffer
[ 2 ];
1722 while (( pEnd
> pMode
) && ( 0 != *pMode
)) {
1725 if ( pEnd
<= pMode
) {
1729 DEBUG (( DEBUG_ERROR
| DEBUG_RX
,
1730 "ERROR - File mode not found\r\n" ));
1732 // Tell the client of the error
1734 SendError ( pContext
,
1736 (UINT8
*)"File open mode not found" );
1740 DEBUG (( DEBUG_TFTP_REQUEST
,
1741 "TFTP - FileName: %a\r\n",
1745 // Locate the options
1748 while (( pEnd
> pOption
) && ( 0 != *pOption
)) {
1751 if ( pEnd
<= pOption
) {
1753 // End of mode not found
1755 DEBUG (( DEBUG_ERROR
| DEBUG_RX
,
1756 "ERROR - File mode not valid\r\n" ));
1758 // Tell the client of the error
1760 SendError ( pContext
,
1762 (UINT8
*)"File open mode not valid" );
1766 DEBUG (( DEBUG_TFTP_REQUEST
,
1767 "TFTP - Mode: %a\r\n",
1771 // Verify the mode is supported
1774 if ( 0 == strcasecmp ((char *)pMode
, "octet" )) {
1776 // Read the file as binary input
1782 // Determine the file length
1784 pContext
->File
= fopen ( pFileName
, pReadMode
);
1785 if (( NULL
== pContext
->File
)
1786 || ( -1 == stat ( pFileName
, &FileStatus
))) {
1790 DEBUG (( DEBUG_ERROR
| DEBUG_TFTP_REQUEST
,
1791 ( NULL
== pContext
->File
)
1792 ? "ERROR - File not found!\r\n"
1793 : "ERROR - Unable to determine file %a size!\r\n",
1797 // Tell the client of the error
1799 SendError ( pContext
,
1800 TFTP_ERROR_NOT_FOUND
,
1801 (UINT8
*)"File not found" );
1804 pContext
->LengthInBytes
= FileStatus
.st_size
;
1805 pContext
->BytesRemaining
= pContext
->LengthInBytes
;
1806 pContext
->BytesToSend
= pContext
->LengthInBytes
;
1809 // Display the file size
1811 DEBUG_CODE_BEGIN ( );
1814 if ( 1024 > pContext
->LengthInBytes
) {
1815 Value
= (UINT32
)pContext
->LengthInBytes
;
1816 DEBUG (( DEBUG_FILE_BUFFER
,
1817 "%a size: %d Bytes\r\n",
1821 else if (( 1024 * 1024 ) > pContext
->LengthInBytes
) {
1822 Value
= (UINT32
)pContext
->LengthInBytes
;
1823 DEBUG (( DEBUG_FILE_BUFFER
,
1824 "%a size: %d.%03d KiBytes (%Ld Bytes)\r\n",
1827 (( Value
% 1024 ) * 1000 ) / 1024,
1828 pContext
->LengthInBytes
));
1830 else if (( 1024 * 1024 * 1024 ) > pContext
->LengthInBytes
) {
1831 Value
= (UINT32
)DivU64x32 ( pContext
->LengthInBytes
, 1024 );
1832 DEBUG (( DEBUG_FILE_BUFFER
,
1833 "%a size: %d.%03d MiBytes (%Ld Bytes)\r\n",
1836 (( Value
% 1024 ) * 1000 ) / 1024,
1837 pContext
->LengthInBytes
));
1840 Value
= (UINT32
)DivU64x32 ( pContext
->LengthInBytes
, 1024 * 1024 );
1841 DEBUG (( DEBUG_FILE_BUFFER
,
1842 "%a size: %d.%03d GiBytes (%Ld Bytes)\r\n",
1845 (( Value
% 1024 ) * 1000 ) / 1024,
1846 pContext
->LengthInBytes
));
1851 // Process the options
1853 if ( pEnd
> pOption
) {
1854 TftpOptions ( pContext
, pOption
, pEnd
);
1858 // Skip the open ACK
1860 pContext
->BlockNumber
= 1;
1864 // Send the first packet (OACK or data block)
1866 bCloseContext
= PacketFill ( pContext
);
1871 // Return the close status
1874 return bCloseContext
;
1879 Create the port for the TFTP server
1881 This routine polls the network layer to create the TFTP port for the
1882 TFTP server. More than one attempt may be necessary since it may take
1883 some time to get the IP address and initialize the upper layers of
1886 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
1887 @param [in] AddressFamily The address family to use for the conection.
1888 @param [in] pIndex Address of the index into the port array
1893 IN TSDT_TFTP_SERVER
* pTftpServer
,
1894 IN sa_family_t AddressFamily
,
1899 struct pollfd
* pTftpPort
;
1902 struct sockaddr_in v4
;
1903 struct sockaddr_in6 v6
;
1904 } TftpServerAddress
;
1906 DEBUG (( DEBUG_SERVER_TIMER
, "Entering TftpServerListen\r\n" ));
1909 // Determine if the socket is already initialized
1911 if ( -1 == *pIndex
) {
1913 // Attempt to create the socket for the TFTP server
1915 pTftpPort
= &pTftpServer
->TftpPort
[ pTftpServer
->Entries
];
1916 pTftpPort
->fd
= socket ( AddressFamily
,
1919 if ( -1 != pTftpPort
->fd
) {
1921 // Initialize the poll structure
1923 pTftpPort
->events
= POLLRDNORM
| POLLHUP
;
1924 pTftpPort
->revents
= 0;
1927 // Set the socket address
1930 ZeroMem ( &TftpServerAddress
, sizeof ( TftpServerAddress
));
1931 TftpServerAddress
.v4
.sin_port
= htons ( TftpPort
);
1932 if ( AF_INET
== AddressFamily
) {
1933 TftpServerAddress
.v4
.sin_len
= sizeof ( TftpServerAddress
.v4
);
1934 TftpServerAddress
.v4
.sin_family
= AF_INET
;
1937 TftpServerAddress
.v6
.sin6_len
= sizeof ( TftpServerAddress
.v6
);
1938 TftpServerAddress
.v6
.sin6_family
= AF_INET6
;
1942 // Bind the socket to the TFTP port
1944 SocketStatus
= bind ( pTftpPort
->fd
,
1945 (struct sockaddr
*) &TftpServerAddress
,
1946 TftpServerAddress
.v6
.sin6_len
);
1947 if ( -1 != SocketStatus
) {
1948 DEBUG (( DEBUG_TFTP_PORT
,
1949 "0x%08x: Socket bound to port %d\r\n",
1954 // Account for this connection
1956 *pIndex
= pTftpServer
->Entries
;
1957 pTftpServer
->Entries
+= 1;
1958 ASSERT ( DIM ( pTftpServer
->TftpPort
) >= pTftpServer
->Entries
);
1962 // Release the socket if necessary
1964 if ( -1 == SocketStatus
) {
1965 close ( pTftpPort
->fd
);
1971 DEBUG (( DEBUG_SERVER_TIMER
, "Exiting TftpServerListen\r\n" ));
1976 Update the window due to the ACK
1978 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
1979 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
1980 @param [in] pPacket Address of a ::TFTP_PACKET structure
1985 IN TSDT_TFTP_SERVER
* pTftpServer
,
1986 IN TSDT_CONNECTION_CONTEXT
* pContext
,
1987 IN TFTP_PACKET
* pPacket
1990 if ( PcdGetBool ( Tftp_HighSpeed
)) {
1997 // Compute the round trip time
1999 if ( pTftpServer
->Time2
> pTftpServer
->Time1
) {
2000 DeltaTime
= pTftpServer
->RxTime
- pPacket
->TxTime
;
2003 DeltaTime
= pPacket
->TxTime
- pTftpServer
->RxTime
;
2007 // Adjust the round trip time
2009 NanoSeconds
= GetTimeInNanoSecond ( DeltaTime
);
2010 DeltaTime
= RShiftU64 ( pContext
->Rtt2x
, ACK_SHIFT
);
2011 pContext
->Rtt2x
+= NanoSeconds
+ NanoSeconds
- DeltaTime
;
2012 if ( pContext
->Rtt2x
> pContext
->MaxTimeout
) {
2013 pContext
->Rtt2x
= pContext
->MaxTimeout
;
2017 // Account for the ACK
2019 if ( pContext
->WindowSize
< MAX_PACKETS
) {
2020 pContext
->AckCount
-= 1;
2021 if ( 0 == pContext
->AckCount
) {
2023 // Increase the window
2025 pContext
->WindowSize
+= 1;
2028 // Set the ACK count
2030 if ( pContext
->WindowSize
< pContext
->Threshold
) {
2031 pContext
->AckCount
= pContext
->WindowSize
* PcdGet32 ( Tftp_AckMultiplier
);
2034 pContext
->AckCount
= PcdGet32 ( Tftp_AckLogBase
) << pContext
->WindowSize
;
2038 // Display the round trip time
2040 DEBUG_CODE_BEGIN ( );
2043 DeltaTime
= RShiftU64 ( pContext
->Rtt2x
, 1 );
2044 if ( 1000 > DeltaTime
) {
2045 DEBUG (( DEBUG_WINDOW
,
2046 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",
2047 pContext
->WindowSize
,
2048 pContext
->Threshold
,
2052 else if (( 1000 * 1000 ) > DeltaTime
) {
2053 Value
= (UINT32
)DeltaTime
;
2054 DEBUG (( DEBUG_WINDOW
,
2055 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",
2056 pContext
->WindowSize
,
2057 pContext
->Threshold
,
2062 else if (( 1000 * 1000 * 1000 ) > DeltaTime
) {
2063 Value
= (UINT32
)DivU64x32 ( DeltaTime
, 1000 );
2064 DEBUG (( DEBUG_WINDOW
,
2065 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",
2066 pContext
->WindowSize
,
2067 pContext
->Threshold
,
2073 Value
= (UINT32
)DivU64x32 ( DeltaTime
, 1000 * 1000 );
2074 DEBUG (( DEBUG_WINDOW
,
2075 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",
2076 pContext
->WindowSize
,
2077 pContext
->Threshold
,
2092 A timeout has occurred, close the window
2094 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure
2099 IN TSDT_CONNECTION_CONTEXT
* pContext
2102 if ( PcdGetBool ( Tftp_HighSpeed
)) {
2103 TFTP_PACKET
* pPacket
;
2108 // Set the threshold at half the previous window size
2110 pContext
->Threshold
= ( pContext
->WindowSize
+ 1 ) >> 1;
2113 // Close the transmit window
2115 pContext
->WindowSize
= 1;
2116 pContext
->PacketsInWindow
= 0;
2119 // Double the round trip time
2121 pContext
->Rtt2x
= LShiftU64 ( pContext
->Rtt2x
, 1 );
2122 if ( pContext
->Rtt2x
> pContext
->MaxTimeout
) {
2123 pContext
->Rtt2x
= pContext
->MaxTimeout
;
2127 // Set the ACK count
2129 if ( pContext
->WindowSize
< pContext
->Threshold
) {
2130 pContext
->AckCount
= pContext
->WindowSize
* PcdGet32 ( Tftp_AckMultiplier
);
2133 pContext
->AckCount
= PcdGet32 ( Tftp_AckLogBase
) << pContext
->WindowSize
;
2137 // Display the round trip time
2139 DEBUG_CODE_BEGIN ( );
2143 DeltaTime
= RShiftU64 ( pContext
->Rtt2x
, 1 );
2144 if ( 1000 > DeltaTime
) {
2145 DEBUG (( DEBUG_WINDOW
,
2146 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",
2147 pContext
->WindowSize
,
2148 pContext
->Threshold
,
2152 else if (( 1000 * 1000 ) > DeltaTime
) {
2153 Value
= (UINT32
)DeltaTime
;
2154 DEBUG (( DEBUG_WINDOW
,
2155 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",
2156 pContext
->WindowSize
,
2157 pContext
->Threshold
,
2162 else if (( 1000 * 1000 * 1000 ) > DeltaTime
) {
2163 Value
= (UINT32
)DivU64x32 ( DeltaTime
, 1000 );
2164 DEBUG (( DEBUG_WINDOW
,
2165 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",
2166 pContext
->WindowSize
,
2167 pContext
->Threshold
,
2173 Value
= (UINT32
)DivU64x32 ( DeltaTime
, 1000 * 1000 );
2174 DEBUG (( DEBUG_WINDOW
,
2175 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",
2176 pContext
->WindowSize
,
2177 pContext
->Threshold
,
2185 // Retransmit the first packet in the window
2187 pPacket
= pContext
->pTxHead
;
2188 if ( NULL
!= pPacket
) {
2189 PacketTx ( pContext
, pPacket
);
2198 Entry point for the TFTP server application.
2200 @param [in] Argc The number of arguments
2201 @param [in] Argv The argument value array
2203 @retval 0 The application exited normally.
2204 @retval Other An error occurred.
2213 TSDT_TFTP_SERVER
* pTftpServer
;
2218 // Get the performance counter characteristics
2220 pTftpServer
= &mTftpServer
;
2221 if ( PcdGetBool ( Tftp_HighSpeed
)
2222 || PcdGetBool ( Tftp_Bandwidth
)) {
2223 pTftpServer
->ClockFrequency
= GetPerformanceCounterProperties ( &pTftpServer
->Time1
,
2224 &pTftpServer
->Time2
);
2228 // Create a timer event to start TFTP port
2230 Status
= gBS
->CreateEvent ( EVT_TIMER
,
2234 &pTftpServer
->TimerEvent
);
2235 if ( !EFI_ERROR ( Status
)) {
2237 // Compute the poll interval
2239 TriggerTime
= TFTP_PORT_POLL_DELAY
* ( 1000 * 10 );
2240 Status
= gBS
->SetTimer ( pTftpServer
->TimerEvent
,
2243 if ( !EFI_ERROR ( Status
)) {
2244 DEBUG (( DEBUG_TFTP_PORT
, "TFTP port timer started\r\n" ));
2247 // Run the TFTP server forever
2249 pTftpServer
->Udpv4Index
= -1;
2250 pTftpServer
->Udpv6Index
= -1;
2253 // Poll the network layer to create the TFTP port
2254 // for the tftp server. More than one attempt may
2255 // be necessary since it may take some time to get
2256 // the IP address and initialize the upper layers
2257 // of the network stack.
2259 if ( DIM ( pTftpServer
->TftpPort
) != pTftpServer
->Entries
) {
2262 // Wait a while before polling for a connection
2264 if ( EFI_SUCCESS
!= gBS
->CheckEvent ( pTftpServer
->TimerEvent
)) {
2265 if ( 0 == pTftpServer
->Entries
) {
2268 gBS
->WaitForEvent ( 1, &pTftpServer
->TimerEvent
, &Index
);
2272 // Poll for a network connection
2274 TftpServerSocket ( pTftpServer
,
2276 &pTftpServer
->Udpv4Index
);
2277 TftpServerSocket ( pTftpServer
,
2279 &pTftpServer
->Udpv6Index
);
2280 } while ( 0 == pTftpServer
->Entries
);
2284 // Poll the socket for activity
2287 SocketPoll ( pTftpServer
);
2290 // Normal TFTP lets the client request the retransmit by
2291 // sending another ACK for the previous packet
2293 if ( PcdGetBool ( Tftp_HighSpeed
)) {
2296 TSDT_CONNECTION_CONTEXT
* pContext
;
2297 TFTP_PACKET
* pPacket
;
2300 // High speed TFTP uses an agressive retransmit to
2301 // get the TFTP client moving again when the ACK or
2302 // previous data packet was lost.
2304 // Get the current time
2306 CurrentTime
= GetPerformanceCounter ( );
2309 // Walk the list of contexts
2311 pContext
= pTftpServer
->pContextList
;
2312 while ( NULL
!= pContext
)
2315 // Check for a transmit timeout
2317 pPacket
= pContext
->pTxHead
;
2318 if ( NULL
!= pPacket
) {
2320 // Compute the elapsed time
2322 if ( pTftpServer
->Time2
> pTftpServer
->Time1
) {
2323 ElapsedTime
= CurrentTime
- pPacket
->TxTime
;
2326 ElapsedTime
= pPacket
->TxTime
- CurrentTime
;
2328 ElapsedTime
= GetTimeInNanoSecond ( ElapsedTime
);
2331 // Determine if a retransmission is necessary
2333 if ( ElapsedTime
>= pContext
->Rtt2x
) {
2334 DEBUG (( DEBUG_WINDOW
,
2335 "0x%08x: Context TX timeout for packet 0x%08x, Window: %d\r\n",
2338 pContext
->WindowSize
));
2339 WindowTimeout ( pContext
);
2344 // Set the next context
2346 pContext
= pContext
->pNext
;
2349 } while ( DIM ( pTftpServer
->TftpPort
) == pTftpServer
->Entries
);
2350 } while ( !mbTftpServerExit
);
2353 // Done with the timer event
2355 gBS
->SetTimer ( pTftpServer
->TimerEvent
,
2359 gBS
->CloseEvent ( pTftpServer
->TimerEvent
);
2363 // Return the final status
2365 DBG_EXIT_STATUS ( Status
);