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