1 /** @file 2 Support routines for Mtftp. 3 4 Copyright (c) 2006 - 2015, 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 Allocate a MTFTP4 block range, then init it to the range of [Start, End] 20 21 @param Start The start block number 22 @param End The last block number in the range 23 24 @return Pointer to the created block range, NULL if failed to allocate memory. 25 26 **/ 27 MTFTP4_BLOCK_RANGE * 28 Mtftp4AllocateRange ( 29 IN UINT16 Start, 30 IN UINT16 End 31 ) 32 { 33 MTFTP4_BLOCK_RANGE *Range; 34 35 Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE)); 36 37 if (Range == NULL) { 38 return NULL; 39 } 40 41 InitializeListHead (&Range->Link); 42 Range->Start = Start; 43 Range->End = End; 44 Range->Bound = End; 45 46 return Range; 47 } 48 49 50 /** 51 Initialize the block range for either RRQ or WRQ. 52 53 RRQ and WRQ have different requirements for Start and End. 54 For example, during start up, WRQ initializes its whole valid block range 55 to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us 56 to start the upload. When the client received ACK0, it will remove 0 from the 57 range, get the next block number, which is 1, then upload the BLOCK1. For RRQ 58 without option negotiation, the server will directly send us the BLOCK1 in 59 response to the client's RRQ. When received BLOCK1, the client will remove 60 it from the block range and send an ACK. It also works if there is option 61 negotiation. 62 63 @param Head The block range head to initialize 64 @param Start The Start block number. 65 @param End The last block number. 66 67 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range 68 @retval EFI_SUCCESS The initial block range is created. 69 70 **/ 71 EFI_STATUS 72 Mtftp4InitBlockRange ( 73 IN LIST_ENTRY *Head, 74 IN UINT16 Start, 75 IN UINT16 End 76 ) 77 { 78 MTFTP4_BLOCK_RANGE *Range; 79 80 Range = Mtftp4AllocateRange (Start, End); 81 82 if (Range == NULL) { 83 return EFI_OUT_OF_RESOURCES; 84 } 85 86 InsertTailList (Head, &Range->Link); 87 return EFI_SUCCESS; 88 } 89 90 91 /** 92 Get the first valid block number on the range list. 93 94 @param Head The block range head 95 96 @return The first valid block number, -1 if the block range is empty. 97 98 **/ 99 INTN 100 Mtftp4GetNextBlockNum ( 101 IN LIST_ENTRY *Head 102 ) 103 { 104 MTFTP4_BLOCK_RANGE *Range; 105 106 if (IsListEmpty (Head)) { 107 return -1; 108 } 109 110 Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link); 111 return Range->Start; 112 } 113 114 115 /** 116 Set the last block number of the block range list. 117 118 It will remove all the blocks after the Last. MTFTP initialize the block range 119 to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the 120 last block number, it will call this function to set the last block number. 121 122 @param Head The block range list 123 @param Last The last block number 124 125 **/ 126 VOID 127 Mtftp4SetLastBlockNum ( 128 IN LIST_ENTRY *Head, 129 IN UINT16 Last 130 ) 131 { 132 MTFTP4_BLOCK_RANGE *Range; 133 134 // 135 // Iterate from the tail to head to remove the block number 136 // after the last. 137 // 138 while (!IsListEmpty (Head)) { 139 Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link); 140 141 if (Range->Start > Last) { 142 RemoveEntryList (&Range->Link); 143 FreePool (Range); 144 continue; 145 } 146 147 if (Range->End > Last) { 148 Range->End = Last; 149 } 150 151 return ; 152 } 153 } 154 155 156 /** 157 Remove the block number from the block range list. 158 159 @param Head The block range list to remove from 160 @param Num The block number to remove 161 @param Completed Whether Num is the last block number 162 @param TotalBlock The continuous block number in all 163 164 @retval EFI_NOT_FOUND The block number isn't in the block range list 165 @retval EFI_SUCCESS The block number has been removed from the list 166 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource 167 168 **/ 169 EFI_STATUS 170 Mtftp4RemoveBlockNum ( 171 IN LIST_ENTRY *Head, 172 IN UINT16 Num, 173 IN BOOLEAN Completed, 174 OUT UINT64 *TotalBlock 175 ) 176 { 177 MTFTP4_BLOCK_RANGE *Range; 178 MTFTP4_BLOCK_RANGE *NewRange; 179 LIST_ENTRY *Entry; 180 181 NET_LIST_FOR_EACH (Entry, Head) { 182 183 // 184 // Each block represents a hole [Start, End] in the file, 185 // skip to the first range with End >= Num 186 // 187 Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link); 188 189 if (Range->End < Num) { 190 continue; 191 } 192 193 // 194 // There are three different cases for Start 195 // 1. (Start > Num) && (End >= Num): 196 // because all the holes before this one has the condition of 197 // End < Num, so this block number has been removed. 198 // 199 // 2. (Start == Num) && (End >= Num): 200 // Need to increase the Start by one, and if End == Num, this 201 // hole has been removed completely, remove it. 202 // 203 // 3. (Start < Num) && (End >= Num): 204 // if End == Num, only need to decrease the End by one because 205 // we have (Start < Num) && (Num == End), so (Start <= End - 1). 206 // if (End > Num), the hold is splited into two holes, with 207 // [Start, Num - 1] and [Num + 1, End]. 208 // 209 if (Range->Start > Num) { 210 return EFI_NOT_FOUND; 211 212 } else if (Range->Start == Num) { 213 Range->Start++; 214 215 // 216 // Note that: RFC 1350 does not mention block counter roll-over, 217 // but several TFTP hosts implement the roll-over be able to accept 218 // transfers of unlimited size. There is no consensus, however, whether 219 // the counter should wrap around to zero or to one. Many implementations 220 // wrap to zero, because this is the simplest to implement. Here we choose 221 // this solution. 222 // 223 *TotalBlock = Num; 224 225 if (Range->Round > 0) { 226 *TotalBlock += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1; 227 } 228 229 if (Range->Start > Range->Bound) { 230 Range->Start = 0; 231 Range->Round ++; 232 } 233 234 if ((Range->Start > Range->End) || Completed) { 235 RemoveEntryList (&Range->Link); 236 FreePool (Range); 237 } 238 239 return EFI_SUCCESS; 240 241 } else { 242 if (Range->End == Num) { 243 Range->End--; 244 } else { 245 NewRange = Mtftp4AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End); 246 247 if (NewRange == NULL) { 248 return EFI_OUT_OF_RESOURCES; 249 } 250 251 Range->End = Num - 1; 252 NetListInsertAfter (&Range->Link, &NewRange->Link); 253 } 254 255 return EFI_SUCCESS; 256 } 257 } 258 259 return EFI_NOT_FOUND; 260 } 261 262 263 /** 264 Build then transmit the request packet for the MTFTP session. 265 266 @param Instance The Mtftp session 267 268 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request 269 @retval EFI_SUCCESS The request is built and sent 270 @retval Others Failed to transmit the packet. 271 272 **/ 273 EFI_STATUS 274 Mtftp4SendRequest ( 275 IN MTFTP4_PROTOCOL *Instance 276 ) 277 { 278 EFI_MTFTP4_PACKET *Packet; 279 EFI_MTFTP4_OPTION *Options; 280 EFI_MTFTP4_TOKEN *Token; 281 RETURN_STATUS Status; 282 NET_BUF *Nbuf; 283 UINT8 *Mode; 284 UINT8 *Cur; 285 UINTN Index; 286 UINT32 BufferLength; 287 UINTN FileNameLength; 288 UINTN ModeLength; 289 UINTN OptionStrLength; 290 UINTN ValueStrLength; 291 292 Token = Instance->Token; 293 Options = Token->OptionList; 294 Mode = Instance->Token->ModeStr; 295 296 if (Mode == NULL) { 297 Mode = (UINT8 *) "octet"; 298 } 299 300 // 301 // Compute the packet length 302 // 303 FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename); 304 ModeLength = AsciiStrLen ((CHAR8 *) Mode); 305 BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4; 306 307 for (Index = 0; Index < Token->OptionCount; Index++) { 308 OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr); 309 ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr); 310 BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2; 311 } 312 // 313 // Allocate a packet then copy the data over 314 // 315 if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) { 316 return EFI_OUT_OF_RESOURCES; 317 } 318 319 Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE); 320 ASSERT (Packet != NULL); 321 322 Packet->OpCode = HTONS (Instance->Operation); 323 BufferLength -= sizeof (Packet->OpCode); 324 325 Cur = Packet->Rrq.Filename; 326 Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename); 327 ASSERT_EFI_ERROR (Status); 328 BufferLength -= (UINT32) (FileNameLength + 1); 329 Cur += FileNameLength + 1; 330 Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode); 331 ASSERT_EFI_ERROR (Status); 332 BufferLength -= (UINT32) (ModeLength + 1); 333 Cur += ModeLength + 1; 334 335 for (Index = 0; Index < Token->OptionCount; ++Index) { 336 OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr); 337 ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr); 338 339 Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr); 340 ASSERT_EFI_ERROR (Status); 341 BufferLength -= (UINT32) (OptionStrLength + 1); 342 Cur += OptionStrLength + 1; 343 344 Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr); 345 ASSERT_EFI_ERROR (Status); 346 BufferLength -= (UINT32) (ValueStrLength + 1); 347 Cur += ValueStrLength + 1; 348 349 } 350 351 return Mtftp4SendPacket (Instance, Nbuf); 352 } 353 354 355 /** 356 Build then send an error message. 357 358 @param Instance The MTFTP session 359 @param ErrCode The error code 360 @param ErrInfo The error message 361 362 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet 363 @retval EFI_SUCCESS The error packet is transmitted. 364 @retval Others Failed to transmit the packet. 365 366 **/ 367 EFI_STATUS 368 Mtftp4SendError ( 369 IN MTFTP4_PROTOCOL *Instance, 370 IN UINT16 ErrCode, 371 IN UINT8 *ErrInfo 372 ) 373 { 374 NET_BUF *Packet; 375 EFI_MTFTP4_PACKET *TftpError; 376 UINT32 Len; 377 378 Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER)); 379 Packet = NetbufAlloc (Len); 380 if (Packet == NULL) { 381 return EFI_OUT_OF_RESOURCES; 382 } 383 384 TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE); 385 ASSERT (TftpError != NULL); 386 387 TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR); 388 TftpError->Error.ErrorCode = HTONS (ErrCode); 389 390 AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, Len, (CHAR8 *) ErrInfo); 391 392 return Mtftp4SendPacket (Instance, Packet); 393 } 394 395 396 /** 397 The callback function called when the packet is transmitted. 398 399 It simply frees the packet. 400 401 @param Packet The transmitted (or failed to) packet 402 @param EndPoint The local and remote UDP access point 403 @param IoStatus The result of the transmission 404 @param Context Opaque parameter to the callback 405 406 **/ 407 VOID 408 EFIAPI 409 Mtftp4OnPacketSent ( 410 IN NET_BUF *Packet, 411 IN UDP_END_POINT *EndPoint, 412 IN EFI_STATUS IoStatus, 413 IN VOID *Context 414 ) 415 { 416 NetbufFree (Packet); 417 } 418 419 420 /** 421 Set the timeout for the instance. User a longer time for passive instances. 422 423 @param Instance The Mtftp session to set time out 424 425 **/ 426 VOID 427 Mtftp4SetTimeout ( 428 IN OUT MTFTP4_PROTOCOL *Instance 429 ) 430 { 431 if (Instance->Master) { 432 Instance->PacketToLive = Instance->Timeout; 433 } else { 434 Instance->PacketToLive = Instance->Timeout * 2; 435 } 436 } 437 438 439 /** 440 Send the packet for the instance. 441 442 It will first save a reference to the packet for later retransmission. 443 Then determine the destination port, listen port for requests, and connected 444 port for others. At last, send the packet out. 445 446 @param Instance The Mtftp instance 447 @param Packet The packet to send 448 449 @retval EFI_SUCCESS The packet is sent out 450 @retval Others Failed to transmit the packet. 451 452 **/ 453 EFI_STATUS 454 Mtftp4SendPacket ( 455 IN OUT MTFTP4_PROTOCOL *Instance, 456 IN OUT NET_BUF *Packet 457 ) 458 { 459 UDP_END_POINT UdpPoint; 460 EFI_STATUS Status; 461 UINT16 OpCode; 462 UINT8 *Buffer; 463 464 // 465 // Save the packet for retransmission 466 // 467 if (Instance->LastPacket != NULL) { 468 NetbufFree (Instance->LastPacket); 469 } 470 471 Instance->LastPacket = Packet; 472 473 Instance->CurRetry = 0; 474 Mtftp4SetTimeout (Instance); 475 476 ZeroMem (&UdpPoint, sizeof (UdpPoint)); 477 UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp; 478 479 // 480 // Send the requests to the listening port, other packets 481 // to the connected port 482 // 483 Buffer = NetbufGetByte (Packet, 0, NULL); 484 ASSERT (Buffer != NULL); 485 OpCode = NTOHS (*(UINT16 *)Buffer); 486 487 if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || 488 (OpCode == EFI_MTFTP4_OPCODE_DIR) || 489 (OpCode == EFI_MTFTP4_OPCODE_WRQ)) { 490 UdpPoint.RemotePort = Instance->ListeningPort; 491 } else { 492 UdpPoint.RemotePort = Instance->ConnectedPort; 493 } 494 495 NET_GET_REF (Packet); 496 497 Status = UdpIoSendDatagram ( 498 Instance->UnicastPort, 499 Packet, 500 &UdpPoint, 501 NULL, 502 Mtftp4OnPacketSent, 503 Instance 504 ); 505 506 if (EFI_ERROR (Status)) { 507 NET_PUT_REF (Packet); 508 } 509 510 return Status; 511 } 512 513 514 /** 515 Retransmit the last packet for the instance. 516 517 @param Instance The Mtftp instance 518 519 @retval EFI_SUCCESS The last packet is retransmitted. 520 @retval Others Failed to retransmit. 521 522 **/ 523 EFI_STATUS 524 Mtftp4Retransmit ( 525 IN MTFTP4_PROTOCOL *Instance 526 ) 527 { 528 UDP_END_POINT UdpPoint; 529 EFI_STATUS Status; 530 UINT16 OpCode; 531 UINT8 *Buffer; 532 533 ASSERT (Instance->LastPacket != NULL); 534 535 ZeroMem (&UdpPoint, sizeof (UdpPoint)); 536 UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp; 537 538 // 539 // Set the requests to the listening port, other packets to the connected port 540 // 541 Buffer = NetbufGetByte (Instance->LastPacket, 0, NULL); 542 ASSERT (Buffer != NULL); 543 OpCode = NTOHS (*(UINT16 *) Buffer); 544 545 if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) || 546 (OpCode == EFI_MTFTP4_OPCODE_WRQ)) { 547 UdpPoint.RemotePort = Instance->ListeningPort; 548 } else { 549 UdpPoint.RemotePort = Instance->ConnectedPort; 550 } 551 552 NET_GET_REF (Instance->LastPacket); 553 554 Status = UdpIoSendDatagram ( 555 Instance->UnicastPort, 556 Instance->LastPacket, 557 &UdpPoint, 558 NULL, 559 Mtftp4OnPacketSent, 560 Instance 561 ); 562 563 if (EFI_ERROR (Status)) { 564 NET_PUT_REF (Instance->LastPacket); 565 } 566 567 return Status; 568 } 569 570 571 /** 572 The timer ticking function for the Mtftp service instance. 573 574 @param Event The ticking event 575 @param Context The Mtftp service instance 576 577 **/ 578 VOID 579 EFIAPI 580 Mtftp4OnTimerTick ( 581 IN EFI_EVENT Event, 582 IN VOID *Context 583 ) 584 { 585 MTFTP4_SERVICE *MtftpSb; 586 LIST_ENTRY *Entry; 587 LIST_ENTRY *Next; 588 MTFTP4_PROTOCOL *Instance; 589 EFI_MTFTP4_TOKEN *Token; 590 591 MtftpSb = (MTFTP4_SERVICE *) Context; 592 593 // 594 // Iterate through all the children of the Mtftp service instance. Time 595 // out the packet. If maximum retries reached, clean the session up. 596 // 597 NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) { 598 Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link); 599 600 if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) { 601 continue; 602 } 603 604 // 605 // Call the user's time out handler 606 // 607 Token = Instance->Token; 608 609 if ((Token->TimeoutCallback != NULL) && 610 EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) { 611 612 Mtftp4SendError ( 613 Instance, 614 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, 615 (UINT8 *) "User aborted the transfer in time out" 616 ); 617 618 Mtftp4CleanOperation (Instance, EFI_ABORTED); 619 continue; 620 } 621 622 // 623 // Retransmit the packet if haven't reach the maxmium retry count, 624 // otherwise exit the transfer. 625 // 626 if (++Instance->CurRetry < Instance->MaxRetry) { 627 Mtftp4Retransmit (Instance); 628 Mtftp4SetTimeout (Instance); 629 } else { 630 Mtftp4CleanOperation (Instance, EFI_TIMEOUT); 631 continue; 632 } 633 } 634 } 635