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