• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
Mtftp4AllocateRange(IN UINT16 Start,IN UINT16 End)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
Mtftp4InitBlockRange(IN LIST_ENTRY * Head,IN UINT16 Start,IN UINT16 End)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
Mtftp4GetNextBlockNum(IN LIST_ENTRY * Head)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
Mtftp4SetLastBlockNum(IN LIST_ENTRY * Head,IN UINT16 Last)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
Mtftp4RemoveBlockNum(IN LIST_ENTRY * Head,IN UINT16 Num,IN BOOLEAN Completed,OUT UINT64 * TotalBlock)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
Mtftp4SendRequest(IN MTFTP4_PROTOCOL * Instance)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
Mtftp4SendError(IN MTFTP4_PROTOCOL * Instance,IN UINT16 ErrCode,IN UINT8 * ErrInfo)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
Mtftp4OnPacketSent(IN NET_BUF * Packet,IN UDP_END_POINT * EndPoint,IN EFI_STATUS IoStatus,IN VOID * Context)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
Mtftp4SetTimeout(IN OUT MTFTP4_PROTOCOL * Instance)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
Mtftp4SendPacket(IN OUT MTFTP4_PROTOCOL * Instance,IN OUT NET_BUF * Packet)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
Mtftp4Retransmit(IN MTFTP4_PROTOCOL * Instance)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
Mtftp4OnTimerTick(IN EFI_EVENT Event,IN VOID * Context)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