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