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