]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/HttpBootDxe/HttpBootClient.c
MdeModulePkg/BootLogoLib: Use Boot Logo 2 Protocol
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootClient.c
1 /** @file
2 Implementation of the boot file download function.
3
4 Copyright (c) 2015 - 2017, 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 frome 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 == 0 || 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 //
763 // Hit in cache, record image type.
764 //
765 *ImageType = Cache->ImageType;
766
767 //
768 // Check buffer size.
769 //
770 if (*BufferSize < Cache->EntityLength) {
771 *BufferSize = Cache->EntityLength;
772 return EFI_BUFFER_TOO_SMALL;
773 }
774
775 //
776 // Fill data to buffer.
777 //
778 CopyedSize = 0;
779 NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {
780 EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);
781 if (*BufferSize > CopyedSize) {
782 CopyMem (
783 Buffer + CopyedSize,
784 EntityData->DataStart,
785 MIN (EntityData->DataLength, *BufferSize - CopyedSize)
786 );
787 CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);
788 }
789 }
790 *BufferSize = CopyedSize;
791 return EFI_SUCCESS;
792 }
793 }
794
795 return EFI_NOT_FOUND;
796 }
797
798 /**
799 A callback function to intercept events during message parser.
800
801 This function will be invoked during HttpParseMessageBody() with various events type. An error
802 return status of the callback function will cause the HttpParseMessageBody() aborted.
803
804 @param[in] EventType Event type of this callback call.
805 @param[in] Data A pointer to data buffer.
806 @param[in] Length Length in bytes of the Data.
807 @param[in] Context Callback context set by HttpInitMsgParser().
808
809 @retval EFI_SUCCESS Continue to parser the message body.
810 @retval Others Abort the parse.
811
812 **/
813 EFI_STATUS
814 EFIAPI
815 HttpBootGetBootFileCallback (
816 IN HTTP_BODY_PARSE_EVENT EventType,
817 IN CHAR8 *Data,
818 IN UINTN Length,
819 IN VOID *Context
820 )
821 {
822 HTTP_BOOT_CALLBACK_DATA *CallbackData;
823 HTTP_BOOT_ENTITY_DATA *NewEntityData;
824 EFI_STATUS Status;
825 EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;
826
827 //
828 // We only care about the entity data.
829 //
830 if (EventType != BodyParseEventOnData) {
831 return EFI_SUCCESS;
832 }
833
834 CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;
835 HttpBootCallback = CallbackData->Private->HttpBootCallback;
836 if (HttpBootCallback != NULL) {
837 Status = HttpBootCallback->Callback (
838 HttpBootCallback,
839 HttpBootHttpEntityBody,
840 TRUE,
841 (UINT32)Length,
842 Data
843 );
844 if (EFI_ERROR (Status)) {
845 return Status;
846 }
847 }
848 //
849 // Copy data if caller has provided a buffer.
850 //
851 if (CallbackData->BufferSize > CallbackData->CopyedSize) {
852 CopyMem (
853 CallbackData->Buffer + CallbackData->CopyedSize,
854 Data,
855 MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
856 );
857 CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
858 }
859
860 //
861 // The caller doesn't provide a buffer, save the block into cache list.
862 //
863 if (CallbackData->Cache != NULL) {
864 NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
865 if (NewEntityData == NULL) {
866 return EFI_OUT_OF_RESOURCES;
867 }
868 if (CallbackData->NewBlock) {
869 NewEntityData->Block = CallbackData->Block;
870 CallbackData->Block = NULL;
871 }
872 NewEntityData->DataLength = Length;
873 NewEntityData->DataStart = (UINT8*) Data;
874 InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
875 }
876 return EFI_SUCCESS;
877 }
878
879 /**
880 This function download the boot file by using UEFI HTTP protocol.
881
882 @param[in] Private The pointer to the driver's private data.
883 @param[in] HeaderOnly Only request the response header, it could save a lot of time if
884 the caller only want to know the size of the requested file.
885 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
886 code of EFI_SUCCESS, the amount of data transferred to
887 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
888 the size of Buffer required to retrieve the requested file.
889 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
890 then the size of the requested file is returned in
891 BufferSize.
892 @param[out] ImageType The image type of the downloaded file.
893
894 @retval EFI_SUCCESS The file was loaded.
895 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
896 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
897 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
898 BufferSize has been updated with the size needed to complete
899 the request.
900 @retval Others Unexpected error happened.
901
902 **/
903 EFI_STATUS
904 HttpBootGetBootFile (
905 IN HTTP_BOOT_PRIVATE_DATA *Private,
906 IN BOOLEAN HeaderOnly,
907 IN OUT UINTN *BufferSize,
908 OUT UINT8 *Buffer,
909 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
910 )
911 {
912 EFI_STATUS Status;
913 EFI_HTTP_STATUS_CODE StatusCode;
914 CHAR8 *HostName;
915 EFI_HTTP_REQUEST_DATA *RequestData;
916 HTTP_IO_RESPONSE_DATA *ResponseData;
917 HTTP_IO_RESPONSE_DATA ResponseBody;
918 HTTP_IO *HttpIo;
919 HTTP_IO_HEADER *HttpIoHeader;
920 VOID *Parser;
921 HTTP_BOOT_CALLBACK_DATA Context;
922 UINTN ContentLength;
923 HTTP_BOOT_CACHE_CONTENT *Cache;
924 UINT8 *Block;
925 UINTN UrlSize;
926 CHAR16 *Url;
927 BOOLEAN IdentityMode;
928 UINTN ReceivedSize;
929
930 ASSERT (Private != NULL);
931 ASSERT (Private->HttpCreated);
932
933 if (BufferSize == NULL || ImageType == NULL) {
934 return EFI_INVALID_PARAMETER;
935 }
936
937 if (*BufferSize != 0 && Buffer == NULL) {
938 return EFI_INVALID_PARAMETER;
939 }
940
941 //
942 // First, check whether we already cached the requested Uri.
943 //
944 UrlSize = AsciiStrSize (Private->BootFileUri);
945 Url = AllocatePool (UrlSize * sizeof (CHAR16));
946 if (Url == NULL) {
947 return EFI_OUT_OF_RESOURCES;
948 }
949 AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize);
950 if (!HeaderOnly) {
951 Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType);
952 if (Status != EFI_NOT_FOUND) {
953 FreePool (Url);
954 return Status;
955 }
956 }
957
958 //
959 // Not found in cache, try to download it through HTTP.
960 //
961
962 //
963 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.
964 //
965 Cache = NULL;
966 if ((!HeaderOnly) && (*BufferSize == 0)) {
967 Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));
968 if (Cache == NULL) {
969 Status = EFI_OUT_OF_RESOURCES;
970 goto ERROR_1;
971 }
972 Cache->ImageType = ImageTypeMax;
973 InitializeListHead (&Cache->EntityDataList);
974 }
975
976 //
977 // 2. Send HTTP request message.
978 //
979
980 //
981 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
982 // Host
983 // Accept
984 // User-Agent
985 //
986 HttpIoHeader = HttpBootCreateHeader (3);
987 if (HttpIoHeader == NULL) {
988 Status = EFI_OUT_OF_RESOURCES;
989 goto ERROR_2;
990 }
991
992 //
993 // Add HTTP header field 1: Host
994 //
995 HostName = NULL;
996 Status = HttpUrlGetHostName (
997 Private->BootFileUri,
998 Private->BootFileUriParser,
999 &HostName
1000 );
1001 if (EFI_ERROR (Status)) {
1002 goto ERROR_3;
1003 }
1004 Status = HttpBootSetHeader (
1005 HttpIoHeader,
1006 HTTP_HEADER_HOST,
1007 HostName
1008 );
1009 FreePool (HostName);
1010 if (EFI_ERROR (Status)) {
1011 goto ERROR_3;
1012 }
1013
1014 //
1015 // Add HTTP header field 2: Accept
1016 //
1017 Status = HttpBootSetHeader (
1018 HttpIoHeader,
1019 HTTP_HEADER_ACCEPT,
1020 "*/*"
1021 );
1022 if (EFI_ERROR (Status)) {
1023 goto ERROR_3;
1024 }
1025
1026 //
1027 // Add HTTP header field 3: User-Agent
1028 //
1029 Status = HttpBootSetHeader (
1030 HttpIoHeader,
1031 HTTP_HEADER_USER_AGENT,
1032 HTTP_USER_AGENT_EFI_HTTP_BOOT
1033 );
1034 if (EFI_ERROR (Status)) {
1035 goto ERROR_3;
1036 }
1037
1038 //
1039 // 2.2 Build the rest of HTTP request info.
1040 //
1041 RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
1042 if (RequestData == NULL) {
1043 Status = EFI_OUT_OF_RESOURCES;
1044 goto ERROR_3;
1045 }
1046 RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;
1047 RequestData->Url = Url;
1048
1049 //
1050 // 2.3 Record the request info in a temp cache item.
1051 //
1052 if (Cache != NULL) {
1053 Cache->RequestData = RequestData;
1054 }
1055
1056 //
1057 // 2.4 Send out the request to HTTP server.
1058 //
1059 HttpIo = &Private->HttpIo;
1060 Status = HttpIoSendRequest (
1061 HttpIo,
1062 RequestData,
1063 HttpIoHeader->HeaderCount,
1064 HttpIoHeader->Headers,
1065 0,
1066 NULL
1067 );
1068 if (EFI_ERROR (Status)) {
1069 goto ERROR_4;
1070 }
1071
1072 //
1073 // 3. Receive HTTP response message.
1074 //
1075
1076 //
1077 // 3.1 First step, use zero BodyLength to only receive the response headers.
1078 //
1079 ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA));
1080 if (ResponseData == NULL) {
1081 Status = EFI_OUT_OF_RESOURCES;
1082 goto ERROR_4;
1083 }
1084 Status = HttpIoRecvResponse (
1085 &Private->HttpIo,
1086 TRUE,
1087 ResponseData
1088 );
1089 if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) {
1090 if (EFI_ERROR (ResponseData->Status)) {
1091 StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;
1092 HttpBootPrintErrorMessage (StatusCode);
1093 Status = ResponseData->Status;
1094 }
1095 goto ERROR_5;
1096 }
1097
1098 //
1099 // Check the image type according to server's response.
1100 //
1101 Status = HttpBootCheckImageType (
1102 Private->BootFileUri,
1103 Private->BootFileUriParser,
1104 ResponseData->HeaderCount,
1105 ResponseData->Headers,
1106 ImageType
1107 );
1108 if (EFI_ERROR (Status)) {
1109 goto ERROR_5;
1110 }
1111
1112 //
1113 // 3.2 Cache the response header.
1114 //
1115 if (Cache != NULL) {
1116 Cache->ResponseData = ResponseData;
1117 Cache->ImageType = *ImageType;
1118 }
1119
1120 //
1121 // 3.3 Init a message-body parser from the header information.
1122 //
1123 Parser = NULL;
1124 Context.NewBlock = FALSE;
1125 Context.Block = NULL;
1126 Context.CopyedSize = 0;
1127 Context.Buffer = Buffer;
1128 Context.BufferSize = *BufferSize;
1129 Context.Cache = Cache;
1130 Context.Private = Private;
1131 Status = HttpInitMsgParser (
1132 HeaderOnly? HttpMethodHead : HttpMethodGet,
1133 ResponseData->Response.StatusCode,
1134 ResponseData->HeaderCount,
1135 ResponseData->Headers,
1136 HttpBootGetBootFileCallback,
1137 (VOID*) &Context,
1138 &Parser
1139 );
1140 if (EFI_ERROR (Status)) {
1141 goto ERROR_6;
1142 }
1143
1144 //
1145 // 3.4 Continue to receive and parse message-body if needed.
1146 //
1147 Block = NULL;
1148 if (!HeaderOnly) {
1149 //
1150 // 3.4.1, check whether we are in identity transfer-coding.
1151 //
1152 ContentLength = 0;
1153 Status = HttpGetEntityLength (Parser, &ContentLength);
1154 if (!EFI_ERROR (Status)) {
1155 IdentityMode = TRUE;
1156 } else {
1157 IdentityMode = FALSE;
1158 }
1159
1160 //
1161 // 3.4.2, start the message-body download, the identity and chunked transfer-coding
1162 // is handled in different path here.
1163 //
1164 ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA));
1165 if (IdentityMode) {
1166 //
1167 // In identity transfer-coding there is no need to parse the message body,
1168 // just download the message body to the user provided buffer directly.
1169 //
1170 ReceivedSize = 0;
1171 while (ReceivedSize < ContentLength) {
1172 ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize;
1173 ResponseBody.BodyLength = *BufferSize - ReceivedSize;
1174 Status = HttpIoRecvResponse (
1175 &Private->HttpIo,
1176 FALSE,
1177 &ResponseBody
1178 );
1179 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {
1180 if (EFI_ERROR (ResponseBody.Status)) {
1181 Status = ResponseBody.Status;
1182 }
1183 goto ERROR_6;
1184 }
1185 ReceivedSize += ResponseBody.BodyLength;
1186 if (Private->HttpBootCallback != NULL) {
1187 Status = Private->HttpBootCallback->Callback (
1188 Private->HttpBootCallback,
1189 HttpBootHttpEntityBody,
1190 TRUE,
1191 (UINT32)ResponseBody.BodyLength,
1192 ResponseBody.Body
1193 );
1194 if (EFI_ERROR (Status)) {
1195 goto ERROR_6;
1196 }
1197 }
1198 }
1199 } else {
1200 //
1201 // In "chunked" transfer-coding mode, so we need to parse the received
1202 // data to get the real entity content.
1203 //
1204 Block = NULL;
1205 while (!HttpIsMessageComplete (Parser)) {
1206 //
1207 // Allocate a buffer in Block to hold the message-body.
1208 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().
1209 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before
1210 // every HttpIoRecvResponse().
1211 //
1212 if (Block == NULL || Context.BufferSize == 0) {
1213 Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
1214 if (Block == NULL) {
1215 Status = EFI_OUT_OF_RESOURCES;
1216 goto ERROR_6;
1217 }
1218 Context.NewBlock = TRUE;
1219 Context.Block = Block;
1220 } else {
1221 Context.NewBlock = FALSE;
1222 }
1223
1224 ResponseBody.Body = (CHAR8*) Block;
1225 ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
1226 Status = HttpIoRecvResponse (
1227 &Private->HttpIo,
1228 FALSE,
1229 &ResponseBody
1230 );
1231 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {
1232 if (EFI_ERROR (ResponseBody.Status)) {
1233 Status = ResponseBody.Status;
1234 }
1235 goto ERROR_6;
1236 }
1237
1238 //
1239 // Parse the new received block of the message-body, the block will be saved in cache.
1240 //
1241 Status = HttpParseMessageBody (
1242 Parser,
1243 ResponseBody.BodyLength,
1244 ResponseBody.Body
1245 );
1246 if (EFI_ERROR (Status)) {
1247 goto ERROR_6;
1248 }
1249 }
1250 }
1251 }
1252
1253 //
1254 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
1255 //
1256 Status = HttpGetEntityLength (Parser, &ContentLength);
1257 if (EFI_ERROR (Status)) {
1258 goto ERROR_6;
1259 }
1260
1261 if (*BufferSize < ContentLength) {
1262 Status = EFI_BUFFER_TOO_SMALL;
1263 } else {
1264 Status = EFI_SUCCESS;
1265 }
1266 *BufferSize = ContentLength;
1267
1268 //
1269 // 4. Save the cache item to driver's cache list and return.
1270 //
1271 if (Cache != NULL) {
1272 Cache->EntityLength = ContentLength;
1273 InsertTailList (&Private->CacheList, &Cache->Link);
1274 }
1275
1276 if (Parser != NULL) {
1277 HttpFreeMsgParser (Parser);
1278 }
1279
1280 return Status;
1281
1282 ERROR_6:
1283 if (Parser != NULL) {
1284 HttpFreeMsgParser (Parser);
1285 }
1286 if (Context.Block != NULL) {
1287 FreePool (Context.Block);
1288 }
1289 HttpBootFreeCache (Cache);
1290
1291 ERROR_5:
1292 if (ResponseData != NULL) {
1293 FreePool (ResponseData);
1294 }
1295 ERROR_4:
1296 if (RequestData != NULL) {
1297 FreePool (RequestData);
1298 }
1299 ERROR_3:
1300 HttpBootFreeHeader (HttpIoHeader);
1301 ERROR_2:
1302 if (Cache != NULL) {
1303 FreePool (Cache);
1304 }
1305 ERROR_1:
1306 if (Url != NULL) {
1307 FreePool (Url);
1308 }
1309
1310 return Status;
1311 }
1312