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