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