]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/HttpBootDxe/HttpBootClient.c
NetworkPkg/HttpBootDxe: Correct the parameter check for the usage of HttpBootGetFileF...
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootClient.c
1 /** @file
2 Implementation of the boot file download function.
3
4 Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
6 This program and the accompanying materials are licensed and made available under
7 the terms and conditions of the BSD License that accompanies this distribution.
8 The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "HttpBootDxe.h"
17
18 /**
19 Update the device path node to include the boot resource information.
20
21 @param[in] Private The pointer to the driver's private data.
22
23 @retval EFI_SUCCESS Device patch successfully updated.
24 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
25 @retval Others Unexpected error happened.
26
27 **/
28 EFI_STATUS
29 HttpBootUpdateDevicePath (
30 IN HTTP_BOOT_PRIVATE_DATA *Private
31 )
32 {
33 EFI_DEV_PATH *Node;
34 EFI_DEVICE_PATH_PROTOCOL *TmpIpDevicePath;
35 EFI_DEVICE_PATH_PROTOCOL *TmpDnsDevicePath;
36 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
37 UINTN Length;
38 EFI_STATUS Status;
39
40 TmpIpDevicePath = NULL;
41 TmpDnsDevicePath = NULL;
42
43 //
44 // Update the IP node with DHCP assigned information.
45 //
46 if (!Private->UsingIpv6) {
47 Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
48 if (Node == NULL) {
49 return EFI_OUT_OF_RESOURCES;
50 }
51 Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
52 Node->Ipv4.Header.SubType = MSG_IPv4_DP;
53 SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
54 CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
55 Node->Ipv4.RemotePort = Private->Port;
56 Node->Ipv4.Protocol = EFI_IP_PROTO_TCP;
57 Node->Ipv4.StaticIpAddress = FALSE;
58 CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
59 CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
60 } else {
61 Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));
62 if (Node == NULL) {
63 return EFI_OUT_OF_RESOURCES;
64 }
65 Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;
66 Node->Ipv6.Header.SubType = MSG_IPv6_DP;
67 SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));
68 Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH;
69 Node->Ipv6.RemotePort = Private->Port;
70 Node->Ipv6.Protocol = EFI_IP_PROTO_TCP;
71 Node->Ipv6.IpAddressOrigin = 0;
72 CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
73 CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS));
74 CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS));
75 }
76
77 TmpIpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
78 FreePool (Node);
79 if (TmpIpDevicePath == NULL) {
80 return EFI_OUT_OF_RESOURCES;
81 }
82
83 //
84 // Update the DNS node with DNS server IP list if existed.
85 //
86 if (Private->DnsServerIp != NULL) {
87 Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6) + Private->DnsServerCount * sizeof (EFI_IP_ADDRESS);
88 Node = AllocatePool (Length);
89 if (Node == NULL) {
90 FreePool (TmpIpDevicePath);
91 return EFI_OUT_OF_RESOURCES;
92 }
93 Node->DevPath.Type = MESSAGING_DEVICE_PATH;
94 Node->DevPath.SubType = MSG_DNS_DP;
95 SetDevicePathNodeLength (Node, Length);
96 Node->Dns.IsIPv6 = Private->UsingIpv6 ? 0x01 : 0x00;
97 CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6), Private->DnsServerIp, Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
98
99 TmpDnsDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
100 FreePool (Node);
101 FreePool (TmpIpDevicePath);
102 TmpIpDevicePath = NULL;
103 if (TmpDnsDevicePath == NULL) {
104 return EFI_OUT_OF_RESOURCES;
105 }
106 }
107
108 //
109 // Update the URI node with the boot file URI.
110 //
111 Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);
112 Node = AllocatePool (Length);
113 if (Node == NULL) {
114 if (TmpIpDevicePath != NULL) {
115 FreePool (TmpIpDevicePath);
116 }
117 if (TmpDnsDevicePath != NULL) {
118 FreePool (TmpDnsDevicePath);
119 }
120 return EFI_OUT_OF_RESOURCES;
121 }
122 Node->DevPath.Type = MESSAGING_DEVICE_PATH;
123 Node->DevPath.SubType = MSG_URI_DP;
124 SetDevicePathNodeLength (Node, Length);
125 CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));
126
127 if (TmpDnsDevicePath != NULL) {
128 NewDevicePath = AppendDevicePathNode (TmpDnsDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
129 FreePool (TmpDnsDevicePath);
130 } else {
131 ASSERT (TmpIpDevicePath != NULL);
132 NewDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
133 FreePool (TmpIpDevicePath);
134 }
135 FreePool (Node);
136 if (NewDevicePath == NULL) {
137 return EFI_OUT_OF_RESOURCES;
138 }
139
140 if (!Private->UsingIpv6) {
141 //
142 // Reinstall the device path protocol of the child handle.
143 //
144 Status = gBS->ReinstallProtocolInterface (
145 Private->Ip4Nic->Controller,
146 &gEfiDevicePathProtocolGuid,
147 Private->Ip4Nic->DevicePath,
148 NewDevicePath
149 );
150 if (EFI_ERROR (Status)) {
151 return Status;
152 }
153
154 FreePool (Private->Ip4Nic->DevicePath);
155 Private->Ip4Nic->DevicePath = NewDevicePath;
156 } else {
157 //
158 // Reinstall the device path protocol of the child handle.
159 //
160 Status = gBS->ReinstallProtocolInterface (
161 Private->Ip6Nic->Controller,
162 &gEfiDevicePathProtocolGuid,
163 Private->Ip6Nic->DevicePath,
164 NewDevicePath
165 );
166 if (EFI_ERROR (Status)) {
167 return Status;
168 }
169 FreePool (Private->Ip6Nic->DevicePath);
170 Private->Ip6Nic->DevicePath = NewDevicePath;
171 }
172
173 return EFI_SUCCESS;
174 }
175
176 /**
177 Parse the boot file URI information from the selected Dhcp4 offer packet.
178
179 @param[in] Private The pointer to the driver's private data.
180
181 @retval EFI_SUCCESS Successfully parsed out all the boot information.
182 @retval Others Failed to parse out the boot information.
183
184 **/
185 EFI_STATUS
186 HttpBootDhcp4ExtractUriInfo (
187 IN HTTP_BOOT_PRIVATE_DATA *Private
188 )
189 {
190 HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer;
191 HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer;
192 UINT32 SelectIndex;
193 UINT32 ProxyIndex;
194 UINT32 DnsServerIndex;
195 EFI_DHCP4_PACKET_OPTION *Option;
196 EFI_STATUS Status;
197
198 ASSERT (Private != NULL);
199 ASSERT (Private->SelectIndex != 0);
200 SelectIndex = Private->SelectIndex - 1;
201 ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
202
203 DnsServerIndex = 0;
204
205 Status = EFI_SUCCESS;
206
207 //
208 // SelectOffer contains the IP address configuration and name server configuration.
209 // HttpOffer contains the boot file URL.
210 //
211 SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;
212 if (Private->FilePathUri == NULL) {
213 //
214 // In Corporate environment, we need a HttpOffer.
215 //
216 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||
217 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||
218 (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
219 HttpOffer = SelectOffer;
220 } else {
221 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
222 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
223 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;
224 }
225 Private->BootFileUriParser = HttpOffer->UriParser;
226 Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;
227 } else {
228 //
229 // In Home environment the BootFileUri comes from the FilePath.
230 //
231 Private->BootFileUriParser = Private->FilePathUriParser;
232 Private->BootFileUri = Private->FilePathUri;
233 }
234
235 //
236 // Check the URI scheme.
237 //
238 Status = HttpBootCheckUriScheme (Private->BootFileUri);
239 if (EFI_ERROR (Status)) {
240 DEBUG ((EFI_D_ERROR, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status));
241 if (Status == EFI_INVALID_PARAMETER) {
242 AsciiPrint ("\n Error: Invalid URI address.\n");
243 } else if (Status == EFI_ACCESS_DENIED) {
244 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
245 }
246 return Status;
247 }
248
249 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||
250 (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||
251 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {
252 Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
253 ASSERT (Option != NULL);
254
255 //
256 // Record the Dns Server address list.
257 //
258 Private->DnsServerCount = (Option->Length) / sizeof (EFI_IPv4_ADDRESS);
259
260 Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
261 if (Private->DnsServerIp == NULL) {
262 return EFI_OUT_OF_RESOURCES;
263 }
264
265 for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) {
266 CopyMem (&(Private->DnsServerIp[DnsServerIndex].v4), &(((EFI_IPv4_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv4_ADDRESS));
267 }
268
269 //
270 // Configure the default DNS server if server assigned.
271 //
272 Status = HttpBootRegisterIp4Dns (
273 Private,
274 Option->Length,
275 Option->Data
276 );
277 if (EFI_ERROR (Status)) {
278 FreePool (Private->DnsServerIp);
279 Private->DnsServerIp = NULL;
280 return Status;
281 }
282 }
283
284 //
285 // Extract the port from URL, and use default HTTP port 80 if not provided.
286 //
287 Status = HttpUrlGetPort (
288 Private->BootFileUri,
289 Private->BootFileUriParser,
290 &Private->Port
291 );
292 if (EFI_ERROR (Status) || Private->Port == 0) {
293 Private->Port = 80;
294 }
295
296 //
297 // All boot informations are valid here.
298 //
299
300 //
301 // Update the device path to include the boot resource information.
302 //
303 Status = HttpBootUpdateDevicePath (Private);
304 if (EFI_ERROR (Status) && Private->DnsServerIp != NULL) {
305 FreePool (Private->DnsServerIp);
306 Private->DnsServerIp = NULL;
307 }
308
309 return Status;
310 }
311
312 /**
313 Parse the boot file URI information from the selected Dhcp6 offer packet.
314
315 @param[in] Private The pointer to the driver's private data.
316
317 @retval EFI_SUCCESS Successfully parsed out all the boot information.
318 @retval Others Failed to parse out the boot information.
319
320 **/
321 EFI_STATUS
322 HttpBootDhcp6ExtractUriInfo (
323 IN HTTP_BOOT_PRIVATE_DATA *Private
324 )
325 {
326 HTTP_BOOT_DHCP6_PACKET_CACHE *SelectOffer;
327 HTTP_BOOT_DHCP6_PACKET_CACHE *HttpOffer;
328 UINT32 SelectIndex;
329 UINT32 ProxyIndex;
330 UINT32 DnsServerIndex;
331 EFI_DHCP6_PACKET_OPTION *Option;
332 EFI_IPv6_ADDRESS IpAddr;
333 CHAR8 *HostName;
334 UINTN HostNameSize;
335 CHAR16 *HostNameStr;
336 EFI_STATUS Status;
337
338 ASSERT (Private != NULL);
339 ASSERT (Private->SelectIndex != 0);
340 SelectIndex = Private->SelectIndex - 1;
341 ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
342
343 DnsServerIndex = 0;
344
345 Status = EFI_SUCCESS;
346 HostName = NULL;
347 //
348 // SelectOffer contains the IP address configuration and name server configuration.
349 // HttpOffer contains the boot file URL.
350 //
351 SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6;
352 if (Private->FilePathUri == NULL) {
353 //
354 // In Corporate environment, we need a HttpOffer.
355 //
356 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||
357 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||
358 (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
359 HttpOffer = SelectOffer;
360 } else {
361 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
362 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
363 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;
364 }
365 Private->BootFileUriParser = HttpOffer->UriParser;
366 Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;
367 } else {
368 //
369 // In Home environment the BootFileUri comes from the FilePath.
370 //
371 Private->BootFileUriParser = Private->FilePathUriParser;
372 Private->BootFileUri = Private->FilePathUri;
373 }
374
375 //
376 // Check the URI scheme.
377 //
378 Status = HttpBootCheckUriScheme (Private->BootFileUri);
379 if (EFI_ERROR (Status)) {
380 DEBUG ((EFI_D_ERROR, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status));
381 if (Status == EFI_INVALID_PARAMETER) {
382 AsciiPrint ("\n Error: Invalid URI address.\n");
383 } else if (Status == EFI_ACCESS_DENIED) {
384 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
385 }
386 return Status;
387 }
388
389 //
390 // Set the Local station address to IP layer.
391 //
392 Status = HttpBootSetIp6Address (Private);
393 if (EFI_ERROR (Status)) {
394 return Status;
395 }
396
397 //
398 // Register the IPv6 gateway address to the network device.
399 //
400 Status = HttpBootSetIp6Gateway (Private);
401 if (EFI_ERROR (Status)) {
402 return Status;
403 }
404
405 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||
406 (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||
407 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {
408 Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];
409 ASSERT (Option != NULL);
410
411 //
412 // Record the Dns Server address list.
413 //
414 Private->DnsServerCount = HTONS (Option->OpLen) / sizeof (EFI_IPv6_ADDRESS);
415
416 Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
417 if (Private->DnsServerIp == NULL) {
418 return EFI_OUT_OF_RESOURCES;
419 }
420
421 for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) {
422 CopyMem (&(Private->DnsServerIp[DnsServerIndex].v6), &(((EFI_IPv6_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv6_ADDRESS));
423 }
424
425 //
426 // Configure the default DNS server if server assigned.
427 //
428 Status = HttpBootSetIp6Dns (
429 Private,
430 HTONS (Option->OpLen),
431 Option->Data
432 );
433 if (EFI_ERROR (Status)) {
434 goto Error;
435 }
436 }
437
438 //
439 // Extract the HTTP server Ip from URL. This is used to Check route table
440 // whether can send message to HTTP Server Ip through the GateWay.
441 //
442 Status = HttpUrlGetIp6 (
443 Private->BootFileUri,
444 Private->BootFileUriParser,
445 &IpAddr
446 );
447
448 if (EFI_ERROR (Status)) {
449 //
450 // The Http server address is expressed by Name Ip, so perform DNS resolution
451 //
452 Status = HttpUrlGetHostName (
453 Private->BootFileUri,
454 Private->BootFileUriParser,
455 &HostName
456 );
457 if (EFI_ERROR (Status)) {
458 goto Error;
459 }
460
461 HostNameSize = AsciiStrSize (HostName);
462 HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));
463 if (HostNameStr == NULL) {
464 Status = EFI_OUT_OF_RESOURCES;
465 goto Error;
466 }
467
468 AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);
469
470 if (HostName != NULL) {
471 FreePool (HostName);
472 }
473
474 Status = HttpBootDns (Private, HostNameStr, &IpAddr);
475 FreePool (HostNameStr);
476 if (EFI_ERROR (Status)) {
477 goto Error;
478 }
479 }
480
481 CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS));
482
483 //
484 // Extract the port from URL, and use default HTTP port 80 if not provided.
485 //
486 Status = HttpUrlGetPort (
487 Private->BootFileUri,
488 Private->BootFileUriParser,
489 &Private->Port
490 );
491 if (EFI_ERROR (Status) || Private->Port == 0) {
492 Private->Port = 80;
493 }
494
495 //
496 // All boot informations are valid here.
497 //
498
499 //
500 // Update the device path to include the boot resource information.
501 //
502 Status = HttpBootUpdateDevicePath (Private);
503 if (EFI_ERROR (Status)) {
504 goto Error;
505 }
506
507 return Status;
508
509 Error:
510 if (Private->DnsServerIp != NULL) {
511 FreePool (Private->DnsServerIp);
512 Private->DnsServerIp = NULL;
513 }
514
515 return Status;
516 }
517
518
519 /**
520 Discover all the boot information for boot file.
521
522 @param[in, out] Private The pointer to the driver's private data.
523
524 @retval EFI_SUCCESS Successfully obtained all the boot information .
525 @retval Others Failed to retrieve the boot information.
526
527 **/
528 EFI_STATUS
529 HttpBootDiscoverBootInfo (
530 IN OUT HTTP_BOOT_PRIVATE_DATA *Private
531 )
532 {
533 EFI_STATUS Status;
534
535 //
536 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
537 // other Http boot information.
538 //
539 Status = HttpBootDhcp (Private);
540 if (EFI_ERROR (Status)) {
541 return Status;
542 }
543
544 if (!Private->UsingIpv6) {
545 Status = HttpBootDhcp4ExtractUriInfo (Private);
546 } else {
547 Status = HttpBootDhcp6ExtractUriInfo (Private);
548 }
549
550 return Status;
551 }
552
553 /**
554 HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
555
556 @param[in] EventType Indicate the Event type that occurs in the current callback.
557 @param[in] Message HTTP message which will be send to, or just received from HTTP server.
558 @param[in] Context The Callback Context pointer.
559
560 @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
561 @retval Others Tells the HttpIo to abort the current HTTP process.
562 **/
563 EFI_STATUS
564 EFIAPI
565 HttpBootHttpIoCallback (
566 IN HTTP_IO_CALLBACK_EVENT EventType,
567 IN EFI_HTTP_MESSAGE *Message,
568 IN VOID *Context
569 )
570 {
571 HTTP_BOOT_PRIVATE_DATA *Private;
572 EFI_STATUS Status;
573 Private = (HTTP_BOOT_PRIVATE_DATA *) Context;
574 if (Private->HttpBootCallback != NULL) {
575 Status = Private->HttpBootCallback->Callback (
576 Private->HttpBootCallback,
577 EventType == HttpIoRequest ? HttpBootHttpRequest : HttpBootHttpResponse,
578 EventType == HttpIoRequest ? FALSE : TRUE,
579 sizeof (EFI_HTTP_MESSAGE),
580 (VOID *) Message
581 );
582 return Status;
583 }
584 return EFI_SUCCESS;
585 }
586
587 /**
588 Create a HttpIo instance for the file download.
589
590 @param[in] Private The pointer to the driver's private data.
591
592 @retval EFI_SUCCESS Successfully created.
593 @retval Others Failed to create HttpIo.
594
595 **/
596 EFI_STATUS
597 HttpBootCreateHttpIo (
598 IN HTTP_BOOT_PRIVATE_DATA *Private
599 )
600 {
601 HTTP_IO_CONFIG_DATA ConfigData;
602 EFI_STATUS Status;
603 EFI_HANDLE ImageHandle;
604
605 ASSERT (Private != NULL);
606
607 ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));
608 if (!Private->UsingIpv6) {
609 ConfigData.Config4.HttpVersion = HttpVersion11;
610 ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
611 IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);
612 IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);
613 ImageHandle = Private->Ip4Nic->ImageHandle;
614 } else {
615 ConfigData.Config6.HttpVersion = HttpVersion11;
616 ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
617 IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);
618 ImageHandle = Private->Ip6Nic->ImageHandle;
619 }
620
621 Status = HttpIoCreateIo (
622 ImageHandle,
623 Private->Controller,
624 Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,
625 &ConfigData,
626 HttpBootHttpIoCallback,
627 (VOID *) Private,
628 &Private->HttpIo
629 );
630 if (EFI_ERROR (Status)) {
631 return Status;
632 }
633
634 Private->HttpCreated = TRUE;
635 return EFI_SUCCESS;
636 }
637
638 /**
639 Release all the resource of a cache item.
640
641 @param[in] Cache The pointer to the cache item.
642
643 **/
644 VOID
645 HttpBootFreeCache (
646 IN HTTP_BOOT_CACHE_CONTENT *Cache
647 )
648 {
649 UINTN Index;
650 LIST_ENTRY *Entry;
651 LIST_ENTRY *NextEntry;
652 HTTP_BOOT_ENTITY_DATA *EntityData;
653
654 if (Cache != NULL) {
655 //
656 // Free the request data
657 //
658 if (Cache->RequestData != NULL) {
659 if (Cache->RequestData->Url != NULL) {
660 FreePool (Cache->RequestData->Url);
661 }
662 FreePool (Cache->RequestData);
663 }
664
665 //
666 // Free the response header
667 //
668 if (Cache->ResponseData != NULL) {
669 if (Cache->ResponseData->Headers != NULL) {
670 for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {
671 FreePool (Cache->ResponseData->Headers[Index].FieldName);
672 FreePool (Cache->ResponseData->Headers[Index].FieldValue);
673 }
674 FreePool (Cache->ResponseData->Headers);
675 }
676 }
677
678 //
679 // Free the response body
680 //
681 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {
682 EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);
683 if (EntityData->Block != NULL) {
684 FreePool (EntityData->Block);
685 }
686 RemoveEntryList (&EntityData->Link);
687 FreePool (EntityData);
688 }
689
690 FreePool (Cache);
691 }
692 }
693
694 /**
695 Clean up all cached data.
696
697 @param[in] Private The pointer to the driver's private data.
698
699 **/
700 VOID
701 HttpBootFreeCacheList (
702 IN HTTP_BOOT_PRIVATE_DATA *Private
703 )
704 {
705 LIST_ENTRY *Entry;
706 LIST_ENTRY *NextEntry;
707 HTTP_BOOT_CACHE_CONTENT *Cache;
708
709 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {
710 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
711 RemoveEntryList (&Cache->Link);
712 HttpBootFreeCache (Cache);
713 }
714 }
715
716 /**
717 Get the file content from cached data.
718
719 @param[in] Private The pointer to the driver's private data.
720 @param[in] Uri Uri of the file to be retrieved from cache.
721 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
722 code of EFI_SUCCESS, the amount of data transferred to
723 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
724 the size of Buffer required to retrieve the requested file.
725 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
726 then the size of the requested file is returned in
727 BufferSize.
728 @param[out] ImageType The image type of the downloaded file.
729
730 @retval EFI_SUCCESS Successfully created.
731 @retval Others Failed to create HttpIo.
732
733 **/
734 EFI_STATUS
735 HttpBootGetFileFromCache (
736 IN HTTP_BOOT_PRIVATE_DATA *Private,
737 IN CHAR16 *Uri,
738 IN OUT UINTN *BufferSize,
739 OUT UINT8 *Buffer,
740 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
741 )
742 {
743 LIST_ENTRY *Entry;
744 LIST_ENTRY *Entry2;
745 HTTP_BOOT_CACHE_CONTENT *Cache;
746 HTTP_BOOT_ENTITY_DATA *EntityData;
747 UINTN CopyedSize;
748
749 if (Uri == NULL || BufferSize == NULL || Buffer == NULL || ImageType == NULL) {
750 return EFI_INVALID_PARAMETER;
751 }
752
753 NET_LIST_FOR_EACH (Entry, &Private->CacheList) {
754 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
755 //
756 // Compare the URI to see whether we already have a cache for this file.
757 //
758 if ((Cache->RequestData != NULL) &&
759 (Cache->RequestData->Url != NULL) &&
760 (StrCmp (Uri, Cache->RequestData->Url) == 0)) {
761 //
762 // Hit in cache, record image type.
763 //
764 *ImageType = Cache->ImageType;
765
766 //
767 // Check buffer size.
768 //
769 if (*BufferSize < Cache->EntityLength) {
770 *BufferSize = Cache->EntityLength;
771 return EFI_BUFFER_TOO_SMALL;
772 }
773
774 //
775 // Fill data to buffer.
776 //
777 CopyedSize = 0;
778 NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {
779 EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);
780 if (*BufferSize > CopyedSize) {
781 CopyMem (
782 Buffer + CopyedSize,
783 EntityData->DataStart,
784 MIN (EntityData->DataLength, *BufferSize - CopyedSize)
785 );
786 CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);
787 }
788 }
789 *BufferSize = CopyedSize;
790 return EFI_SUCCESS;
791 }
792 }
793
794 return EFI_NOT_FOUND;
795 }
796
797 /**
798 A callback function to intercept events during message parser.
799
800 This function will be invoked during HttpParseMessageBody() with various events type. An error
801 return status of the callback function will cause the HttpParseMessageBody() aborted.
802
803 @param[in] EventType Event type of this callback call.
804 @param[in] Data A pointer to data buffer.
805 @param[in] Length Length in bytes of the Data.
806 @param[in] Context Callback context set by HttpInitMsgParser().
807
808 @retval EFI_SUCCESS Continue to parser the message body.
809 @retval Others Abort the parse.
810
811 **/
812 EFI_STATUS
813 EFIAPI
814 HttpBootGetBootFileCallback (
815 IN HTTP_BODY_PARSE_EVENT EventType,
816 IN CHAR8 *Data,
817 IN UINTN Length,
818 IN VOID *Context
819 )
820 {
821 HTTP_BOOT_CALLBACK_DATA *CallbackData;
822 HTTP_BOOT_ENTITY_DATA *NewEntityData;
823 EFI_STATUS Status;
824 EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;
825
826 //
827 // We only care about the entity data.
828 //
829 if (EventType != BodyParseEventOnData) {
830 return EFI_SUCCESS;
831 }
832
833 CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;
834 HttpBootCallback = CallbackData->Private->HttpBootCallback;
835 if (HttpBootCallback != NULL) {
836 Status = HttpBootCallback->Callback (
837 HttpBootCallback,
838 HttpBootHttpEntityBody,
839 TRUE,
840 (UINT32)Length,
841 Data
842 );
843 if (EFI_ERROR (Status)) {
844 return Status;
845 }
846 }
847 //
848 // Copy data if caller has provided a buffer.
849 //
850 if (CallbackData->BufferSize > CallbackData->CopyedSize) {
851 CopyMem (
852 CallbackData->Buffer + CallbackData->CopyedSize,
853 Data,
854 MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
855 );
856 CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
857 }
858
859 //
860 // The caller doesn't provide a buffer, save the block into cache list.
861 //
862 if (CallbackData->Cache != NULL) {
863 NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
864 if (NewEntityData == NULL) {
865 return EFI_OUT_OF_RESOURCES;
866 }
867 if (CallbackData->NewBlock) {
868 NewEntityData->Block = CallbackData->Block;
869 CallbackData->Block = NULL;
870 }
871 NewEntityData->DataLength = Length;
872 NewEntityData->DataStart = (UINT8*) Data;
873 InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
874 }
875 return EFI_SUCCESS;
876 }
877
878 /**
879 This function download the boot file by using UEFI HTTP protocol.
880
881 @param[in] Private The pointer to the driver's private data.
882 @param[in] HeaderOnly Only request the response header, it could save a lot of time if
883 the caller only want to know the size of the requested file.
884 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
885 code of EFI_SUCCESS, the amount of data transferred to
886 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
887 the size of Buffer required to retrieve the requested file.
888 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
889 then the size of the requested file is returned in
890 BufferSize.
891 @param[out] ImageType The image type of the downloaded file.
892
893 @retval EFI_SUCCESS The file was loaded.
894 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
895 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
896 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
897 BufferSize has been updated with the size needed to complete
898 the request.
899 @retval Others Unexpected error happened.
900
901 **/
902 EFI_STATUS
903 HttpBootGetBootFile (
904 IN HTTP_BOOT_PRIVATE_DATA *Private,
905 IN BOOLEAN HeaderOnly,
906 IN OUT UINTN *BufferSize,
907 OUT UINT8 *Buffer,
908 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
909 )
910 {
911 EFI_STATUS Status;
912 EFI_HTTP_STATUS_CODE StatusCode;
913 CHAR8 *HostName;
914 EFI_HTTP_REQUEST_DATA *RequestData;
915 HTTP_IO_RESPONSE_DATA *ResponseData;
916 HTTP_IO_RESPONSE_DATA ResponseBody;
917 HTTP_IO *HttpIo;
918 HTTP_IO_HEADER *HttpIoHeader;
919 VOID *Parser;
920 HTTP_BOOT_CALLBACK_DATA Context;
921 UINTN ContentLength;
922 HTTP_BOOT_CACHE_CONTENT *Cache;
923 UINT8 *Block;
924 UINTN UrlSize;
925 CHAR16 *Url;
926 BOOLEAN IdentityMode;
927 UINTN ReceivedSize;
928
929 ASSERT (Private != NULL);
930 ASSERT (Private->HttpCreated);
931
932 if (BufferSize == NULL || ImageType == NULL) {
933 return EFI_INVALID_PARAMETER;
934 }
935
936 if (*BufferSize != 0 && Buffer == NULL) {
937 return EFI_INVALID_PARAMETER;
938 }
939
940 //
941 // First, check whether we already cached the requested Uri.
942 //
943 UrlSize = AsciiStrSize (Private->BootFileUri);
944 Url = AllocatePool (UrlSize * sizeof (CHAR16));
945 if (Url == NULL) {
946 return EFI_OUT_OF_RESOURCES;
947 }
948 AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize);
949 if (!HeaderOnly && Buffer != NULL) {
950 Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType);
951 if (Status != EFI_NOT_FOUND) {
952 FreePool (Url);
953 return Status;
954 }
955 }
956
957 //
958 // Not found in cache, try to download it through HTTP.
959 //
960
961 //
962 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.
963 //
964 Cache = NULL;
965 if ((!HeaderOnly) && (*BufferSize == 0)) {
966 Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));
967 if (Cache == NULL) {
968 Status = EFI_OUT_OF_RESOURCES;
969 goto ERROR_1;
970 }
971 Cache->ImageType = ImageTypeMax;
972 InitializeListHead (&Cache->EntityDataList);
973 }
974
975 //
976 // 2. Send HTTP request message.
977 //
978
979 //
980 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
981 // Host
982 // Accept
983 // User-Agent
984 //
985 HttpIoHeader = HttpBootCreateHeader (3);
986 if (HttpIoHeader == NULL) {
987 Status = EFI_OUT_OF_RESOURCES;
988 goto ERROR_2;
989 }
990
991 //
992 // Add HTTP header field 1: Host
993 //
994 HostName = NULL;
995 Status = HttpUrlGetHostName (
996 Private->BootFileUri,
997 Private->BootFileUriParser,
998 &HostName
999 );
1000 if (EFI_ERROR (Status)) {
1001 goto ERROR_3;
1002 }
1003 Status = HttpBootSetHeader (
1004 HttpIoHeader,
1005 HTTP_HEADER_HOST,
1006 HostName
1007 );
1008 FreePool (HostName);
1009 if (EFI_ERROR (Status)) {
1010 goto ERROR_3;
1011 }
1012
1013 //
1014 // Add HTTP header field 2: Accept
1015 //
1016 Status = HttpBootSetHeader (
1017 HttpIoHeader,
1018 HTTP_HEADER_ACCEPT,
1019 "*/*"
1020 );
1021 if (EFI_ERROR (Status)) {
1022 goto ERROR_3;
1023 }
1024
1025 //
1026 // Add HTTP header field 3: User-Agent
1027 //
1028 Status = HttpBootSetHeader (
1029 HttpIoHeader,
1030 HTTP_HEADER_USER_AGENT,
1031 HTTP_USER_AGENT_EFI_HTTP_BOOT
1032 );
1033 if (EFI_ERROR (Status)) {
1034 goto ERROR_3;
1035 }
1036
1037 //
1038 // 2.2 Build the rest of HTTP request info.
1039 //
1040 RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
1041 if (RequestData == NULL) {
1042 Status = EFI_OUT_OF_RESOURCES;
1043 goto ERROR_3;
1044 }
1045 RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;
1046 RequestData->Url = Url;
1047
1048 //
1049 // 2.3 Record the request info in a temp cache item.
1050 //
1051 if (Cache != NULL) {
1052 Cache->RequestData = RequestData;
1053 }
1054
1055 //
1056 // 2.4 Send out the request to HTTP server.
1057 //
1058 HttpIo = &Private->HttpIo;
1059 Status = HttpIoSendRequest (
1060 HttpIo,
1061 RequestData,
1062 HttpIoHeader->HeaderCount,
1063 HttpIoHeader->Headers,
1064 0,
1065 NULL
1066 );
1067 if (EFI_ERROR (Status)) {
1068 goto ERROR_4;
1069 }
1070
1071 //
1072 // 3. Receive HTTP response message.
1073 //
1074
1075 //
1076 // 3.1 First step, use zero BodyLength to only receive the response headers.
1077 //
1078 ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA));
1079 if (ResponseData == NULL) {
1080 Status = EFI_OUT_OF_RESOURCES;
1081 goto ERROR_4;
1082 }
1083 Status = HttpIoRecvResponse (
1084 &Private->HttpIo,
1085 TRUE,
1086 ResponseData
1087 );
1088 if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) {
1089 if (EFI_ERROR (ResponseData->Status)) {
1090 StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;
1091 HttpBootPrintErrorMessage (StatusCode);
1092 Status = ResponseData->Status;
1093 }
1094 goto ERROR_5;
1095 }
1096
1097 //
1098 // Check the image type according to server's response.
1099 //
1100 Status = HttpBootCheckImageType (
1101 Private->BootFileUri,
1102 Private->BootFileUriParser,
1103 ResponseData->HeaderCount,
1104 ResponseData->Headers,
1105 ImageType
1106 );
1107 if (EFI_ERROR (Status)) {
1108 goto ERROR_5;
1109 }
1110
1111 //
1112 // 3.2 Cache the response header.
1113 //
1114 if (Cache != NULL) {
1115 Cache->ResponseData = ResponseData;
1116 Cache->ImageType = *ImageType;
1117 }
1118
1119 //
1120 // 3.3 Init a message-body parser from the header information.
1121 //
1122 Parser = NULL;
1123 Context.NewBlock = FALSE;
1124 Context.Block = NULL;
1125 Context.CopyedSize = 0;
1126 Context.Buffer = Buffer;
1127 Context.BufferSize = *BufferSize;
1128 Context.Cache = Cache;
1129 Context.Private = Private;
1130 Status = HttpInitMsgParser (
1131 HeaderOnly ? HttpMethodHead : HttpMethodGet,
1132 ResponseData->Response.StatusCode,
1133 ResponseData->HeaderCount,
1134 ResponseData->Headers,
1135 HttpBootGetBootFileCallback,
1136 (VOID*) &Context,
1137 &Parser
1138 );
1139 if (EFI_ERROR (Status)) {
1140 goto ERROR_6;
1141 }
1142
1143 //
1144 // 3.4 Continue to receive and parse message-body if needed.
1145 //
1146 Block = NULL;
1147 if (!HeaderOnly) {
1148 //
1149 // 3.4.1, check whether we are in identity transfer-coding.
1150 //
1151 ContentLength = 0;
1152 Status = HttpGetEntityLength (Parser, &ContentLength);
1153 if (!EFI_ERROR (Status)) {
1154 IdentityMode = TRUE;
1155 } else {
1156 IdentityMode = FALSE;
1157 }
1158
1159 //
1160 // 3.4.2, start the message-body download, the identity and chunked transfer-coding
1161 // is handled in different path here.
1162 //
1163 ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA));
1164 if (IdentityMode) {
1165 //
1166 // In identity transfer-coding there is no need to parse the message body,
1167 // just download the message body to the user provided buffer directly.
1168 //
1169 ReceivedSize = 0;
1170 while (ReceivedSize < ContentLength) {
1171 ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize;
1172 ResponseBody.BodyLength = *BufferSize - ReceivedSize;
1173 Status = HttpIoRecvResponse (
1174 &Private->HttpIo,
1175 FALSE,
1176 &ResponseBody
1177 );
1178 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {
1179 if (EFI_ERROR (ResponseBody.Status)) {
1180 Status = ResponseBody.Status;
1181 }
1182 goto ERROR_6;
1183 }
1184 ReceivedSize += ResponseBody.BodyLength;
1185 if (Private->HttpBootCallback != NULL) {
1186 Status = Private->HttpBootCallback->Callback (
1187 Private->HttpBootCallback,
1188 HttpBootHttpEntityBody,
1189 TRUE,
1190 (UINT32)ResponseBody.BodyLength,
1191 ResponseBody.Body
1192 );
1193 if (EFI_ERROR (Status)) {
1194 goto ERROR_6;
1195 }
1196 }
1197 }
1198 } else {
1199 //
1200 // In "chunked" transfer-coding mode, so we need to parse the received
1201 // data to get the real entity content.
1202 //
1203 Block = NULL;
1204 while (!HttpIsMessageComplete (Parser)) {
1205 //
1206 // Allocate a buffer in Block to hold the message-body.
1207 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().
1208 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before
1209 // every HttpIoRecvResponse().
1210 //
1211 if (Block == NULL || Context.BufferSize == 0) {
1212 Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
1213 if (Block == NULL) {
1214 Status = EFI_OUT_OF_RESOURCES;
1215 goto ERROR_6;
1216 }
1217 Context.NewBlock = TRUE;
1218 Context.Block = Block;
1219 } else {
1220 Context.NewBlock = FALSE;
1221 }
1222
1223 ResponseBody.Body = (CHAR8*) Block;
1224 ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
1225 Status = HttpIoRecvResponse (
1226 &Private->HttpIo,
1227 FALSE,
1228 &ResponseBody
1229 );
1230 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {
1231 if (EFI_ERROR (ResponseBody.Status)) {
1232 Status = ResponseBody.Status;
1233 }
1234 goto ERROR_6;
1235 }
1236
1237 //
1238 // Parse the new received block of the message-body, the block will be saved in cache.
1239 //
1240 Status = HttpParseMessageBody (
1241 Parser,
1242 ResponseBody.BodyLength,
1243 ResponseBody.Body
1244 );
1245 if (EFI_ERROR (Status)) {
1246 goto ERROR_6;
1247 }
1248 }
1249 }
1250 }
1251
1252 //
1253 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
1254 //
1255 Status = HttpGetEntityLength (Parser, &ContentLength);
1256 if (EFI_ERROR (Status)) {
1257 goto ERROR_6;
1258 }
1259
1260 if (*BufferSize < ContentLength) {
1261 Status = EFI_BUFFER_TOO_SMALL;
1262 } else {
1263 Status = EFI_SUCCESS;
1264 }
1265 *BufferSize = ContentLength;
1266
1267 //
1268 // 4. Save the cache item to driver's cache list and return.
1269 //
1270 if (Cache != NULL) {
1271 Cache->EntityLength = ContentLength;
1272 InsertTailList (&Private->CacheList, &Cache->Link);
1273 }
1274
1275 if (Parser != NULL) {
1276 HttpFreeMsgParser (Parser);
1277 }
1278
1279 return Status;
1280
1281 ERROR_6:
1282 if (Parser != NULL) {
1283 HttpFreeMsgParser (Parser);
1284 }
1285 if (Context.Block != NULL) {
1286 FreePool (Context.Block);
1287 }
1288 HttpBootFreeCache (Cache);
1289
1290 ERROR_5:
1291 if (ResponseData != NULL) {
1292 FreePool (ResponseData);
1293 }
1294 ERROR_4:
1295 if (RequestData != NULL) {
1296 FreePool (RequestData);
1297 }
1298 ERROR_3:
1299 HttpBootFreeHeader (HttpIoHeader);
1300 ERROR_2:
1301 if (Cache != NULL) {
1302 FreePool (Cache);
1303 }
1304 ERROR_1:
1305 if (Url != NULL) {
1306 FreePool (Url);
1307 }
1308
1309 return Status;
1310 }
1311