Mtftp4Support.c 17 KB

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