Home | History | Annotate | Download | only in Mtftp4Dxe
      1 /** @file
      2   Routines to process Wrq (upload).
      3 
      4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
      5 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<BR>
      9 
     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.
     12 
     13 **/
     14 
     15 #include "Mtftp4Impl.h"
     16 
     17 
     18 
     19 /**
     20   Build then send a MTFTP data packet for the MTFTP upload session.
     21 
     22   @param  Instance              The MTFTP upload session.
     23   @param  BlockNum              The block number to send.
     24 
     25   @retval EFI_OUT_OF_RESOURCES  Failed to build the packet.
     26   @retval EFI_ABORTED           The consumer of this child directs to abort the
     27                                 transmission by return an error through PacketNeeded.
     28   @retval EFI_SUCCESS           The data is sent.
     29 
     30 **/
     31 EFI_STATUS
     32 Mtftp4WrqSendBlock (
     33   IN OUT MTFTP4_PROTOCOL        *Instance,
     34   IN     UINT16                 BlockNum
     35   )
     36 {
     37   EFI_MTFTP4_PACKET         *Packet;
     38   EFI_MTFTP4_TOKEN          *Token;
     39   NET_BUF                   *UdpPacket;
     40   EFI_STATUS                Status;
     41   UINT16                    DataLen;
     42   UINT8                     *DataBuf;
     43   UINT64                    Start;
     44 
     45   //
     46   // Allocate a buffer to hold the user data
     47   //
     48   UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);
     49 
     50   if (UdpPacket == NULL) {
     51     return EFI_OUT_OF_RESOURCES;
     52   }
     53 
     54   Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);
     55   ASSERT (Packet != NULL);
     56 
     57   Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);
     58   Packet->Data.Block  = HTONS (BlockNum);
     59 
     60   //
     61   // Read the block from either the buffer or PacketNeeded callback
     62   //
     63   Token   = Instance->Token;
     64   DataLen = Instance->BlkSize;
     65 
     66   if (Token->Buffer != NULL) {
     67     Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);
     68 
     69     if (Token->BufferSize < Start + Instance->BlkSize) {
     70       DataLen             = (UINT16) (Token->BufferSize - Start);
     71       Instance->LastBlock = BlockNum;
     72       Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
     73     }
     74 
     75     if (DataLen > 0) {
     76       NetbufAllocSpace (UdpPacket, DataLen, FALSE);
     77       CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);
     78     }
     79 
     80   } else {
     81     //
     82     // Get data from PacketNeeded
     83     //
     84     DataBuf = NULL;
     85     Status  = Token->PacketNeeded (
     86                        &Instance->Mtftp4,
     87                        Token,
     88                        &DataLen,
     89                        (VOID **) &DataBuf
     90                        );
     91 
     92     if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {
     93       if (DataBuf != NULL) {
     94         FreePool (DataBuf);
     95       }
     96 
     97       Mtftp4SendError (
     98         Instance,
     99         EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
    100         (UINT8 *) "User aborted the transfer"
    101         );
    102 
    103       return EFI_ABORTED;
    104     }
    105 
    106     if (DataLen < Instance->BlkSize) {
    107       Instance->LastBlock = BlockNum;
    108       Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
    109     }
    110 
    111     if (DataLen > 0) {
    112       NetbufAllocSpace (UdpPacket, DataLen, FALSE);
    113       CopyMem (Packet->Data.Data, DataBuf, DataLen);
    114       FreePool (DataBuf);
    115     }
    116   }
    117 
    118   return Mtftp4SendPacket (Instance, UdpPacket);
    119 }
    120 
    121 
    122 /**
    123   Function to handle received ACK packet.
    124 
    125   If the ACK number matches the expected block number, and there are more
    126   data pending, send the next block. Otherwise tell the caller that we are done.
    127 
    128   @param  Instance              The MTFTP upload session
    129   @param  Packet                The MTFTP packet received
    130   @param  Len                   The packet length
    131   @param  Completed             Return whether the upload has finished.
    132 
    133   @retval EFI_SUCCESS           The ACK is successfully processed.
    134   @retval EFI_TFTP_ERROR        The block number loops back.
    135   @retval Others                Failed to transmit the next data packet.
    136 
    137 **/
    138 EFI_STATUS
    139 Mtftp4WrqHandleAck (
    140   IN     MTFTP4_PROTOCOL       *Instance,
    141   IN     EFI_MTFTP4_PACKET     *Packet,
    142   IN     UINT32                Len,
    143      OUT BOOLEAN               *Completed
    144   )
    145 {
    146   UINT16                    AckNum;
    147   INTN                      Expected;
    148   UINT64                    TotalBlock;
    149 
    150   *Completed  = FALSE;
    151   AckNum      = NTOHS (Packet->Ack.Block[0]);
    152   Expected    = Mtftp4GetNextBlockNum (&Instance->Blocks);
    153 
    154   ASSERT (Expected >= 0);
    155 
    156   //
    157   // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput
    158   // restart receive.
    159   //
    160   if (Expected != AckNum) {
    161     return EFI_SUCCESS;
    162   }
    163 
    164   //
    165   // Remove the acked block number, if this is the last block number,
    166   // tell the Mtftp4WrqInput to finish the transfer. This is the last
    167   // block number if the block range are empty..
    168   //
    169   Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum, *Completed,&TotalBlock);
    170 
    171   Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
    172 
    173   if (Expected < 0) {
    174 
    175     //
    176     // The block range is empty. It may either because the the last
    177     // block has been ACKed, or the sequence number just looped back,
    178     // that is, there is more than 0xffff blocks.
    179     //
    180     if (Instance->LastBlock == AckNum) {
    181       ASSERT (Instance->LastBlock >= 1);
    182       *Completed = TRUE;
    183       return EFI_SUCCESS;
    184 
    185     } else {
    186       Mtftp4SendError (
    187         Instance,
    188         EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
    189         (UINT8 *) "Block number rolls back, not supported, try blksize option"
    190         );
    191 
    192       return EFI_TFTP_ERROR;
    193     }
    194   }
    195 
    196   return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);
    197 }
    198 
    199 
    200 /**
    201   Check whether the received OACK is valid.
    202 
    203   The OACK is valid only if:
    204   1. It only include options requested by us
    205   2. It can only include a smaller block size
    206   3. It can't change the proposed time out value.
    207   4. Other requirements of the individal MTFTP options as required.
    208 
    209   @param  Reply                 The options included in the OACK
    210   @param  Request               The options we requested
    211 
    212   @retval TRUE                  The options included in OACK is valid.
    213   @retval FALSE                 The options included in OACK is invalid.
    214 
    215 **/
    216 BOOLEAN
    217 Mtftp4WrqOackValid (
    218   IN MTFTP4_OPTION              *Reply,
    219   IN MTFTP4_OPTION              *Request
    220   )
    221 {
    222   //
    223   // It is invalid for server to return options we don't request
    224   //
    225   if ((Reply->Exist & ~Request->Exist) != 0) {
    226     return FALSE;
    227   }
    228 
    229   //
    230   // Server can only specify a smaller block size to be used and
    231   // return the timeout matches that requested.
    232   //
    233   if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) ||
    234       (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {
    235     return FALSE;
    236   }
    237 
    238   return TRUE;
    239 }
    240 
    241 
    242 /**
    243   Function to handle the MTFTP OACK packet.
    244 
    245   It parses the packet's options, and update the internal states of the session.
    246 
    247   @param  Instance              The MTFTP session
    248   @param  Packet                The received OACK packet
    249   @param  Len                   The length of the packet
    250   @param  Completed             Whether the transmisson has completed. NOT used by
    251                                 this function.
    252 
    253   @retval EFI_SUCCESS           The OACK process is OK
    254   @retval EFI_TFTP_ERROR        Some error occured, and the session reset.
    255 
    256 **/
    257 EFI_STATUS
    258 Mtftp4WrqHandleOack (
    259   IN OUT MTFTP4_PROTOCOL       *Instance,
    260   IN     EFI_MTFTP4_PACKET     *Packet,
    261   IN     UINT32                Len,
    262      OUT BOOLEAN               *Completed
    263   )
    264 {
    265   MTFTP4_OPTION             Reply;
    266   EFI_MTFTP4_PACKET         Bogus;
    267   EFI_STATUS                Status;
    268   INTN                      Expected;
    269 
    270   *Completed = FALSE;
    271 
    272   //
    273   // Ignore the OACK if already started the upload
    274   //
    275   Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
    276 
    277   if (Expected != 0) {
    278     return EFI_SUCCESS;
    279   }
    280 
    281   //
    282   // Parse and validate the options from server
    283   //
    284   ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
    285   Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);
    286 
    287   if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {
    288     //
    289     // Don't send a MTFTP error packet when out of resource, it can
    290     // only make it worse.
    291     //
    292     if (Status != EFI_OUT_OF_RESOURCES) {
    293       Mtftp4SendError (
    294         Instance,
    295         EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
    296         (UINT8 *) "Mal-formated OACK packet"
    297         );
    298     }
    299 
    300     return EFI_TFTP_ERROR;
    301   }
    302 
    303   if (Reply.BlkSize != 0) {
    304     Instance->BlkSize = Reply.BlkSize;
    305   }
    306 
    307   if (Reply.Timeout != 0) {
    308     Instance->Timeout = Reply.Timeout;
    309   }
    310 
    311   //
    312   // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,
    313   // which will start the transmission of the first data block.
    314   //
    315   Bogus.Ack.OpCode    = HTONS (EFI_MTFTP4_OPCODE_ACK);
    316   Bogus.Ack.Block[0]  = 0;
    317 
    318   Status = Mtftp4WrqHandleAck (
    319              Instance,
    320              &Bogus,
    321              sizeof (EFI_MTFTP4_ACK_HEADER),
    322              Completed
    323              );
    324 
    325   return Status;
    326 }
    327 
    328 
    329 /**
    330   The input process routine for MTFTP upload.
    331 
    332   @param  UdpPacket             The received MTFTP packet.
    333   @param  EndPoint              The local/remote access point
    334   @param  IoStatus              The result of the packet receiving
    335   @param  Context               Opaque parameter for the callback, which is the
    336                                 MTFTP session.
    337 **/
    338 VOID
    339 EFIAPI
    340 Mtftp4WrqInput (
    341   IN NET_BUF                *UdpPacket,
    342   IN UDP_END_POINT          *EndPoint,
    343   IN EFI_STATUS             IoStatus,
    344   IN VOID                   *Context
    345   )
    346 {
    347   MTFTP4_PROTOCOL           *Instance;
    348   EFI_MTFTP4_PACKET         *Packet;
    349   BOOLEAN                   Completed;
    350   EFI_STATUS                Status;
    351   UINT32                    Len;
    352   UINT16                    Opcode;
    353 
    354   Instance = (MTFTP4_PROTOCOL *) Context;
    355   NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
    356 
    357   Completed = FALSE;
    358   Packet    = NULL;
    359   Status    = EFI_SUCCESS;
    360 
    361   if (EFI_ERROR (IoStatus)) {
    362     Status = IoStatus;
    363     goto ON_EXIT;
    364   }
    365 
    366   ASSERT (UdpPacket != NULL);
    367 
    368   if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
    369     goto ON_EXIT;
    370   }
    371 
    372   //
    373   // Client send initial request to server's listening port. Server
    374   // will select a UDP port to communicate with the client.
    375   //
    376   if (EndPoint->RemotePort != Instance->ConnectedPort) {
    377     if (Instance->ConnectedPort != 0) {
    378       goto ON_EXIT;
    379     } else {
    380       Instance->ConnectedPort = EndPoint->RemotePort;
    381     }
    382   }
    383 
    384   //
    385   // Copy the MTFTP packet to a continuous buffer if it isn't already so.
    386   //
    387   Len = UdpPacket->TotalSize;
    388 
    389   if (UdpPacket->BlockOpNum > 1) {
    390     Packet = AllocatePool (Len);
    391 
    392     if (Packet == NULL) {
    393       Status = EFI_OUT_OF_RESOURCES;
    394       goto ON_EXIT;
    395     }
    396 
    397     NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
    398 
    399   } else {
    400     Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
    401     ASSERT (Packet != NULL);
    402   }
    403 
    404   Opcode = NTOHS (Packet->OpCode);
    405 
    406   //
    407   // Call the user's CheckPacket if provided. Abort the transmission
    408   // if CheckPacket returns an EFI_ERROR code.
    409   //
    410   if ((Instance->Token->CheckPacket != NULL) &&
    411       ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
    412 
    413     Status = Instance->Token->CheckPacket (
    414                                 &Instance->Mtftp4,
    415                                 Instance->Token,
    416                                 (UINT16) Len,
    417                                 Packet
    418                                 );
    419 
    420     if (EFI_ERROR (Status)) {
    421       //
    422       // Send an error message to the server to inform it
    423       //
    424       if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
    425         Mtftp4SendError (
    426           Instance,
    427           EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
    428           (UINT8 *) "User aborted the transfer"
    429           );
    430       }
    431 
    432       Status = EFI_ABORTED;
    433       goto ON_EXIT;
    434     }
    435   }
    436 
    437   switch (Opcode) {
    438   case EFI_MTFTP4_OPCODE_ACK:
    439     if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {
    440       goto ON_EXIT;
    441     }
    442 
    443     Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);
    444     break;
    445 
    446   case EFI_MTFTP4_OPCODE_OACK:
    447     if (Len <= MTFTP4_OPCODE_LEN) {
    448       goto ON_EXIT;
    449     }
    450 
    451     Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);
    452     break;
    453 
    454   case EFI_MTFTP4_OPCODE_ERROR:
    455     Status = EFI_TFTP_ERROR;
    456     break;
    457 
    458   default:
    459     break;
    460   }
    461 
    462 ON_EXIT:
    463   //
    464   // Free the resources, then if !EFI_ERROR (Status) and not completed,
    465   // restart the receive, otherwise end the session.
    466   //
    467   if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
    468     FreePool (Packet);
    469   }
    470 
    471   if (UdpPacket != NULL) {
    472     NetbufFree (UdpPacket);
    473   }
    474 
    475   if (!EFI_ERROR (Status) && !Completed) {
    476     Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
    477   }
    478 
    479   //
    480   // Status may have been updated by UdpIoRecvDatagram
    481   //
    482   if (EFI_ERROR (Status) || Completed) {
    483     Mtftp4CleanOperation (Instance, Status);
    484   }
    485 }
    486 
    487 
    488 
    489 /**
    490   Start the MTFTP session for upload.
    491 
    492   It will first init some states, then send the WRQ request packet,
    493   and start receiving the packet.
    494 
    495   @param  Instance              The MTFTP session
    496   @param  Operation             Redundant parameter, which is always
    497                                 EFI_MTFTP4_OPCODE_WRQ here.
    498 
    499   @retval EFI_SUCCESS           The upload process has been started.
    500   @retval Others                Failed to start the upload.
    501 
    502 **/
    503 EFI_STATUS
    504 Mtftp4WrqStart (
    505   IN MTFTP4_PROTOCOL        *Instance,
    506   IN UINT16                 Operation
    507   )
    508 {
    509   EFI_STATUS                Status;
    510 
    511   //
    512   // The valid block number range are [0, 0xffff]. For example:
    513   // the client sends an WRQ request to the server, the server
    514   // ACK with an ACK0 to let client start transfer the first
    515   // packet.
    516   //
    517   Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);
    518 
    519   if (EFI_ERROR (Status)) {
    520     return Status;
    521   }
    522 
    523   Status = Mtftp4SendRequest (Instance);
    524 
    525   if (EFI_ERROR (Status)) {
    526     return Status;
    527   }
    528 
    529   return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
    530 }
    531 
    532