]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c
Update code to avoid potential access violation.
[mirror_edk2.git] / NetworkPkg / Mtftp6Dxe / Mtftp6Wrq.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 Mtftp6 Wrq process functions implementation.\r
3\r
7a49cd08 4 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>\r
a3bcde70
HT
5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php.\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "Mtftp6Impl.h"\r
17\r
18\r
19\r
20/**\r
21 Build and send a Mtftp6 data packet for upload.\r
22\r
23 @param[in] Instance The pointer to the Mtftp6 instance.\r
24 @param[in] BlockNum The block num to be sent.\r
25\r
26 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet.\r
27 @retval EFI_SUCCESS The data packet was sent.\r
28 @retval EFI_ABORTED The user aborted this process.\r
29\r
30**/\r
31EFI_STATUS\r
32Mtftp6WrqSendBlock (\r
33 IN MTFTP6_INSTANCE *Instance,\r
34 IN UINT16 BlockNum\r
35 )\r
36{\r
37 EFI_MTFTP6_PACKET *Packet;\r
38 EFI_MTFTP6_TOKEN *Token;\r
39 NET_BUF *UdpPacket;\r
40 EFI_STATUS Status;\r
41 UINT16 DataLen;\r
42 UINT8 *DataBuf;\r
43 UINT64 Start;\r
44\r
45 //\r
46 // Allocate net buffer to create data packet.\r
47 //\r
48 UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN);\r
49\r
50 if (UdpPacket == NULL) {\r
51 return EFI_OUT_OF_RESOURCES;\r
52 }\r
53\r
54 Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (\r
55 UdpPacket,\r
56 MTFTP6_DATA_HEAD_LEN,\r
57 FALSE\r
58 );\r
59 ASSERT (Packet != NULL);\r
60\r
61 Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA);\r
62 Packet->Data.Block = HTONS (BlockNum);\r
63\r
64 //\r
65 // Read the block from either the buffer or PacketNeeded callback\r
66 //\r
67 Token = Instance->Token;\r
68 DataLen = Instance->BlkSize;\r
69\r
70 if (Token->Buffer != NULL) {\r
71 Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);\r
72\r
73 if (Token->BufferSize < Start + Instance->BlkSize) {\r
74 DataLen = (UINT16) (Token->BufferSize - Start);\r
75 Instance->LastBlk = BlockNum;\r
76 Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);\r
77 }\r
78\r
79 if (DataLen > 0) {\r
80 NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
81 CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);\r
82 }\r
83\r
84 } else {\r
85 //\r
86 // Get data from PacketNeeded\r
87 //\r
88 DataBuf = NULL;\r
89 Status = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf);\r
90\r
91 if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {\r
92 if (DataBuf != NULL) {\r
93 gBS->FreePool (DataBuf);\r
94 }\r
95 //\r
96 // The received packet has already been freed.\r
97 //\r
98 Mtftp6SendError (\r
99 Instance,\r
100 EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
101 (UINT8 *) "User aborted the transfer"\r
102 );\r
103\r
104 return EFI_ABORTED;\r
105 }\r
106\r
107 if (DataLen < Instance->BlkSize) {\r
108 Instance->LastBlk = BlockNum;\r
109 Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum);\r
110 }\r
111\r
112 if (DataLen > 0) {\r
113 NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
114 CopyMem (Packet->Data.Data, DataBuf, DataLen);\r
115 gBS->FreePool (DataBuf);\r
116 }\r
117 }\r
118\r
119 //\r
120 // Reset current retry count of the instance.\r
121 //\r
122 Instance->CurRetry = 0;\r
123\r
124 return Mtftp6TransmitPacket (Instance, UdpPacket);\r
125}\r
126\r
127\r
128/**\r
129 Function to handle received ACK packet. If the ACK number matches the\r
130 expected block number, with more data pending, send the next\r
131 block. Otherwise, tell the caller that we are done.\r
132\r
133 @param[in] Instance The pointer to the Mtftp6 instance.\r
134 @param[in] Packet The pointer to the received packet.\r
135 @param[in] Len The length of the packet.\r
136 @param[out] UdpPacket The net buf of received packet.\r
137 @param[out] IsCompleted If TRUE, the upload has been completed.\r
138 Otherwise, the upload has not been completed.\r
139\r
140 @retval EFI_SUCCESS The ACK packet successfully processed.\r
141 @retval EFI_TFTP_ERROR The block number loops back.\r
142 @retval Others Failed to transmit the next data packet.\r
143\r
144**/\r
145EFI_STATUS\r
146Mtftp6WrqHandleAck (\r
147 IN MTFTP6_INSTANCE *Instance,\r
148 IN EFI_MTFTP6_PACKET *Packet,\r
149 IN UINT32 Len,\r
150 OUT NET_BUF **UdpPacket,\r
151 OUT BOOLEAN *IsCompleted\r
152 )\r
153{\r
154 UINT16 AckNum;\r
155 INTN Expected;\r
156 UINT64 TotalBlock;\r
157\r
158 *IsCompleted = FALSE;\r
159 AckNum = NTOHS (Packet->Ack.Block[0]);\r
160 Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
161\r
162 ASSERT (Expected >= 0);\r
163\r
164 //\r
165 // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput\r
166 // restart receive.\r
167 //\r
168 if (Expected != AckNum) {\r
169 return EFI_SUCCESS;\r
170 }\r
171\r
172 //\r
173 // Remove the acked block number, if this is the last block number,\r
174 // tell the Mtftp6WrqInput to finish the transfer. This is the last\r
175 // block number if the block range are empty..\r
176 //\r
177 Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock);\r
178\r
179 Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
180\r
181 if (Expected < 0) {\r
182 //\r
183 // The block range is empty. It may either because the the last\r
184 // block has been ACKed, or the sequence number just looped back,\r
185 // that is, there is more than 0xffff blocks.\r
186 //\r
187 if (Instance->LastBlk == AckNum) {\r
188 ASSERT (Instance->LastBlk >= 1);\r
189 *IsCompleted = TRUE;\r
190 return EFI_SUCCESS;\r
191\r
192 } else {\r
193 //\r
194 // Free the received packet before send new packet in ReceiveNotify,\r
195 // since the udpio might need to be reconfigured.\r
196 //\r
197 NetbufFree (*UdpPacket);\r
198 *UdpPacket = NULL;\r
199 //\r
200 // Send the Mtftp6 error message if block number rolls back.\r
201 //\r
202 Mtftp6SendError (\r
203 Instance,\r
204 EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
205 (UINT8 *) "Block number rolls back, not supported, try blksize option"\r
206 );\r
207\r
208 return EFI_TFTP_ERROR;\r
209 }\r
210 }\r
211\r
212 //\r
213 // Free the receive buffer before send new packet since it might need\r
214 // reconfigure udpio.\r
215 //\r
216 NetbufFree (*UdpPacket);\r
217 *UdpPacket = NULL;\r
218\r
219 return Mtftp6WrqSendBlock (Instance, (UINT16) Expected);\r
220}\r
221\r
222\r
223/**\r
224 Check whether the received OACK is valid. The OACK is valid\r
225 only if:\r
226 1. It only include options requested by us.\r
227 2. It can only include a smaller block size.\r
228 3. It can't change the proposed time out value.\r
229 4. Other requirements of the individal MTFTP6 options as required.\r
230\r
231 @param[in] ReplyInfo The pointer to options information in reply packet.\r
232 @param[in] RequestInfo The pointer to requested options information.\r
233\r
234 @retval TRUE If the option in OACK is valid.\r
235 @retval FALSE If the option is invalid.\r
236\r
237**/\r
238BOOLEAN\r
239Mtftp6WrqOackValid (\r
240 IN MTFTP6_EXT_OPTION_INFO *ReplyInfo,\r
241 IN MTFTP6_EXT_OPTION_INFO *RequestInfo\r
242 )\r
243{\r
244 //\r
245 // It is invalid for server to return options we don't request\r
246 //\r
247 if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) {\r
248 return FALSE;\r
249 }\r
250\r
251 //\r
252 // Server can only specify a smaller block size to be used and\r
253 // return the timeout matches that requested.\r
254 //\r
255 if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) ||\r
256 (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout))\r
257 ) {\r
258\r
259 return FALSE;\r
260 }\r
261\r
262 return TRUE;\r
263}\r
264\r
265\r
266/**\r
267 Process the OACK packet for Wrq.\r
268\r
269 @param[in] Instance The pointer to the Mtftp6 instance.\r
270 @param[in] Packet The pointer to the received packet.\r
271 @param[in] Len The length of the packet.\r
272 @param[out] UdpPacket The net buf of received packet.\r
273 @param[out] IsCompleted If TRUE, the upload has been completed.\r
274 Otherwise, the upload has not been completed.\r
275\r
276 @retval EFI_SUCCESS The OACK packet successfully processed.\r
277 @retval EFI_TFTP_ERROR An TFTP communication error happened.\r
278 @retval Others Failed to process the OACK packet.\r
279\r
280**/\r
281EFI_STATUS\r
282Mtftp6WrqHandleOack (\r
283 IN MTFTP6_INSTANCE *Instance,\r
284 IN EFI_MTFTP6_PACKET *Packet,\r
285 IN UINT32 Len,\r
286 OUT NET_BUF **UdpPacket,\r
287 OUT BOOLEAN *IsCompleted\r
288 )\r
289{\r
290 EFI_MTFTP6_OPTION *Options;\r
291 UINT32 Count;\r
292 MTFTP6_EXT_OPTION_INFO ExtInfo;\r
293 EFI_MTFTP6_PACKET Dummy;\r
294 EFI_STATUS Status;\r
295 INTN Expected;\r
296\r
297 *IsCompleted = FALSE;\r
298\r
299 //\r
300 // Ignore the OACK if already started the upload\r
301 //\r
302 Expected = Mtftp6GetNextBlockNum (&Instance->BlkList);\r
303\r
304 if (Expected != 0) {\r
305 return EFI_SUCCESS;\r
306 }\r
307\r
308 //\r
309 // Parse and validate the options from server\r
310 //\r
311 ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));\r
312\r
313 Status = Mtftp6ParseStart (Packet, Len, &Count, &Options);\r
314\r
315 if (EFI_ERROR (Status)) {\r
316 return Status;\r
317 }\r
7a49cd08 318 ASSERT (Options != NULL);\r
a3bcde70
HT
319\r
320 Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo);\r
321\r
322 if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) {\r
323 //\r
324 // Don't send a MTFTP error packet when out of resource, it can\r
325 // only make it worse.\r
326 //\r
327 if (Status != EFI_OUT_OF_RESOURCES) {\r
328 //\r
329 // Free the received packet before send new packet in ReceiveNotify,\r
330 // since the udpio might need to be reconfigured.\r
331 //\r
332 NetbufFree (*UdpPacket);\r
333 *UdpPacket = NULL;\r
334 //\r
335 // Send the Mtftp6 error message if invalid Oack packet received.\r
336 //\r
337 Mtftp6SendError (\r
338 Instance,\r
339 EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION,\r
340 (UINT8 *) "Mal-formated OACK packet"\r
341 );\r
342 }\r
343\r
344 return EFI_TFTP_ERROR;\r
345 }\r
346\r
347 if (ExtInfo.BlkSize != 0) {\r
348 Instance->BlkSize = ExtInfo.BlkSize;\r
349 }\r
350\r
351 if (ExtInfo.Timeout != 0) {\r
352 Instance->Timeout = ExtInfo.Timeout;\r
353 }\r
354\r
355 //\r
356 // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck,\r
357 // which will start the transmission of the first data block.\r
358 //\r
359 Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK);\r
360 Dummy.Ack.Block[0] = 0;\r
361\r
362 return Mtftp6WrqHandleAck (\r
363 Instance,\r
364 &Dummy,\r
365 sizeof (EFI_MTFTP6_ACK_HEADER),\r
366 UdpPacket,\r
367 IsCompleted\r
368 );\r
369}\r
370\r
371\r
372/**\r
373 The packet process callback for Mtftp6 upload.\r
374\r
375 @param[in] UdpPacket The pointer to the packet received.\r
376 @param[in] UdpEpt The pointer to the Udp6 access point.\r
377 @param[in] IoStatus The status from Udp6 instance.\r
378 @param[in] Context The pointer to the context.\r
379\r
380**/\r
381VOID\r
382EFIAPI\r
383Mtftp6WrqInput (\r
384 IN NET_BUF *UdpPacket,\r
385 IN UDP_END_POINT *UdpEpt,\r
386 IN EFI_STATUS IoStatus,\r
387 IN VOID *Context\r
388 )\r
389{\r
390 MTFTP6_INSTANCE *Instance;\r
391 EFI_MTFTP6_PACKET *Packet;\r
392 BOOLEAN IsCompleted;\r
393 EFI_STATUS Status;\r
394 UINT32 TotalNum;\r
395 UINT32 Len;\r
396 UINT16 Opcode;\r
397\r
398 Instance = (MTFTP6_INSTANCE *) Context;\r
399\r
400 NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE);\r
401\r
402 IsCompleted = FALSE;\r
403 Packet = NULL;\r
404 Status = EFI_SUCCESS;\r
405 TotalNum = 0;\r
406\r
407 //\r
408 // Return error status if Udp6 instance failed to receive.\r
409 //\r
410 if (EFI_ERROR (IoStatus)) {\r
411 Status = IoStatus;\r
412 goto ON_EXIT;\r
413 }\r
414\r
415 ASSERT (UdpPacket != NULL);\r
416\r
417 if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) {\r
418 goto ON_EXIT;\r
419 }\r
420\r
421 //\r
422 // Client send initial request to server's listening port. Server\r
423 // will select a UDP port to communicate with the client.\r
424 //\r
425 if (UdpEpt->RemotePort != Instance->ServerDataPort) {\r
426 if (Instance->ServerDataPort != 0) {\r
427 goto ON_EXIT;\r
428 } else {\r
429 Instance->ServerDataPort = UdpEpt->RemotePort;\r
430 }\r
431 }\r
432\r
433 //\r
434 // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
435 //\r
436 Len = UdpPacket->TotalSize;\r
437 TotalNum = UdpPacket->BlockOpNum;\r
438\r
439 if (TotalNum > 1) {\r
440 Packet = AllocateZeroPool (Len);\r
441\r
442 if (Packet == NULL) {\r
443 Status = EFI_OUT_OF_RESOURCES;\r
444 goto ON_EXIT;\r
445 }\r
446\r
447 NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
448\r
449 } else {\r
450 Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
451 ASSERT (Packet != NULL);\r
452 }\r
453\r
454 Opcode = NTOHS (Packet->OpCode);\r
455\r
456 //\r
457 // Callback to the user's CheckPacket if provided. Abort the transmission\r
458 // if CheckPacket returns an EFI_ERROR code.\r
459 //\r
460 if (Instance->Token->CheckPacket != NULL &&\r
461 (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR)\r
462 ) {\r
463\r
464 Status = Instance->Token->CheckPacket (\r
465 &Instance->Mtftp6,\r
466 Instance->Token,\r
467 (UINT16) Len,\r
468 Packet\r
469 );\r
470\r
471 if (EFI_ERROR (Status)) {\r
472 //\r
473 // Send an error message to the server to inform it\r
474 //\r
475 if (Opcode != EFI_MTFTP6_OPCODE_ERROR) {\r
476 //\r
477 // Free the received packet before send new packet in ReceiveNotify,\r
478 // since the udpio might need to be reconfigured.\r
479 //\r
480 NetbufFree (UdpPacket);\r
481 UdpPacket = NULL;\r
482 //\r
483 // Send the Mtftp6 error message if user aborted the current session.\r
484 //\r
485 Mtftp6SendError (\r
486 Instance,\r
487 EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
488 (UINT8 *) "User aborted the transfer"\r
489 );\r
490 }\r
491\r
492 Status = EFI_ABORTED;\r
493 goto ON_EXIT;\r
494 }\r
495 }\r
496\r
497 //\r
498 // Switch the process routines by the operation code.\r
499 //\r
500 switch (Opcode) {\r
501 case EFI_MTFTP6_OPCODE_ACK:\r
502 if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) {\r
503 goto ON_EXIT;\r
504 }\r
505 //\r
506 // Handle the Ack packet of Wrq.\r
507 //\r
508 Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted);\r
509 break;\r
510\r
511 case EFI_MTFTP6_OPCODE_OACK:\r
512 if (Len <= MTFTP6_OPCODE_LEN) {\r
513 goto ON_EXIT;\r
514 }\r
515 //\r
516 // Handle the Oack packet of Wrq.\r
517 //\r
518 Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted);\r
519 break;\r
520\r
521 default:\r
522 //\r
523 // Drop and return eror if received error message.\r
524 //\r
525 Status = EFI_TFTP_ERROR;\r
526 break;\r
527 }\r
528\r
529ON_EXIT:\r
530 //\r
531 // Free the resources, then if !EFI_ERROR (Status) and not completed,\r
532 // restart the receive, otherwise end the session.\r
533 //\r
534 if (Packet != NULL && TotalNum > 1) {\r
535 FreePool (Packet);\r
536 }\r
537\r
538 if (UdpPacket != NULL) {\r
539 NetbufFree (UdpPacket);\r
540 }\r
541\r
542 if (!EFI_ERROR (Status) && !IsCompleted) {\r
543 Status = UdpIoRecvDatagram (\r
544 Instance->UdpIo,\r
545 Mtftp6WrqInput,\r
546 Instance,\r
547 0\r
548 );\r
549 }\r
550 //\r
551 // Clean up the current session if failed to continue.\r
552 //\r
553 if (EFI_ERROR (Status) || IsCompleted) {\r
554 Mtftp6OperationClean (Instance, Status);\r
555 }\r
556}\r
557\r
558\r
559/**\r
560 Start the Mtftp6 instance to upload. It will first init some states,\r
561 then send the WRQ request packet, and start to receive the packet.\r
562\r
563 @param[in] Instance The pointer to the Mtftp6 instance.\r
564 @param[in] Operation The operation code of the current packet.\r
565\r
566 @retval EFI_SUCCESS The Mtftp6 was started to upload.\r
567 @retval Others Failed to start to upload.\r
568\r
569**/\r
570EFI_STATUS\r
571Mtftp6WrqStart (\r
572 IN MTFTP6_INSTANCE *Instance,\r
573 IN UINT16 Operation\r
574 )\r
575{\r
576 EFI_STATUS Status;\r
577\r
578 //\r
579 // The valid block number range are [0, 0xffff]. For example:\r
580 // the client sends an WRQ request to the server, the server\r
581 // ACK with an ACK0 to let client start transfer the first\r
582 // packet.\r
583 //\r
584 Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff);\r
585\r
586 if (EFI_ERROR (Status)) {\r
587 return Status;\r
588 }\r
589\r
590 Status = Mtftp6SendRequest (Instance, Operation);\r
591\r
592 if (EFI_ERROR (Status)) {\r
593 return Status;\r
594 }\r
595\r
596 return UdpIoRecvDatagram (\r
597 Instance->UdpIo,\r
598 Mtftp6WrqInput,\r
599 Instance,\r
600 0\r
601 );\r
602}\r
603\r