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