Home | History | Annotate | Download | only in Mtftp4Dxe
      1 /** @file
      2   Support routines for Mtftp.
      3 
      4 Copyright (c) 2006 - 2015, 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   Allocate a MTFTP4 block range, then init it to the range of [Start, End]
     20 
     21   @param  Start                 The start block number
     22   @param  End                   The last block number in the range
     23 
     24   @return Pointer to the created block range, NULL if failed to allocate memory.
     25 
     26 **/
     27 MTFTP4_BLOCK_RANGE *
     28 Mtftp4AllocateRange (
     29   IN UINT16                 Start,
     30   IN UINT16                 End
     31   )
     32 {
     33   MTFTP4_BLOCK_RANGE        *Range;
     34 
     35   Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE));
     36 
     37   if (Range == NULL) {
     38     return NULL;
     39   }
     40 
     41   InitializeListHead (&Range->Link);
     42   Range->Start  = Start;
     43   Range->End    = End;
     44   Range->Bound  = End;
     45 
     46   return Range;
     47 }
     48 
     49 
     50 /**
     51   Initialize the block range for either RRQ or WRQ.
     52 
     53   RRQ and WRQ have different requirements for Start and End.
     54   For example, during start up, WRQ initializes its whole valid block range
     55   to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us
     56   to start the upload. When the client received ACK0, it will remove 0 from the
     57   range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
     58   without option negotiation, the server will directly send us the BLOCK1 in
     59   response to the client's RRQ. When received BLOCK1, the client will remove
     60   it from the block range and send an ACK. It also works if there is option
     61   negotiation.
     62 
     63   @param  Head                  The block range head to initialize
     64   @param  Start                 The Start block number.
     65   @param  End                   The last block number.
     66 
     67   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for initial block range
     68   @retval EFI_SUCCESS           The initial block range is created.
     69 
     70 **/
     71 EFI_STATUS
     72 Mtftp4InitBlockRange (
     73   IN LIST_ENTRY             *Head,
     74   IN UINT16                 Start,
     75   IN UINT16                 End
     76   )
     77 {
     78   MTFTP4_BLOCK_RANGE        *Range;
     79 
     80   Range = Mtftp4AllocateRange (Start, End);
     81 
     82   if (Range == NULL) {
     83     return EFI_OUT_OF_RESOURCES;
     84   }
     85 
     86   InsertTailList (Head, &Range->Link);
     87   return EFI_SUCCESS;
     88 }
     89 
     90 
     91 /**
     92   Get the first valid block number on the range list.
     93 
     94   @param  Head                  The block range head
     95 
     96   @return The first valid block number, -1 if the block range is empty.
     97 
     98 **/
     99 INTN
    100 Mtftp4GetNextBlockNum (
    101   IN LIST_ENTRY             *Head
    102   )
    103 {
    104   MTFTP4_BLOCK_RANGE  *Range;
    105 
    106   if (IsListEmpty (Head)) {
    107     return -1;
    108   }
    109 
    110   Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);
    111   return Range->Start;
    112 }
    113 
    114 
    115 /**
    116   Set the last block number of the block range list.
    117 
    118   It will remove all the blocks after the Last. MTFTP initialize the block range
    119   to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
    120   last block number, it will call this function to set the last block number.
    121 
    122   @param  Head                  The block range list
    123   @param  Last                  The last block number
    124 
    125 **/
    126 VOID
    127 Mtftp4SetLastBlockNum (
    128   IN LIST_ENTRY             *Head,
    129   IN UINT16                 Last
    130   )
    131 {
    132   MTFTP4_BLOCK_RANGE        *Range;
    133 
    134   //
    135   // Iterate from the tail to head to remove the block number
    136   // after the last.
    137   //
    138   while (!IsListEmpty (Head)) {
    139     Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);
    140 
    141     if (Range->Start > Last) {
    142       RemoveEntryList (&Range->Link);
    143       FreePool (Range);
    144       continue;
    145     }
    146 
    147     if (Range->End > Last) {
    148       Range->End = Last;
    149     }
    150 
    151     return ;
    152   }
    153 }
    154 
    155 
    156 /**
    157   Remove the block number from the block range list.
    158 
    159   @param  Head                  The block range list to remove from
    160   @param  Num                   The block number to remove
    161   @param  Completed             Whether Num is the last block number
    162   @param  TotalBlock            The continuous block number in all
    163 
    164   @retval EFI_NOT_FOUND         The block number isn't in the block range list
    165   @retval EFI_SUCCESS           The block number has been removed from the list
    166   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource
    167 
    168 **/
    169 EFI_STATUS
    170 Mtftp4RemoveBlockNum (
    171   IN LIST_ENTRY             *Head,
    172   IN UINT16                 Num,
    173   IN BOOLEAN                Completed,
    174   OUT UINT64                *TotalBlock
    175   )
    176 {
    177   MTFTP4_BLOCK_RANGE        *Range;
    178   MTFTP4_BLOCK_RANGE        *NewRange;
    179   LIST_ENTRY                *Entry;
    180 
    181   NET_LIST_FOR_EACH (Entry, Head) {
    182 
    183     //
    184     // Each block represents a hole [Start, End] in the file,
    185     // skip to the first range with End >= Num
    186     //
    187     Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
    188 
    189     if (Range->End < Num) {
    190       continue;
    191     }
    192 
    193     //
    194     // There are three different cases for Start
    195     // 1. (Start > Num) && (End >= Num):
    196     //    because all the holes before this one has the condition of
    197     //    End < Num, so this block number has been removed.
    198     //
    199     // 2. (Start == Num) && (End >= Num):
    200     //    Need to increase the Start by one, and if End == Num, this
    201     //    hole has been removed completely, remove it.
    202     //
    203     // 3. (Start < Num) && (End >= Num):
    204     //    if End == Num, only need to decrease the End by one because
    205     //    we have (Start < Num) && (Num == End), so (Start <= End - 1).
    206     //    if (End > Num), the hold is splited into two holes, with
    207     //    [Start, Num - 1] and [Num + 1, End].
    208     //
    209     if (Range->Start > Num) {
    210       return EFI_NOT_FOUND;
    211 
    212     } else if (Range->Start == Num) {
    213       Range->Start++;
    214 
    215       //
    216       // Note that: RFC 1350 does not mention block counter roll-over,
    217       // but several TFTP hosts implement the roll-over be able to accept
    218       // transfers of unlimited size. There is no consensus, however, whether
    219       // the counter should wrap around to zero or to one. Many implementations
    220       // wrap to zero, because this is the simplest to implement. Here we choose
    221       // this solution.
    222       //
    223 	  *TotalBlock  = Num;
    224 
    225       if (Range->Round > 0) {
    226 	    *TotalBlock += Range->Bound +  MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;
    227 	  }
    228 
    229       if (Range->Start > Range->Bound) {
    230 	  	  Range->Start = 0;
    231 		  Range->Round ++;
    232       }
    233 
    234       if ((Range->Start > Range->End) || Completed) {
    235         RemoveEntryList (&Range->Link);
    236         FreePool (Range);
    237       }
    238 
    239       return EFI_SUCCESS;
    240 
    241     } else {
    242       if (Range->End == Num) {
    243         Range->End--;
    244       } else {
    245         NewRange = Mtftp4AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
    246 
    247         if (NewRange == NULL) {
    248           return EFI_OUT_OF_RESOURCES;
    249         }
    250 
    251         Range->End = Num - 1;
    252         NetListInsertAfter (&Range->Link, &NewRange->Link);
    253       }
    254 
    255       return EFI_SUCCESS;
    256     }
    257   }
    258 
    259   return EFI_NOT_FOUND;
    260 }
    261 
    262 
    263 /**
    264   Build then transmit the request packet for the MTFTP session.
    265 
    266   @param  Instance              The Mtftp session
    267 
    268   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the request
    269   @retval EFI_SUCCESS           The request is built and sent
    270   @retval Others                Failed to transmit the packet.
    271 
    272 **/
    273 EFI_STATUS
    274 Mtftp4SendRequest (
    275   IN MTFTP4_PROTOCOL        *Instance
    276   )
    277 {
    278   EFI_MTFTP4_PACKET         *Packet;
    279   EFI_MTFTP4_OPTION         *Options;
    280   EFI_MTFTP4_TOKEN          *Token;
    281   RETURN_STATUS             Status;
    282   NET_BUF                   *Nbuf;
    283   UINT8                     *Mode;
    284   UINT8                     *Cur;
    285   UINTN                     Index;
    286   UINT32                    BufferLength;
    287   UINTN                     FileNameLength;
    288   UINTN                     ModeLength;
    289   UINTN                     OptionStrLength;
    290   UINTN                     ValueStrLength;
    291 
    292   Token   = Instance->Token;
    293   Options = Token->OptionList;
    294   Mode    = Instance->Token->ModeStr;
    295 
    296   if (Mode == NULL) {
    297     Mode = (UINT8 *) "octet";
    298   }
    299 
    300   //
    301   // Compute the packet length
    302   //
    303   FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);
    304   ModeLength     = AsciiStrLen ((CHAR8 *) Mode);
    305   BufferLength   = (UINT32) FileNameLength + (UINT32) ModeLength + 4;
    306 
    307   for (Index = 0; Index < Token->OptionCount; Index++) {
    308     OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
    309     ValueStrLength  = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
    310     BufferLength   += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;
    311   }
    312   //
    313   // Allocate a packet then copy the data over
    314   //
    315   if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {
    316     return EFI_OUT_OF_RESOURCES;
    317   }
    318 
    319   Packet         = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);
    320   ASSERT (Packet != NULL);
    321 
    322   Packet->OpCode = HTONS (Instance->Operation);
    323   BufferLength  -= sizeof (Packet->OpCode);
    324 
    325   Cur            = Packet->Rrq.Filename;
    326   Status         = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);
    327   ASSERT_EFI_ERROR (Status);
    328   BufferLength  -= (UINT32) (FileNameLength + 1);
    329   Cur           += FileNameLength + 1;
    330   Status         = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);
    331   ASSERT_EFI_ERROR (Status);
    332   BufferLength  -= (UINT32) (ModeLength + 1);
    333   Cur           += ModeLength + 1;
    334 
    335   for (Index = 0; Index < Token->OptionCount; ++Index) {
    336     OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
    337     ValueStrLength  = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
    338 
    339     Status          = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);
    340     ASSERT_EFI_ERROR (Status);
    341     BufferLength   -= (UINT32) (OptionStrLength + 1);
    342     Cur            += OptionStrLength + 1;
    343 
    344     Status          = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);
    345     ASSERT_EFI_ERROR (Status);
    346     BufferLength   -= (UINT32) (ValueStrLength + 1);
    347     Cur            += ValueStrLength + 1;
    348 
    349   }
    350 
    351   return Mtftp4SendPacket (Instance, Nbuf);
    352 }
    353 
    354 
    355 /**
    356   Build then send an error message.
    357 
    358   @param  Instance              The MTFTP session
    359   @param  ErrCode               The error code
    360   @param  ErrInfo               The error message
    361 
    362   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the error packet
    363   @retval EFI_SUCCESS           The error packet is transmitted.
    364   @retval Others                Failed to transmit the packet.
    365 
    366 **/
    367 EFI_STATUS
    368 Mtftp4SendError (
    369   IN MTFTP4_PROTOCOL        *Instance,
    370   IN UINT16                 ErrCode,
    371   IN UINT8                  *ErrInfo
    372   )
    373 {
    374   NET_BUF                   *Packet;
    375   EFI_MTFTP4_PACKET         *TftpError;
    376   UINT32                    Len;
    377 
    378   Len     = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));
    379   Packet  = NetbufAlloc (Len);
    380   if (Packet == NULL) {
    381     return EFI_OUT_OF_RESOURCES;
    382   }
    383 
    384   TftpError         = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);
    385   ASSERT (TftpError != NULL);
    386 
    387   TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);
    388   TftpError->Error.ErrorCode = HTONS (ErrCode);
    389 
    390   AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, Len, (CHAR8 *) ErrInfo);
    391 
    392   return Mtftp4SendPacket (Instance, Packet);
    393 }
    394 
    395 
    396 /**
    397   The callback function called when the packet is transmitted.
    398 
    399   It simply frees the packet.
    400 
    401   @param  Packet                The transmitted (or failed to) packet
    402   @param  EndPoint              The local and remote UDP access point
    403   @param  IoStatus              The result of the transmission
    404   @param  Context               Opaque parameter to the callback
    405 
    406 **/
    407 VOID
    408 EFIAPI
    409 Mtftp4OnPacketSent (
    410   IN NET_BUF                   *Packet,
    411   IN UDP_END_POINT             *EndPoint,
    412   IN EFI_STATUS                IoStatus,
    413   IN VOID                      *Context
    414   )
    415 {
    416   NetbufFree (Packet);
    417 }
    418 
    419 
    420 /**
    421   Set the timeout for the instance. User a longer time for passive instances.
    422 
    423   @param  Instance              The Mtftp session to set time out
    424 
    425 **/
    426 VOID
    427 Mtftp4SetTimeout (
    428   IN OUT MTFTP4_PROTOCOL        *Instance
    429   )
    430 {
    431   if (Instance->Master) {
    432     Instance->PacketToLive = Instance->Timeout;
    433   } else {
    434     Instance->PacketToLive = Instance->Timeout * 2;
    435   }
    436 }
    437 
    438 
    439 /**
    440   Send the packet for the instance.
    441 
    442   It will first save a reference to the packet for later retransmission.
    443   Then determine the destination port, listen port for requests, and connected
    444   port for others. At last, send the packet out.
    445 
    446   @param  Instance              The Mtftp instance
    447   @param  Packet                The packet to send
    448 
    449   @retval EFI_SUCCESS           The packet is sent out
    450   @retval Others                Failed to transmit the packet.
    451 
    452 **/
    453 EFI_STATUS
    454 Mtftp4SendPacket (
    455   IN OUT MTFTP4_PROTOCOL        *Instance,
    456   IN OUT NET_BUF                *Packet
    457   )
    458 {
    459   UDP_END_POINT             UdpPoint;
    460   EFI_STATUS                Status;
    461   UINT16                    OpCode;
    462   UINT8                     *Buffer;
    463 
    464   //
    465   // Save the packet for retransmission
    466   //
    467   if (Instance->LastPacket != NULL) {
    468     NetbufFree (Instance->LastPacket);
    469   }
    470 
    471   Instance->LastPacket        = Packet;
    472 
    473   Instance->CurRetry          = 0;
    474   Mtftp4SetTimeout (Instance);
    475 
    476   ZeroMem (&UdpPoint, sizeof (UdpPoint));
    477   UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
    478 
    479   //
    480   // Send the requests to the listening port, other packets
    481   // to the connected port
    482   //
    483   Buffer = NetbufGetByte (Packet, 0, NULL);
    484   ASSERT (Buffer != NULL);
    485   OpCode = NTOHS (*(UINT16 *)Buffer);
    486 
    487   if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) ||
    488       (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
    489       (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
    490     UdpPoint.RemotePort = Instance->ListeningPort;
    491   } else {
    492     UdpPoint.RemotePort = Instance->ConnectedPort;
    493   }
    494 
    495   NET_GET_REF (Packet);
    496 
    497   Status = UdpIoSendDatagram (
    498              Instance->UnicastPort,
    499              Packet,
    500              &UdpPoint,
    501              NULL,
    502              Mtftp4OnPacketSent,
    503              Instance
    504              );
    505 
    506   if (EFI_ERROR (Status)) {
    507     NET_PUT_REF (Packet);
    508   }
    509 
    510   return Status;
    511 }
    512 
    513 
    514 /**
    515   Retransmit the last packet for the instance.
    516 
    517   @param  Instance              The Mtftp instance
    518 
    519   @retval EFI_SUCCESS           The last packet is retransmitted.
    520   @retval Others                Failed to retransmit.
    521 
    522 **/
    523 EFI_STATUS
    524 Mtftp4Retransmit (
    525   IN MTFTP4_PROTOCOL        *Instance
    526   )
    527 {
    528   UDP_END_POINT             UdpPoint;
    529   EFI_STATUS                Status;
    530   UINT16                    OpCode;
    531   UINT8                     *Buffer;
    532 
    533   ASSERT (Instance->LastPacket != NULL);
    534 
    535   ZeroMem (&UdpPoint, sizeof (UdpPoint));
    536   UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
    537 
    538   //
    539   // Set the requests to the listening port, other packets to the connected port
    540   //
    541   Buffer = NetbufGetByte (Instance->LastPacket, 0, NULL);
    542   ASSERT (Buffer != NULL);
    543   OpCode = NTOHS (*(UINT16 *) Buffer);
    544 
    545   if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
    546       (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
    547     UdpPoint.RemotePort = Instance->ListeningPort;
    548   } else {
    549     UdpPoint.RemotePort = Instance->ConnectedPort;
    550   }
    551 
    552   NET_GET_REF (Instance->LastPacket);
    553 
    554   Status = UdpIoSendDatagram (
    555              Instance->UnicastPort,
    556              Instance->LastPacket,
    557              &UdpPoint,
    558              NULL,
    559              Mtftp4OnPacketSent,
    560              Instance
    561              );
    562 
    563   if (EFI_ERROR (Status)) {
    564     NET_PUT_REF (Instance->LastPacket);
    565   }
    566 
    567   return Status;
    568 }
    569 
    570 
    571 /**
    572   The timer ticking function for the Mtftp service instance.
    573 
    574   @param  Event                 The ticking event
    575   @param  Context               The Mtftp service instance
    576 
    577 **/
    578 VOID
    579 EFIAPI
    580 Mtftp4OnTimerTick (
    581   IN EFI_EVENT              Event,
    582   IN VOID                   *Context
    583   )
    584 {
    585   MTFTP4_SERVICE            *MtftpSb;
    586   LIST_ENTRY                *Entry;
    587   LIST_ENTRY                *Next;
    588   MTFTP4_PROTOCOL           *Instance;
    589   EFI_MTFTP4_TOKEN          *Token;
    590 
    591   MtftpSb = (MTFTP4_SERVICE *) Context;
    592 
    593   //
    594   // Iterate through all the children of the Mtftp service instance. Time
    595   // out the packet. If maximum retries reached, clean the session up.
    596   //
    597   NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {
    598     Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);
    599 
    600     if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {
    601       continue;
    602     }
    603 
    604     //
    605     // Call the user's time out handler
    606     //
    607     Token = Instance->Token;
    608 
    609     if ((Token->TimeoutCallback != NULL) &&
    610         EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {
    611 
    612       Mtftp4SendError (
    613          Instance,
    614          EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
    615          (UINT8 *) "User aborted the transfer in time out"
    616          );
    617 
    618       Mtftp4CleanOperation (Instance, EFI_ABORTED);
    619       continue;
    620     }
    621 
    622     //
    623     // Retransmit the packet if haven't reach the maxmium retry count,
    624     // otherwise exit the transfer.
    625     //
    626     if (++Instance->CurRetry < Instance->MaxRetry) {
    627       Mtftp4Retransmit (Instance);
    628       Mtftp4SetTimeout (Instance);
    629     } else {
    630       Mtftp4CleanOperation (Instance, EFI_TIMEOUT);
    631       continue;
    632     }
    633   }
    634 }
    635