+++ /dev/null
-/** @file\r
- Support routines for Mtftp.\r
-\r
-Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
-SPDX-License-Identifier: BSD-2-Clause-Patent\r
-\r
-**/\r
-\r
-#include "Mtftp4Impl.h"\r
-\r
-\r
-/**\r
- Allocate a MTFTP4 block range, then init it to the range of [Start, End]\r
-\r
- @param Start The start block number\r
- @param End The last block number in the range\r
-\r
- @return Pointer to the created block range, NULL if failed to allocate memory.\r
-\r
-**/\r
-MTFTP4_BLOCK_RANGE *\r
-Mtftp4AllocateRange (\r
- IN UINT16 Start,\r
- IN UINT16 End\r
- )\r
-{\r
- MTFTP4_BLOCK_RANGE *Range;\r
-\r
- Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE));\r
-\r
- if (Range == NULL) {\r
- return NULL;\r
- }\r
-\r
- InitializeListHead (&Range->Link);\r
- Range->Start = Start;\r
- Range->End = End;\r
- Range->Bound = End;\r
-\r
- return Range;\r
-}\r
-\r
-\r
-/**\r
- Initialize the block range for either RRQ or WRQ.\r
-\r
- RRQ and WRQ have different requirements for Start and End.\r
- For example, during start up, WRQ initializes its whole valid block range\r
- to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us\r
- to start the upload. When the client received ACK0, it will remove 0 from the\r
- range, get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
- without option negotiation, the server will directly send us the BLOCK1 in\r
- response to the client's RRQ. When received BLOCK1, the client will remove\r
- it from the block range and send an ACK. It also works if there is option\r
- negotiation.\r
-\r
- @param Head The block range head to initialize\r
- @param Start The Start block number.\r
- @param End The last block number.\r
-\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range\r
- @retval EFI_SUCCESS The initial block range is created.\r
-\r
-**/\r
-EFI_STATUS\r
-Mtftp4InitBlockRange (\r
- IN LIST_ENTRY *Head,\r
- IN UINT16 Start,\r
- IN UINT16 End\r
- )\r
-{\r
- MTFTP4_BLOCK_RANGE *Range;\r
-\r
- Range = Mtftp4AllocateRange (Start, End);\r
-\r
- if (Range == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- InsertTailList (Head, &Range->Link);\r
- return EFI_SUCCESS;\r
-}\r
-\r
-\r
-/**\r
- Get the first valid block number on the range list.\r
-\r
- @param Head The block range head\r
-\r
- @return The first valid block number, -1 if the block range is empty.\r
-\r
-**/\r
-INTN\r
-Mtftp4GetNextBlockNum (\r
- IN LIST_ENTRY *Head\r
- )\r
-{\r
- MTFTP4_BLOCK_RANGE *Range;\r
-\r
- if (IsListEmpty (Head)) {\r
- return -1;\r
- }\r
-\r
- Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);\r
- return Range->Start;\r
-}\r
-\r
-\r
-/**\r
- Set the last block number of the block range list.\r
-\r
- It will remove all the blocks after the Last. MTFTP initialize the block range\r
- to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the\r
- last block number, it will call this function to set the last block number.\r
-\r
- @param Head The block range list\r
- @param Last The last block number\r
-\r
-**/\r
-VOID\r
-Mtftp4SetLastBlockNum (\r
- IN LIST_ENTRY *Head,\r
- IN UINT16 Last\r
- )\r
-{\r
- MTFTP4_BLOCK_RANGE *Range;\r
-\r
- //\r
- // Iterate from the tail to head to remove the block number\r
- // after the last.\r
- //\r
- while (!IsListEmpty (Head)) {\r
- Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);\r
-\r
- if (Range->Start > Last) {\r
- RemoveEntryList (&Range->Link);\r
- FreePool (Range);\r
- continue;\r
- }\r
-\r
- if (Range->End > Last) {\r
- Range->End = Last;\r
- }\r
-\r
- return ;\r
- }\r
-}\r
-\r
-\r
-/**\r
- Remove the block number from the block range list.\r
-\r
- @param Head The block range list to remove from\r
- @param Num The block number to remove\r
- @param Completed Whether Num is the last block number.\r
- @param BlockCounter The continuous block counter instead of the value after roll-over.\r
-\r
- @retval EFI_NOT_FOUND The block number isn't in the block range list\r
- @retval EFI_SUCCESS The block number has been removed from the list\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resource\r
-\r
-**/\r
-EFI_STATUS\r
-Mtftp4RemoveBlockNum (\r
- IN LIST_ENTRY *Head,\r
- IN UINT16 Num,\r
- IN BOOLEAN Completed,\r
- OUT UINT64 *BlockCounter\r
- )\r
-{\r
- MTFTP4_BLOCK_RANGE *Range;\r
- MTFTP4_BLOCK_RANGE *NewRange;\r
- LIST_ENTRY *Entry;\r
-\r
- NET_LIST_FOR_EACH (Entry, Head) {\r
-\r
- //\r
- // Each block represents a hole [Start, End] in the file,\r
- // skip to the first range with End >= Num\r
- //\r
- Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);\r
-\r
- if (Range->End < Num) {\r
- continue;\r
- }\r
-\r
- //\r
- // There are three different cases for Start\r
- // 1. (Start > Num) && (End >= Num):\r
- // because all the holes before this one has the condition of\r
- // End < Num, so this block number has been removed.\r
- //\r
- // 2. (Start == Num) && (End >= Num):\r
- // Need to increase the Start by one, and if End == Num, this\r
- // hole has been removed completely, remove it.\r
- //\r
- // 3. (Start < Num) && (End >= Num):\r
- // if End == Num, only need to decrease the End by one because\r
- // we have (Start < Num) && (Num == End), so (Start <= End - 1).\r
- // if (End > Num), the hold is splited into two holes, with\r
- // [Start, Num - 1] and [Num + 1, End].\r
- //\r
- if (Range->Start > Num) {\r
- return EFI_NOT_FOUND;\r
-\r
- } else if (Range->Start == Num) {\r
- Range->Start++;\r
-\r
- //\r
- // Note that: RFC 1350 does not mention block counter roll-over,\r
- // but several TFTP hosts implement the roll-over be able to accept\r
- // transfers of unlimited size. There is no consensus, however, whether\r
- // the counter should wrap around to zero or to one. Many implementations\r
- // wrap to zero, because this is the simplest to implement. Here we choose\r
- // this solution.\r
- //\r
- *BlockCounter = Num;\r
-\r
- if (Range->Round > 0) {\r
- *BlockCounter += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;\r
- }\r
-\r
- if (Range->Start > Range->Bound) {\r
- Range->Start = 0;\r
- Range->Round ++;\r
- }\r
-\r
- if ((Range->Start > Range->End) || Completed) {\r
- RemoveEntryList (&Range->Link);\r
- FreePool (Range);\r
- }\r
-\r
- return EFI_SUCCESS;\r
-\r
- } else {\r
- if (Range->End == Num) {\r
- Range->End--;\r
- } else {\r
- NewRange = Mtftp4AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);\r
-\r
- if (NewRange == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- Range->End = Num - 1;\r
- NetListInsertAfter (&Range->Link, &NewRange->Link);\r
- }\r
-\r
- return EFI_SUCCESS;\r
- }\r
- }\r
-\r
- return EFI_NOT_FOUND;\r
-}\r
-\r
-\r
-/**\r
- Build then transmit the request packet for the MTFTP session.\r
-\r
- @param Instance The Mtftp session\r
-\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request\r
- @retval EFI_SUCCESS The request is built and sent\r
- @retval Others Failed to transmit the packet.\r
-\r
-**/\r
-EFI_STATUS\r
-Mtftp4SendRequest (\r
- IN MTFTP4_PROTOCOL *Instance\r
- )\r
-{\r
- EFI_MTFTP4_PACKET *Packet;\r
- EFI_MTFTP4_OPTION *Options;\r
- EFI_MTFTP4_TOKEN *Token;\r
- RETURN_STATUS Status;\r
- NET_BUF *Nbuf;\r
- UINT8 *Mode;\r
- UINT8 *Cur;\r
- UINTN Index;\r
- UINT32 BufferLength;\r
- UINTN FileNameLength;\r
- UINTN ModeLength;\r
- UINTN OptionStrLength;\r
- UINTN ValueStrLength;\r
-\r
- Token = Instance->Token;\r
- Options = Token->OptionList;\r
- Mode = Instance->Token->ModeStr;\r
-\r
- if (Mode == NULL) {\r
- Mode = (UINT8 *) "octet";\r
- }\r
-\r
- //\r
- // Compute the packet length\r
- //\r
- FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);\r
- ModeLength = AsciiStrLen ((CHAR8 *) Mode);\r
- BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4;\r
-\r
- for (Index = 0; Index < Token->OptionCount; Index++) {\r
- OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
- ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
- BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;\r
- }\r
- //\r
- // Allocate a packet then copy the data over\r
- //\r
- if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);\r
- ASSERT (Packet != NULL);\r
-\r
- Packet->OpCode = HTONS (Instance->Operation);\r
- BufferLength -= sizeof (Packet->OpCode);\r
-\r
- Cur = Packet->Rrq.Filename;\r
- Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);\r
- ASSERT_EFI_ERROR (Status);\r
- BufferLength -= (UINT32) (FileNameLength + 1);\r
- Cur += FileNameLength + 1;\r
- Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);\r
- ASSERT_EFI_ERROR (Status);\r
- BufferLength -= (UINT32) (ModeLength + 1);\r
- Cur += ModeLength + 1;\r
-\r
- for (Index = 0; Index < Token->OptionCount; ++Index) {\r
- OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
- ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
-\r
- Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);\r
- ASSERT_EFI_ERROR (Status);\r
- BufferLength -= (UINT32) (OptionStrLength + 1);\r
- Cur += OptionStrLength + 1;\r
-\r
- Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);\r
- ASSERT_EFI_ERROR (Status);\r
- BufferLength -= (UINT32) (ValueStrLength + 1);\r
- Cur += ValueStrLength + 1;\r
-\r
- }\r
-\r
- return Mtftp4SendPacket (Instance, Nbuf);\r
-}\r
-\r
-\r
-/**\r
- Build then send an error message.\r
-\r
- @param Instance The MTFTP session\r
- @param ErrCode The error code\r
- @param ErrInfo The error message\r
-\r
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet\r
- @retval EFI_SUCCESS The error packet is transmitted.\r
- @retval Others Failed to transmit the packet.\r
-\r
-**/\r
-EFI_STATUS\r
-Mtftp4SendError (\r
- IN MTFTP4_PROTOCOL *Instance,\r
- IN UINT16 ErrCode,\r
- IN UINT8 *ErrInfo\r
- )\r
-{\r
- NET_BUF *Packet;\r
- EFI_MTFTP4_PACKET *TftpError;\r
- UINT32 Len;\r
-\r
- Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));\r
- Packet = NetbufAlloc (Len);\r
- if (Packet == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);\r
- ASSERT (TftpError != NULL);\r
-\r
- TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);\r
- TftpError->Error.ErrorCode = HTONS (ErrCode);\r
-\r
- AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, Len, (CHAR8 *) ErrInfo);\r
-\r
- return Mtftp4SendPacket (Instance, Packet);\r
-}\r
-\r
-\r
-/**\r
- The callback function called when the packet is transmitted.\r
-\r
- It simply frees the packet.\r
-\r
- @param Packet The transmitted (or failed to) packet\r
- @param EndPoint The local and remote UDP access point\r
- @param IoStatus The result of the transmission\r
- @param Context Opaque parameter to the callback\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-Mtftp4OnPacketSent (\r
- IN NET_BUF *Packet,\r
- IN UDP_END_POINT *EndPoint,\r
- IN EFI_STATUS IoStatus,\r
- IN VOID *Context\r
- )\r
-{\r
- NetbufFree (Packet);\r
-}\r
-\r
-\r
-/**\r
- Set the timeout for the instance. User a longer time for passive instances.\r
-\r
- @param Instance The Mtftp session to set time out\r
-\r
-**/\r
-VOID\r
-Mtftp4SetTimeout (\r
- IN OUT MTFTP4_PROTOCOL *Instance\r
- )\r
-{\r
- if (Instance->Master) {\r
- Instance->PacketToLive = Instance->Timeout;\r
- } else {\r
- Instance->PacketToLive = Instance->Timeout * 2;\r
- }\r
-}\r
-\r
-\r
-/**\r
- Send the packet for the instance.\r
-\r
- It will first save a reference to the packet for later retransmission.\r
- Then determine the destination port, listen port for requests, and connected\r
- port for others. At last, send the packet out.\r
-\r
- @param Instance The Mtftp instance\r
- @param Packet The packet to send\r
-\r
- @retval EFI_SUCCESS The packet is sent out\r
- @retval Others Failed to transmit the packet.\r
-\r
-**/\r
-EFI_STATUS\r
-Mtftp4SendPacket (\r
- IN OUT MTFTP4_PROTOCOL *Instance,\r
- IN OUT NET_BUF *Packet\r
- )\r
-{\r
- UDP_END_POINT UdpPoint;\r
- EFI_STATUS Status;\r
- UINT16 OpCode;\r
- UINT8 *Buffer;\r
-\r
- //\r
- // Save the packet for retransmission\r
- //\r
- if (Instance->LastPacket != NULL) {\r
- NetbufFree (Instance->LastPacket);\r
- }\r
-\r
- Instance->LastPacket = Packet;\r
-\r
- Instance->CurRetry = 0;\r
- Mtftp4SetTimeout (Instance);\r
-\r
- ZeroMem (&UdpPoint, sizeof (UdpPoint));\r
- UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;\r
-\r
- //\r
- // Send the requests to the listening port, other packets\r
- // to the connected port\r
- //\r
- Buffer = NetbufGetByte (Packet, 0, NULL);\r
- ASSERT (Buffer != NULL);\r
- OpCode = NTOHS (*(UINT16 *)Buffer);\r
-\r
- if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) ||\r
- (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
- (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {\r
- UdpPoint.RemotePort = Instance->ListeningPort;\r
- } else {\r
- UdpPoint.RemotePort = Instance->ConnectedPort;\r
- }\r
-\r
- NET_GET_REF (Packet);\r
-\r
- Status = UdpIoSendDatagram (\r
- Instance->UnicastPort,\r
- Packet,\r
- &UdpPoint,\r
- NULL,\r
- Mtftp4OnPacketSent,\r
- Instance\r
- );\r
-\r
- if (EFI_ERROR (Status)) {\r
- NET_PUT_REF (Packet);\r
- }\r
-\r
- return Status;\r
-}\r
-\r
-\r
-/**\r
- Retransmit the last packet for the instance.\r
-\r
- @param Instance The Mtftp instance\r
-\r
- @retval EFI_SUCCESS The last packet is retransmitted.\r
- @retval Others Failed to retransmit.\r
-\r
-**/\r
-EFI_STATUS\r
-Mtftp4Retransmit (\r
- IN MTFTP4_PROTOCOL *Instance\r
- )\r
-{\r
- UDP_END_POINT UdpPoint;\r
- EFI_STATUS Status;\r
- UINT16 OpCode;\r
- UINT8 *Buffer;\r
-\r
- ASSERT (Instance->LastPacket != NULL);\r
-\r
- ZeroMem (&UdpPoint, sizeof (UdpPoint));\r
- UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;\r
-\r
- //\r
- // Set the requests to the listening port, other packets to the connected port\r
- //\r
- Buffer = NetbufGetByte (Instance->LastPacket, 0, NULL);\r
- ASSERT (Buffer != NULL);\r
- OpCode = NTOHS (*(UINT16 *) Buffer);\r
-\r
- if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
- (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {\r
- UdpPoint.RemotePort = Instance->ListeningPort;\r
- } else {\r
- UdpPoint.RemotePort = Instance->ConnectedPort;\r
- }\r
-\r
- NET_GET_REF (Instance->LastPacket);\r
-\r
- Status = UdpIoSendDatagram (\r
- Instance->UnicastPort,\r
- Instance->LastPacket,\r
- &UdpPoint,\r
- NULL,\r
- Mtftp4OnPacketSent,\r
- Instance\r
- );\r
-\r
- if (EFI_ERROR (Status)) {\r
- NET_PUT_REF (Instance->LastPacket);\r
- }\r
-\r
- return Status;\r
-}\r
-\r
-\r
-/**\r
- The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.\r
-\r
- @param Event The ticking event\r
- @param Context The Mtftp service instance\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-Mtftp4OnTimerTickNotifyLevel (\r
- IN EFI_EVENT Event,\r
- IN VOID *Context\r
- )\r
-{\r
- MTFTP4_SERVICE *MtftpSb;\r
- LIST_ENTRY *Entry;\r
- LIST_ENTRY *Next;\r
- MTFTP4_PROTOCOL *Instance;\r
-\r
- MtftpSb = (MTFTP4_SERVICE *) Context;\r
-\r
- //\r
- // Iterate through all the children of the Mtftp service instance. Time\r
- // out the current packet transmit.\r
- //\r
- NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {\r
- Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);\r
- if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {\r
- Instance->HasTimeout = FALSE;\r
- } else {\r
- Instance->HasTimeout = TRUE;\r
- }\r
- }\r
-}\r
-\r
-\r
-/**\r
- The timer ticking function for the Mtftp service instance.\r
-\r
- @param Event The ticking event\r
- @param Context The Mtftp service instance\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-Mtftp4OnTimerTick (\r
- IN EFI_EVENT Event,\r
- IN VOID *Context\r
- )\r
-{\r
- MTFTP4_SERVICE *MtftpSb;\r
- LIST_ENTRY *Entry;\r
- LIST_ENTRY *Next;\r
- MTFTP4_PROTOCOL *Instance;\r
- EFI_MTFTP4_TOKEN *Token;\r
-\r
- MtftpSb = (MTFTP4_SERVICE *) Context;\r
-\r
- //\r
- // Iterate through all the children of the Mtftp service instance.\r
- //\r
- NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {\r
- Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);\r
- if (!Instance->HasTimeout) {\r
- continue;\r
- }\r
-\r
- Instance->HasTimeout = FALSE;\r
-\r
- //\r
- // Call the user's time out handler\r
- //\r
- Token = Instance->Token;\r
-\r
- if (Token != NULL && Token->TimeoutCallback != NULL &&\r
- EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {\r
- Mtftp4SendError (\r
- Instance,\r
- EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
- (UINT8 *) "User aborted the transfer in time out"\r
- );\r
-\r
- Mtftp4CleanOperation (Instance, EFI_ABORTED);\r
- continue;\r
- }\r
-\r
- //\r
- // Retransmit the packet if haven't reach the maxmium retry count,\r
- // otherwise exit the transfer.\r
- //\r
- if (++Instance->CurRetry < Instance->MaxRetry) {\r
- Mtftp4Retransmit (Instance);\r
- Mtftp4SetTimeout (Instance);\r
- } else {\r
- Mtftp4CleanOperation (Instance, EFI_TIMEOUT);\r
- continue;\r
- }\r
- }\r
-}\r