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