123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- /** @file
- Routines to process Wrq (upload).
- Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include "Mtftp4Impl.h"
- /**
- Build then send a MTFTP data packet for the MTFTP upload session.
- @param Instance The MTFTP upload session.
- @param BlockNum The block number to send.
- @retval EFI_OUT_OF_RESOURCES Failed to build the packet.
- @retval EFI_ABORTED The consumer of this child directs to abort the
- transmission by return an error through PacketNeeded.
- @retval EFI_SUCCESS The data is sent.
- **/
- EFI_STATUS
- Mtftp4WrqSendBlock (
- IN OUT MTFTP4_PROTOCOL *Instance,
- IN UINT16 BlockNum
- )
- {
- EFI_MTFTP4_PACKET *Packet;
- EFI_MTFTP4_TOKEN *Token;
- NET_BUF *UdpPacket;
- EFI_STATUS Status;
- UINT16 DataLen;
- UINT8 *DataBuf;
- UINT64 Start;
- //
- // Allocate a buffer to hold the user data
- //
- UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);
- if (UdpPacket == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Packet = (EFI_MTFTP4_PACKET *)NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);
- ASSERT (Packet != NULL);
- Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);
- Packet->Data.Block = HTONS (BlockNum);
- //
- // Read the block from either the buffer or PacketNeeded callback
- //
- Token = Instance->Token;
- DataLen = Instance->BlkSize;
- if (Token->Buffer != NULL) {
- Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);
- if (Token->BufferSize < Start + Instance->BlkSize) {
- DataLen = (UINT16)(Token->BufferSize - Start);
- Instance->LastBlock = BlockNum;
- Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
- }
- if (DataLen > 0) {
- NetbufAllocSpace (UdpPacket, DataLen, FALSE);
- CopyMem (Packet->Data.Data, (UINT8 *)Token->Buffer + Start, DataLen);
- }
- } else {
- //
- // Get data from PacketNeeded
- //
- DataBuf = NULL;
- Status = Token->PacketNeeded (
- &Instance->Mtftp4,
- Token,
- &DataLen,
- (VOID **)&DataBuf
- );
- if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {
- if (DataBuf != NULL) {
- FreePool (DataBuf);
- }
- if (UdpPacket != NULL) {
- NetbufFree (UdpPacket);
- }
- Mtftp4SendError (
- Instance,
- EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
- (UINT8 *)"User aborted the transfer"
- );
- return EFI_ABORTED;
- }
- if (DataLen < Instance->BlkSize) {
- Instance->LastBlock = BlockNum;
- Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
- }
- if (DataLen > 0) {
- NetbufAllocSpace (UdpPacket, DataLen, FALSE);
- CopyMem (Packet->Data.Data, DataBuf, DataLen);
- FreePool (DataBuf);
- }
- }
- return Mtftp4SendPacket (Instance, UdpPacket);
- }
- /**
- Function to handle received ACK packet.
- If the ACK number matches the expected block number, and there are more
- data pending, send the next block. Otherwise tell the caller that we are done.
- @param Instance The MTFTP upload session
- @param Packet The MTFTP packet received
- @param Len The packet length
- @param Completed Return whether the upload has finished.
- @retval EFI_SUCCESS The ACK is successfully processed.
- @retval EFI_TFTP_ERROR The block number loops back.
- @retval Others Failed to transmit the next data packet.
- **/
- EFI_STATUS
- Mtftp4WrqHandleAck (
- IN MTFTP4_PROTOCOL *Instance,
- IN EFI_MTFTP4_PACKET *Packet,
- IN UINT32 Len,
- OUT BOOLEAN *Completed
- )
- {
- UINT16 AckNum;
- INTN Expected;
- UINT64 BlockCounter;
- *Completed = FALSE;
- AckNum = NTOHS (Packet->Ack.Block[0]);
- Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
- ASSERT (Expected >= 0);
- //
- // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput
- // restart receive.
- //
- if (Expected != AckNum) {
- return EFI_SUCCESS;
- }
- //
- // Remove the acked block number, if this is the last block number,
- // tell the Mtftp4WrqInput to finish the transfer. This is the last
- // block number if the block range are empty.
- //
- Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum, *Completed, &BlockCounter);
- Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
- if (Expected < 0) {
- //
- // The block range is empty. It may either because the last
- // block has been ACKed, or the sequence number just looped back,
- // that is, there is more than 0xffff blocks.
- //
- if (Instance->LastBlock == AckNum) {
- ASSERT (Instance->LastBlock >= 1);
- *Completed = TRUE;
- return EFI_SUCCESS;
- } else {
- Mtftp4SendError (
- Instance,
- EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
- (UINT8 *)"Block number rolls back, not supported, try blksize option"
- );
- return EFI_TFTP_ERROR;
- }
- }
- return Mtftp4WrqSendBlock (Instance, (UINT16)Expected);
- }
- /**
- Check whether the received OACK is valid.
- The OACK is valid only if:
- 1. It only include options requested by us
- 2. It can only include a smaller block size
- 3. It can't change the proposed time out value.
- 4. Other requirements of the individal MTFTP options as required.
- @param Reply The options included in the OACK
- @param Request The options we requested
- @retval TRUE The options included in OACK is valid.
- @retval FALSE The options included in OACK is invalid.
- **/
- BOOLEAN
- Mtftp4WrqOackValid (
- IN MTFTP4_OPTION *Reply,
- IN MTFTP4_OPTION *Request
- )
- {
- //
- // It is invalid for server to return options we don't request
- //
- if ((Reply->Exist & ~Request->Exist) != 0) {
- return FALSE;
- }
- //
- // Server can only specify a smaller block size to be used and
- // return the timeout matches that requested.
- //
- if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) ||
- (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout)))
- {
- return FALSE;
- }
- return TRUE;
- }
- /**
- Function to handle the MTFTP OACK packet.
- It parses the packet's options, and update the internal states of the session.
- @param Instance The MTFTP session
- @param Packet The received OACK packet
- @param Len The length of the packet
- @param Completed Whether the transmission has completed. NOT used by
- this function.
- @retval EFI_SUCCESS The OACK process is OK
- @retval EFI_TFTP_ERROR Some error occurred, and the session reset.
- **/
- EFI_STATUS
- Mtftp4WrqHandleOack (
- IN OUT MTFTP4_PROTOCOL *Instance,
- IN EFI_MTFTP4_PACKET *Packet,
- IN UINT32 Len,
- OUT BOOLEAN *Completed
- )
- {
- MTFTP4_OPTION Reply;
- EFI_MTFTP4_PACKET Bogus;
- EFI_STATUS Status;
- INTN Expected;
- *Completed = FALSE;
- //
- // Ignore the OACK if already started the upload
- //
- Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
- if (Expected != 0) {
- return EFI_SUCCESS;
- }
- //
- // Parse and validate the options from server
- //
- ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
- Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);
- if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {
- //
- // Don't send a MTFTP error packet when out of resource, it can
- // only make it worse.
- //
- if (Status != EFI_OUT_OF_RESOURCES) {
- Mtftp4SendError (
- Instance,
- EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
- (UINT8 *)"Malformatted OACK packet"
- );
- }
- return EFI_TFTP_ERROR;
- }
- if (Reply.BlkSize != 0) {
- Instance->BlkSize = Reply.BlkSize;
- }
- if (Reply.Timeout != 0) {
- Instance->Timeout = Reply.Timeout;
- }
- //
- // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,
- // which will start the transmission of the first data block.
- //
- Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
- Bogus.Ack.Block[0] = 0;
- Status = Mtftp4WrqHandleAck (
- Instance,
- &Bogus,
- sizeof (EFI_MTFTP4_ACK_HEADER),
- Completed
- );
- return Status;
- }
- /**
- The input process routine for MTFTP upload.
- @param UdpPacket The received MTFTP packet.
- @param EndPoint The local/remote access point
- @param IoStatus The result of the packet receiving
- @param Context Opaque parameter for the callback, which is the
- MTFTP session.
- **/
- VOID
- EFIAPI
- Mtftp4WrqInput (
- IN NET_BUF *UdpPacket,
- IN UDP_END_POINT *EndPoint,
- IN EFI_STATUS IoStatus,
- IN VOID *Context
- )
- {
- MTFTP4_PROTOCOL *Instance;
- EFI_MTFTP4_PACKET *Packet;
- BOOLEAN Completed;
- EFI_STATUS Status;
- UINT32 Len;
- UINT16 Opcode;
- Instance = (MTFTP4_PROTOCOL *)Context;
- NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
- Completed = FALSE;
- Packet = NULL;
- Status = EFI_SUCCESS;
- if (EFI_ERROR (IoStatus)) {
- Status = IoStatus;
- goto ON_EXIT;
- }
- ASSERT (UdpPacket != NULL);
- if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
- goto ON_EXIT;
- }
- //
- // Client send initial request to server's listening port. Server
- // will select a UDP port to communicate with the client.
- //
- if (EndPoint->RemotePort != Instance->ConnectedPort) {
- if (Instance->ConnectedPort != 0) {
- goto ON_EXIT;
- } else {
- Instance->ConnectedPort = EndPoint->RemotePort;
- }
- }
- //
- // Copy the MTFTP packet to a continuous buffer if it isn't already so.
- //
- Len = UdpPacket->TotalSize;
- if (UdpPacket->BlockOpNum > 1) {
- Packet = AllocatePool (Len);
- if (Packet == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto ON_EXIT;
- }
- NetbufCopy (UdpPacket, 0, Len, (UINT8 *)Packet);
- } else {
- Packet = (EFI_MTFTP4_PACKET *)NetbufGetByte (UdpPacket, 0, NULL);
- ASSERT (Packet != NULL);
- }
- Opcode = NTOHS (Packet->OpCode);
- //
- // Call the user's CheckPacket if provided. Abort the transmission
- // if CheckPacket returns an EFI_ERROR code.
- //
- if ((Instance->Token->CheckPacket != NULL) &&
- ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR)))
- {
- Status = Instance->Token->CheckPacket (
- &Instance->Mtftp4,
- Instance->Token,
- (UINT16)Len,
- Packet
- );
- if (EFI_ERROR (Status)) {
- //
- // Send an error message to the server to inform it
- //
- if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
- Mtftp4SendError (
- Instance,
- EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
- (UINT8 *)"User aborted the transfer"
- );
- }
- Status = EFI_ABORTED;
- goto ON_EXIT;
- }
- }
- switch (Opcode) {
- case EFI_MTFTP4_OPCODE_ACK:
- if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {
- goto ON_EXIT;
- }
- Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);
- break;
- case EFI_MTFTP4_OPCODE_OACK:
- if (Len <= MTFTP4_OPCODE_LEN) {
- goto ON_EXIT;
- }
- Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);
- break;
- case EFI_MTFTP4_OPCODE_ERROR:
- Status = EFI_TFTP_ERROR;
- break;
- default:
- break;
- }
- ON_EXIT:
- //
- // Free the resources, then if !EFI_ERROR (Status) and not completed,
- // restart the receive, otherwise end the session.
- //
- if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
- FreePool (Packet);
- }
- if (UdpPacket != NULL) {
- NetbufFree (UdpPacket);
- }
- if (!EFI_ERROR (Status) && !Completed) {
- Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
- }
- //
- // Status may have been updated by UdpIoRecvDatagram
- //
- if (EFI_ERROR (Status) || Completed) {
- Mtftp4CleanOperation (Instance, Status);
- }
- }
- /**
- Start the MTFTP session for upload.
- It will first init some states, then send the WRQ request packet,
- and start receiving the packet.
- @param Instance The MTFTP session
- @param Operation Redundant parameter, which is always
- EFI_MTFTP4_OPCODE_WRQ here.
- @retval EFI_SUCCESS The upload process has been started.
- @retval Others Failed to start the upload.
- **/
- EFI_STATUS
- Mtftp4WrqStart (
- IN MTFTP4_PROTOCOL *Instance,
- IN UINT16 Operation
- )
- {
- EFI_STATUS Status;
- //
- // The valid block number range are [0, 0xffff]. For example:
- // the client sends an WRQ request to the server, the server
- // ACK with an ACK0 to let client start transfer the first
- // packet.
- //
- Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = Mtftp4SendRequest (Instance);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
- }
|