BaseTools:Change the path of the file that Binary Cache
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Mtftp4Dxe / Mtftp4Wrq.c
1 /** @file\r
2   Routines to process Wrq (upload).\r
3 \r
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6 \r
7 **/\r
8 \r
9 #include "Mtftp4Impl.h"\r
10 \r
11 \r
12 \r
13 /**\r
14   Build then send a MTFTP data packet for the MTFTP upload session.\r
15 \r
16   @param  Instance              The MTFTP upload session.\r
17   @param  BlockNum              The block number to send.\r
18 \r
19   @retval EFI_OUT_OF_RESOURCES  Failed to build the packet.\r
20   @retval EFI_ABORTED           The consumer of this child directs to abort the\r
21                                 transmission by return an error through PacketNeeded.\r
22   @retval EFI_SUCCESS           The data is sent.\r
23 \r
24 **/\r
25 EFI_STATUS\r
26 Mtftp4WrqSendBlock (\r
27   IN OUT MTFTP4_PROTOCOL        *Instance,\r
28   IN     UINT16                 BlockNum\r
29   )\r
30 {\r
31   EFI_MTFTP4_PACKET         *Packet;\r
32   EFI_MTFTP4_TOKEN          *Token;\r
33   NET_BUF                   *UdpPacket;\r
34   EFI_STATUS                Status;\r
35   UINT16                    DataLen;\r
36   UINT8                     *DataBuf;\r
37   UINT64                    Start;\r
38 \r
39   //\r
40   // Allocate a buffer to hold the user data\r
41   //\r
42   UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);\r
43 \r
44   if (UdpPacket == NULL) {\r
45     return EFI_OUT_OF_RESOURCES;\r
46   }\r
47 \r
48   Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);\r
49   ASSERT (Packet != NULL);\r
50 \r
51   Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);\r
52   Packet->Data.Block  = HTONS (BlockNum);\r
53 \r
54   //\r
55   // Read the block from either the buffer or PacketNeeded callback\r
56   //\r
57   Token   = Instance->Token;\r
58   DataLen = Instance->BlkSize;\r
59 \r
60   if (Token->Buffer != NULL) {\r
61     Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);\r
62 \r
63     if (Token->BufferSize < Start + Instance->BlkSize) {\r
64       DataLen             = (UINT16) (Token->BufferSize - Start);\r
65       Instance->LastBlock = BlockNum;\r
66       Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);\r
67     }\r
68 \r
69     if (DataLen > 0) {\r
70       NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
71       CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);\r
72     }\r
73 \r
74   } else {\r
75     //\r
76     // Get data from PacketNeeded\r
77     //\r
78     DataBuf = NULL;\r
79     Status  = Token->PacketNeeded (\r
80                        &Instance->Mtftp4,\r
81                        Token,\r
82                        &DataLen,\r
83                        (VOID **) &DataBuf\r
84                        );\r
85 \r
86     if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {\r
87       if (DataBuf != NULL) {\r
88         FreePool (DataBuf);\r
89       }\r
90 \r
91       if (UdpPacket != NULL) {\r
92         NetbufFree (UdpPacket);\r
93       }\r
94 \r
95       Mtftp4SendError (\r
96         Instance,\r
97         EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
98         (UINT8 *) "User aborted the transfer"\r
99         );\r
100 \r
101       return EFI_ABORTED;\r
102     }\r
103 \r
104     if (DataLen < Instance->BlkSize) {\r
105       Instance->LastBlock = BlockNum;\r
106       Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);\r
107     }\r
108 \r
109     if (DataLen > 0) {\r
110       NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
111       CopyMem (Packet->Data.Data, DataBuf, DataLen);\r
112       FreePool (DataBuf);\r
113     }\r
114   }\r
115 \r
116   return Mtftp4SendPacket (Instance, UdpPacket);\r
117 }\r
118 \r
119 \r
120 /**\r
121   Function to handle received ACK packet.\r
122 \r
123   If the ACK number matches the expected block number, and there are more\r
124   data pending, send the next block. Otherwise tell the caller that we are done.\r
125 \r
126   @param  Instance              The MTFTP upload session\r
127   @param  Packet                The MTFTP packet received\r
128   @param  Len                   The packet length\r
129   @param  Completed             Return whether the upload has finished.\r
130 \r
131   @retval EFI_SUCCESS           The ACK is successfully processed.\r
132   @retval EFI_TFTP_ERROR        The block number loops back.\r
133   @retval Others                Failed to transmit the next data packet.\r
134 \r
135 **/\r
136 EFI_STATUS\r
137 Mtftp4WrqHandleAck (\r
138   IN     MTFTP4_PROTOCOL       *Instance,\r
139   IN     EFI_MTFTP4_PACKET     *Packet,\r
140   IN     UINT32                Len,\r
141      OUT BOOLEAN               *Completed\r
142   )\r
143 {\r
144   UINT16                    AckNum;\r
145   INTN                      Expected;\r
146   UINT64                    BlockCounter;\r
147 \r
148   *Completed  = FALSE;\r
149   AckNum      = NTOHS (Packet->Ack.Block[0]);\r
150   Expected    = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
151 \r
152   ASSERT (Expected >= 0);\r
153 \r
154   //\r
155   // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput\r
156   // restart receive.\r
157   //\r
158   if (Expected != AckNum) {\r
159     return EFI_SUCCESS;\r
160   }\r
161 \r
162   //\r
163   // Remove the acked block number, if this is the last block number,\r
164   // tell the Mtftp4WrqInput to finish the transfer. This is the last\r
165   // block number if the block range are empty.\r
166   //\r
167   Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum, *Completed, &BlockCounter);\r
168 \r
169   Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
170 \r
171   if (Expected < 0) {\r
172 \r
173     //\r
174     // The block range is empty. It may either because the the last\r
175     // block has been ACKed, or the sequence number just looped back,\r
176     // that is, there is more than 0xffff blocks.\r
177     //\r
178     if (Instance->LastBlock == AckNum) {\r
179       ASSERT (Instance->LastBlock >= 1);\r
180       *Completed = TRUE;\r
181       return EFI_SUCCESS;\r
182 \r
183     } else {\r
184       Mtftp4SendError (\r
185         Instance,\r
186         EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
187         (UINT8 *) "Block number rolls back, not supported, try blksize option"\r
188         );\r
189 \r
190       return EFI_TFTP_ERROR;\r
191     }\r
192   }\r
193 \r
194   return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);\r
195 }\r
196 \r
197 \r
198 /**\r
199   Check whether the received OACK is valid.\r
200 \r
201   The OACK is valid only if:\r
202   1. It only include options requested by us\r
203   2. It can only include a smaller block size\r
204   3. It can't change the proposed time out value.\r
205   4. Other requirements of the individal MTFTP options as required.\r
206 \r
207   @param  Reply                 The options included in the OACK\r
208   @param  Request               The options we requested\r
209 \r
210   @retval TRUE                  The options included in OACK is valid.\r
211   @retval FALSE                 The options included in OACK is invalid.\r
212 \r
213 **/\r
214 BOOLEAN\r
215 Mtftp4WrqOackValid (\r
216   IN MTFTP4_OPTION              *Reply,\r
217   IN MTFTP4_OPTION              *Request\r
218   )\r
219 {\r
220   //\r
221   // It is invalid for server to return options we don't request\r
222   //\r
223   if ((Reply->Exist & ~Request->Exist) != 0) {\r
224     return FALSE;\r
225   }\r
226 \r
227   //\r
228   // Server can only specify a smaller block size to be used and\r
229   // return the timeout matches that requested.\r
230   //\r
231   if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) ||\r
232       (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {\r
233     return FALSE;\r
234   }\r
235 \r
236   return TRUE;\r
237 }\r
238 \r
239 \r
240 /**\r
241   Function to handle the MTFTP OACK packet.\r
242 \r
243   It parses the packet's options, and update the internal states of the session.\r
244 \r
245   @param  Instance              The MTFTP session\r
246   @param  Packet                The received OACK packet\r
247   @param  Len                   The length of the packet\r
248   @param  Completed             Whether the transmisson has completed. NOT used by\r
249                                 this function.\r
250 \r
251   @retval EFI_SUCCESS           The OACK process is OK\r
252   @retval EFI_TFTP_ERROR        Some error occured, and the session reset.\r
253 \r
254 **/\r
255 EFI_STATUS\r
256 Mtftp4WrqHandleOack (\r
257   IN OUT MTFTP4_PROTOCOL       *Instance,\r
258   IN     EFI_MTFTP4_PACKET     *Packet,\r
259   IN     UINT32                Len,\r
260      OUT BOOLEAN               *Completed\r
261   )\r
262 {\r
263   MTFTP4_OPTION             Reply;\r
264   EFI_MTFTP4_PACKET         Bogus;\r
265   EFI_STATUS                Status;\r
266   INTN                      Expected;\r
267 \r
268   *Completed = FALSE;\r
269 \r
270   //\r
271   // Ignore the OACK if already started the upload\r
272   //\r
273   Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
274 \r
275   if (Expected != 0) {\r
276     return EFI_SUCCESS;\r
277   }\r
278 \r
279   //\r
280   // Parse and validate the options from server\r
281   //\r
282   ZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
283   Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);\r
284 \r
285   if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {\r
286     //\r
287     // Don't send a MTFTP error packet when out of resource, it can\r
288     // only make it worse.\r
289     //\r
290     if (Status != EFI_OUT_OF_RESOURCES) {\r
291       Mtftp4SendError (\r
292         Instance,\r
293         EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,\r
294         (UINT8 *) "Mal-formated OACK packet"\r
295         );\r
296     }\r
297 \r
298     return EFI_TFTP_ERROR;\r
299   }\r
300 \r
301   if (Reply.BlkSize != 0) {\r
302     Instance->BlkSize = Reply.BlkSize;\r
303   }\r
304 \r
305   if (Reply.Timeout != 0) {\r
306     Instance->Timeout = Reply.Timeout;\r
307   }\r
308 \r
309   //\r
310   // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,\r
311   // which will start the transmission of the first data block.\r
312   //\r
313   Bogus.Ack.OpCode    = HTONS (EFI_MTFTP4_OPCODE_ACK);\r
314   Bogus.Ack.Block[0]  = 0;\r
315 \r
316   Status = Mtftp4WrqHandleAck (\r
317              Instance,\r
318              &Bogus,\r
319              sizeof (EFI_MTFTP4_ACK_HEADER),\r
320              Completed\r
321              );\r
322 \r
323   return Status;\r
324 }\r
325 \r
326 \r
327 /**\r
328   The input process routine for MTFTP upload.\r
329 \r
330   @param  UdpPacket             The received MTFTP packet.\r
331   @param  EndPoint              The local/remote access point\r
332   @param  IoStatus              The result of the packet receiving\r
333   @param  Context               Opaque parameter for the callback, which is the\r
334                                 MTFTP session.\r
335 **/\r
336 VOID\r
337 EFIAPI\r
338 Mtftp4WrqInput (\r
339   IN NET_BUF                *UdpPacket,\r
340   IN UDP_END_POINT          *EndPoint,\r
341   IN EFI_STATUS             IoStatus,\r
342   IN VOID                   *Context\r
343   )\r
344 {\r
345   MTFTP4_PROTOCOL           *Instance;\r
346   EFI_MTFTP4_PACKET         *Packet;\r
347   BOOLEAN                   Completed;\r
348   EFI_STATUS                Status;\r
349   UINT32                    Len;\r
350   UINT16                    Opcode;\r
351 \r
352   Instance = (MTFTP4_PROTOCOL *) Context;\r
353   NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);\r
354 \r
355   Completed = FALSE;\r
356   Packet    = NULL;\r
357   Status    = EFI_SUCCESS;\r
358 \r
359   if (EFI_ERROR (IoStatus)) {\r
360     Status = IoStatus;\r
361     goto ON_EXIT;\r
362   }\r
363 \r
364   ASSERT (UdpPacket != NULL);\r
365 \r
366   if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {\r
367     goto ON_EXIT;\r
368   }\r
369 \r
370   //\r
371   // Client send initial request to server's listening port. Server\r
372   // will select a UDP port to communicate with the client.\r
373   //\r
374   if (EndPoint->RemotePort != Instance->ConnectedPort) {\r
375     if (Instance->ConnectedPort != 0) {\r
376       goto ON_EXIT;\r
377     } else {\r
378       Instance->ConnectedPort = EndPoint->RemotePort;\r
379     }\r
380   }\r
381 \r
382   //\r
383   // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
384   //\r
385   Len = UdpPacket->TotalSize;\r
386 \r
387   if (UdpPacket->BlockOpNum > 1) {\r
388     Packet = AllocatePool (Len);\r
389 \r
390     if (Packet == NULL) {\r
391       Status = EFI_OUT_OF_RESOURCES;\r
392       goto ON_EXIT;\r
393     }\r
394 \r
395     NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
396 \r
397   } else {\r
398     Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
399     ASSERT (Packet != NULL);\r
400   }\r
401 \r
402   Opcode = NTOHS (Packet->OpCode);\r
403 \r
404   //\r
405   // Call the user's CheckPacket if provided. Abort the transmission\r
406   // if CheckPacket returns an EFI_ERROR code.\r
407   //\r
408   if ((Instance->Token->CheckPacket != NULL) &&\r
409       ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {\r
410 \r
411     Status = Instance->Token->CheckPacket (\r
412                                 &Instance->Mtftp4,\r
413                                 Instance->Token,\r
414                                 (UINT16) Len,\r
415                                 Packet\r
416                                 );\r
417 \r
418     if (EFI_ERROR (Status)) {\r
419       //\r
420       // Send an error message to the server to inform it\r
421       //\r
422       if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {\r
423         Mtftp4SendError (\r
424           Instance,\r
425           EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
426           (UINT8 *) "User aborted the transfer"\r
427           );\r
428       }\r
429 \r
430       Status = EFI_ABORTED;\r
431       goto ON_EXIT;\r
432     }\r
433   }\r
434 \r
435   switch (Opcode) {\r
436   case EFI_MTFTP4_OPCODE_ACK:\r
437     if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {\r
438       goto ON_EXIT;\r
439     }\r
440 \r
441     Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);\r
442     break;\r
443 \r
444   case EFI_MTFTP4_OPCODE_OACK:\r
445     if (Len <= MTFTP4_OPCODE_LEN) {\r
446       goto ON_EXIT;\r
447     }\r
448 \r
449     Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);\r
450     break;\r
451 \r
452   case EFI_MTFTP4_OPCODE_ERROR:\r
453     Status = EFI_TFTP_ERROR;\r
454     break;\r
455 \r
456   default:\r
457     break;\r
458   }\r
459 \r
460 ON_EXIT:\r
461   //\r
462   // Free the resources, then if !EFI_ERROR (Status) and not completed,\r
463   // restart the receive, otherwise end the session.\r
464   //\r
465   if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {\r
466     FreePool (Packet);\r
467   }\r
468 \r
469   if (UdpPacket != NULL) {\r
470     NetbufFree (UdpPacket);\r
471   }\r
472 \r
473   if (!EFI_ERROR (Status) && !Completed) {\r
474     Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
475   }\r
476 \r
477   //\r
478   // Status may have been updated by UdpIoRecvDatagram\r
479   //\r
480   if (EFI_ERROR (Status) || Completed) {\r
481     Mtftp4CleanOperation (Instance, Status);\r
482   }\r
483 }\r
484 \r
485 \r
486 \r
487 /**\r
488   Start the MTFTP session for upload.\r
489 \r
490   It will first init some states, then send the WRQ request packet,\r
491   and start receiving the packet.\r
492 \r
493   @param  Instance              The MTFTP session\r
494   @param  Operation             Redundant parameter, which is always\r
495                                 EFI_MTFTP4_OPCODE_WRQ here.\r
496 \r
497   @retval EFI_SUCCESS           The upload process has been started.\r
498   @retval Others                Failed to start the upload.\r
499 \r
500 **/\r
501 EFI_STATUS\r
502 Mtftp4WrqStart (\r
503   IN MTFTP4_PROTOCOL        *Instance,\r
504   IN UINT16                 Operation\r
505   )\r
506 {\r
507   EFI_STATUS                Status;\r
508 \r
509   //\r
510   // The valid block number range are [0, 0xffff]. For example:\r
511   // the client sends an WRQ request to the server, the server\r
512   // ACK with an ACK0 to let client start transfer the first\r
513   // packet.\r
514   //\r
515   Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);\r
516 \r
517   if (EFI_ERROR (Status)) {\r
518     return Status;\r
519   }\r
520 \r
521   Status = Mtftp4SendRequest (Instance);\r
522 \r
523   if (EFI_ERROR (Status)) {\r
524     return Status;\r
525   }\r
526 \r
527   return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
528 }\r
529 \r