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