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