]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/HttpBootDxe/HttpBootClient.c
NetworkPkg/HttpBootDxe: Correct the parameter check for the usage of HttpBootGetFileF...
[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
d933e70a
JW
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
8The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php. \r
10 \r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
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
26 \r
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
d933e70a
JW
42 \r
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
70 Node->Ipv6.Protocol = EFI_IP_PROTO_TCP; \r
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
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
98 \r
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
153 \r
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
JW
171 }\r
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
JW
203 DnsServerIndex = 0;\r
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
216 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || \r
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
fa848a40
FS
249 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || \r
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
268 \r
269 //\r
270 // Configure the default DNS server if server assigned.\r
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
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
fa848a40
FS
405 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || \r
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
437 \r
438 //\r
951c6e63 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
447 \r
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
467 \r
b9679cd7 468 AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);\r
7b1dbd15
JW
469\r
470 if (HostName != NULL) {\r
471 FreePool (HostName);\r
472 }\r
473 \r
b659408b
ZL
474 Status = HttpBootDns (Private, HostNameStr, &IpAddr);\r
475 FreePool (HostNameStr);\r
476 if (EFI_ERROR (Status)) {\r
477 goto Error;\r
478 } \r
479 } \r
480 \r
08c6239d 481 CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS));\r
b659408b
ZL
482 \r
483 //\r
484 // Extract the port from URL, and use default HTTP port 80 if not provided.\r
485 //\r
486 Status = HttpUrlGetPort (\r
fa848a40
FS
487 Private->BootFileUri,\r
488 Private->BootFileUriParser,\r
b659408b
ZL
489 &Private->Port\r
490 );\r
491 if (EFI_ERROR (Status) || Private->Port == 0) {\r
492 Private->Port = 80;\r
493 }\r
494 \r
b659408b
ZL
495 //\r
496 // All boot informations are valid here.\r
497 //\r
95b5c32f 498\r
b659408b 499 //\r
7b1dbd15 500 // Update the device path to include the boot resource information.\r
b659408b
ZL
501 //\r
502 Status = HttpBootUpdateDevicePath (Private);\r
7b1dbd15
JW
503 if (EFI_ERROR (Status)) {\r
504 goto Error;\r
505 }\r
506 \r
507 return Status;\r
b659408b
ZL
508\r
509Error:\r
7b1dbd15
JW
510 if (Private->DnsServerIp != NULL) {\r
511 FreePool (Private->DnsServerIp);\r
512 Private->DnsServerIp = NULL;\r
b659408b 513 }\r
7b1dbd15 514 \r
b659408b
ZL
515 return Status;\r
516}\r
517\r
518\r
d933e70a
JW
519/**\r
520 Discover all the boot information for boot file.\r
521\r
522 @param[in, out] Private The pointer to the driver's private data.\r
523\r
524 @retval EFI_SUCCESS Successfully obtained all the boot information .\r
525 @retval Others Failed to retrieve the boot information.\r
526\r
527**/\r
528EFI_STATUS\r
529HttpBootDiscoverBootInfo (\r
530 IN OUT HTTP_BOOT_PRIVATE_DATA *Private\r
531 )\r
532{\r
533 EFI_STATUS Status;\r
534 \r
535 //\r
536 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and\r
537 // other Http boot information.\r
538 //\r
539 Status = HttpBootDhcp (Private);\r
540 if (EFI_ERROR (Status)) {\r
541 return Status;\r
542 }\r
543\r
544 if (!Private->UsingIpv6) {\r
b659408b 545 Status = HttpBootDhcp4ExtractUriInfo (Private);\r
d933e70a 546 } else {\r
b659408b 547 Status = HttpBootDhcp6ExtractUriInfo (Private);\r
d933e70a
JW
548 }\r
549\r
550 return Status;\r
551}\r
552\r
95b5c32f
FS
553/**\r
554 HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.\r
555\r
556 @param[in] EventType Indicate the Event type that occurs in the current callback.\r
557 @param[in] Message HTTP message which will be send to, or just received from HTTP server.\r
558 @param[in] Context The Callback Context pointer.\r
559 \r
560 @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.\r
561 @retval Others Tells the HttpIo to abort the current HTTP process.\r
562**/\r
563EFI_STATUS\r
564EFIAPI\r
565HttpBootHttpIoCallback (\r
566 IN HTTP_IO_CALLBACK_EVENT EventType,\r
567 IN EFI_HTTP_MESSAGE *Message,\r
568 IN VOID *Context\r
569 )\r
570{\r
571 HTTP_BOOT_PRIVATE_DATA *Private;\r
572 EFI_STATUS Status;\r
573 Private = (HTTP_BOOT_PRIVATE_DATA *) Context;\r
574 if (Private->HttpBootCallback != NULL) {\r
575 Status = Private->HttpBootCallback->Callback (\r
576 Private->HttpBootCallback,\r
577 EventType == HttpIoRequest ? HttpBootHttpRequest : HttpBootHttpResponse,\r
578 EventType == HttpIoRequest ? FALSE : TRUE,\r
579 sizeof (EFI_HTTP_MESSAGE),\r
580 (VOID *) Message\r
581 );\r
582 return Status;\r
583 }\r
584 return EFI_SUCCESS;\r
585}\r
586\r
d933e70a
JW
587/**\r
588 Create a HttpIo instance for the file download.\r
589\r
590 @param[in] Private The pointer to the driver's private data.\r
591\r
592 @retval EFI_SUCCESS Successfully created.\r
593 @retval Others Failed to create HttpIo.\r
594\r
595**/\r
596EFI_STATUS\r
597HttpBootCreateHttpIo (\r
598 IN HTTP_BOOT_PRIVATE_DATA *Private\r
599 )\r
600{\r
601 HTTP_IO_CONFIG_DATA ConfigData;\r
602 EFI_STATUS Status;\r
75372581 603 EFI_HANDLE ImageHandle;\r
d933e70a
JW
604\r
605 ASSERT (Private != NULL);\r
606\r
607 ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));\r
608 if (!Private->UsingIpv6) {\r
b659408b 609 ConfigData.Config4.HttpVersion = HttpVersion11;\r
d933e70a
JW
610 ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;\r
611 IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);\r
612 IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);\r
75372581 613 ImageHandle = Private->Ip4Nic->ImageHandle;\r
d933e70a 614 } else {\r
b659408b
ZL
615 ConfigData.Config6.HttpVersion = HttpVersion11;\r
616 ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;\r
617 IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);\r
75372581 618 ImageHandle = Private->Ip6Nic->ImageHandle;\r
d933e70a
JW
619 }\r
620\r
621 Status = HttpIoCreateIo (\r
75372581 622 ImageHandle,\r
d933e70a
JW
623 Private->Controller,\r
624 Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,\r
625 &ConfigData,\r
95b5c32f
FS
626 HttpBootHttpIoCallback,\r
627 (VOID *) Private,\r
d933e70a
JW
628 &Private->HttpIo\r
629 );\r
630 if (EFI_ERROR (Status)) {\r
631 return Status;\r
632 }\r
633\r
634 Private->HttpCreated = TRUE;\r
635 return EFI_SUCCESS;\r
636}\r
637\r
d933e70a
JW
638/**\r
639 Release all the resource of a cache item.\r
640\r
641 @param[in] Cache The pointer to the cache item.\r
642\r
643**/\r
644VOID\r
645HttpBootFreeCache (\r
646 IN HTTP_BOOT_CACHE_CONTENT *Cache\r
647 )\r
648{\r
649 UINTN Index;\r
650 LIST_ENTRY *Entry;\r
651 LIST_ENTRY *NextEntry;\r
652 HTTP_BOOT_ENTITY_DATA *EntityData;\r
653\r
654 if (Cache != NULL) {\r
655 //\r
656 // Free the request data\r
657 //\r
658 if (Cache->RequestData != NULL) {\r
659 if (Cache->RequestData->Url != NULL) {\r
660 FreePool (Cache->RequestData->Url);\r
661 }\r
662 FreePool (Cache->RequestData);\r
663 }\r
664\r
665 //\r
666 // Free the response header\r
667 //\r
668 if (Cache->ResponseData != NULL) {\r
669 if (Cache->ResponseData->Headers != NULL) {\r
670 for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {\r
671 FreePool (Cache->ResponseData->Headers[Index].FieldName);\r
672 FreePool (Cache->ResponseData->Headers[Index].FieldValue);\r
673 }\r
674 FreePool (Cache->ResponseData->Headers);\r
675 }\r
676 }\r
677\r
678 //\r
679 // Free the response body\r
680 //\r
681 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {\r
682 EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);\r
683 if (EntityData->Block != NULL) {\r
684 FreePool (EntityData->Block);\r
685 }\r
686 RemoveEntryList (&EntityData->Link);\r
687 FreePool (EntityData);\r
688 }\r
689\r
690 FreePool (Cache);\r
691 }\r
692}\r
693\r
694/**\r
695 Clean up all cached data.\r
696\r
697 @param[in] Private The pointer to the driver's private data.\r
698\r
699**/\r
700VOID\r
701HttpBootFreeCacheList (\r
702 IN HTTP_BOOT_PRIVATE_DATA *Private\r
703 )\r
704{\r
705 LIST_ENTRY *Entry;\r
706 LIST_ENTRY *NextEntry;\r
707 HTTP_BOOT_CACHE_CONTENT *Cache;\r
708 \r
709 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {\r
710 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
711 RemoveEntryList (&Cache->Link);\r
712 HttpBootFreeCache (Cache);\r
713 }\r
714}\r
715\r
7552c24e
FS
716/**\r
717 Get the file content from cached data.\r
718\r
719 @param[in] Private The pointer to the driver's private data.\r
720 @param[in] Uri Uri of the file to be retrieved from cache.\r
721 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return\r
722 code of EFI_SUCCESS, the amount of data transferred to\r
723 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
724 the size of Buffer required to retrieve the requested file.\r
725 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,\r
726 then the size of the requested file is returned in\r
727 BufferSize.\r
587d204c 728 @param[out] ImageType The image type of the downloaded file.\r
7552c24e
FS
729\r
730 @retval EFI_SUCCESS Successfully created.\r
731 @retval Others Failed to create HttpIo.\r
732\r
733**/\r
734EFI_STATUS\r
735HttpBootGetFileFromCache (\r
736 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
737 IN CHAR16 *Uri,\r
738 IN OUT UINTN *BufferSize,\r
587d204c
FS
739 OUT UINT8 *Buffer,\r
740 OUT HTTP_BOOT_IMAGE_TYPE *ImageType\r
7552c24e
FS
741 )\r
742{\r
743 LIST_ENTRY *Entry;\r
744 LIST_ENTRY *Entry2;\r
745 HTTP_BOOT_CACHE_CONTENT *Cache;\r
746 HTTP_BOOT_ENTITY_DATA *EntityData;\r
747 UINTN CopyedSize;\r
748 \r
951c6e63 749 if (Uri == NULL || BufferSize == NULL || Buffer == NULL || ImageType == NULL) {\r
7552c24e
FS
750 return EFI_INVALID_PARAMETER;\r
751 }\r
752\r
7552c24e
FS
753 NET_LIST_FOR_EACH (Entry, &Private->CacheList) {\r
754 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);\r
755 //\r
756 // Compare the URI to see whether we already have a cache for this file.\r
757 //\r
758 if ((Cache->RequestData != NULL) &&\r
759 (Cache->RequestData->Url != NULL) &&\r
951c6e63 760 (StrCmp (Uri, Cache->RequestData->Url) == 0)) {\r
7552c24e 761 //\r
587d204c
FS
762 // Hit in cache, record image type.\r
763 //\r
764 *ImageType = Cache->ImageType;\r
765\r
766 //\r
767 // Check buffer size.\r
7552c24e
FS
768 //\r
769 if (*BufferSize < Cache->EntityLength) {\r
770 *BufferSize = Cache->EntityLength;\r
771 return EFI_BUFFER_TOO_SMALL;\r
772 }\r
773\r
774 //\r
775 // Fill data to buffer.\r
776 //\r
777 CopyedSize = 0;\r
778 NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {\r
779 EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);\r
780 if (*BufferSize > CopyedSize) {\r
781 CopyMem (\r
782 Buffer + CopyedSize,\r
783 EntityData->DataStart,\r
784 MIN (EntityData->DataLength, *BufferSize - CopyedSize)\r
785 );\r
786 CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);\r
787 }\r
788 }\r
789 *BufferSize = CopyedSize;\r
7552c24e
FS
790 return EFI_SUCCESS;\r
791 }\r
792 }\r
793\r
794 return EFI_NOT_FOUND;\r
795}\r
796\r
d933e70a
JW
797/**\r
798 A callback function to intercept events during message parser.\r
799\r
800 This function will be invoked during HttpParseMessageBody() with various events type. An error\r
801 return status of the callback function will cause the HttpParseMessageBody() aborted.\r
802\r
803 @param[in] EventType Event type of this callback call.\r
804 @param[in] Data A pointer to data buffer.\r
805 @param[in] Length Length in bytes of the Data.\r
806 @param[in] Context Callback context set by HttpInitMsgParser().\r
807\r
808 @retval EFI_SUCCESS Continue to parser the message body.\r
809 @retval Others Abort the parse.\r
810 \r
811**/\r
812EFI_STATUS\r
813EFIAPI\r
814HttpBootGetBootFileCallback (\r
815 IN HTTP_BODY_PARSE_EVENT EventType,\r
816 IN CHAR8 *Data,\r
817 IN UINTN Length,\r
818 IN VOID *Context\r
819 )\r
820{\r
821 HTTP_BOOT_CALLBACK_DATA *CallbackData;\r
822 HTTP_BOOT_ENTITY_DATA *NewEntityData;\r
95b5c32f
FS
823 EFI_STATUS Status;\r
824 EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;\r
d933e70a
JW
825\r
826 //\r
827 // We only care about the entity data.\r
828 //\r
829 if (EventType != BodyParseEventOnData) {\r
830 return EFI_SUCCESS;\r
831 }\r
832\r
833 CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;\r
95b5c32f
FS
834 HttpBootCallback = CallbackData->Private->HttpBootCallback;\r
835 if (HttpBootCallback != NULL) {\r
836 Status = HttpBootCallback->Callback (\r
837 HttpBootCallback,\r
838 HttpBootHttpEntityBody,\r
839 TRUE,\r
840 (UINT32)Length,\r
841 Data\r
842 );\r
843 if (EFI_ERROR (Status)) {\r
844 return Status;\r
845 }\r
846 }\r
d933e70a
JW
847 //\r
848 // Copy data if caller has provided a buffer.\r
849 //\r
850 if (CallbackData->BufferSize > CallbackData->CopyedSize) {\r
851 CopyMem (\r
852 CallbackData->Buffer + CallbackData->CopyedSize,\r
853 Data,\r
854 MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)\r
855 );\r
856 CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);\r
857 }\r
858\r
7fd71047
FS
859 //\r
860 // The caller doesn't provide a buffer, save the block into cache list.\r
861 //\r
862 if (CallbackData->Cache != NULL) {\r
863 NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));\r
864 if (NewEntityData == NULL) {\r
865 return EFI_OUT_OF_RESOURCES;\r
866 }\r
867 if (CallbackData->NewBlock) {\r
868 NewEntityData->Block = CallbackData->Block;\r
869 CallbackData->Block = NULL;\r
870 }\r
871 NewEntityData->DataLength = Length;\r
872 NewEntityData->DataStart = (UINT8*) Data;\r
873 InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);\r
874 }\r
d933e70a
JW
875 return EFI_SUCCESS;\r
876}\r
877\r
878/**\r
879 This function download the boot file by using UEFI HTTP protocol.\r
880 \r
881 @param[in] Private The pointer to the driver's private data.\r
882 @param[in] HeaderOnly Only request the response header, it could save a lot of time if\r
883 the caller only want to know the size of the requested file.\r
884 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return\r
885 code of EFI_SUCCESS, the amount of data transferred to\r
886 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,\r
887 the size of Buffer required to retrieve the requested file.\r
888 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,\r
889 then the size of the requested file is returned in\r
890 BufferSize.\r
587d204c 891 @param[out] ImageType The image type of the downloaded file.\r
d933e70a
JW
892\r
893 @retval EFI_SUCCESS The file was loaded.\r
894 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.\r
895 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources\r
896 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.\r
897 BufferSize has been updated with the size needed to complete\r
898 the request.\r
899 @retval Others Unexpected error happened.\r
900\r
901**/\r
902EFI_STATUS\r
903HttpBootGetBootFile (\r
904 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
905 IN BOOLEAN HeaderOnly,\r
906 IN OUT UINTN *BufferSize,\r
587d204c
FS
907 OUT UINT8 *Buffer,\r
908 OUT HTTP_BOOT_IMAGE_TYPE *ImageType\r
d933e70a
JW
909 )\r
910{\r
911 EFI_STATUS Status;\r
072289f4 912 EFI_HTTP_STATUS_CODE StatusCode;\r
d933e70a
JW
913 CHAR8 *HostName;\r
914 EFI_HTTP_REQUEST_DATA *RequestData;\r
ef422fc5
PA
915 HTTP_IO_RESPONSE_DATA *ResponseData;\r
916 HTTP_IO_RESPONSE_DATA ResponseBody;\r
d933e70a
JW
917 HTTP_IO *HttpIo;\r
918 HTTP_IO_HEADER *HttpIoHeader;\r
919 VOID *Parser;\r
920 HTTP_BOOT_CALLBACK_DATA Context;\r
921 UINTN ContentLength;\r
922 HTTP_BOOT_CACHE_CONTENT *Cache;\r
923 UINT8 *Block;\r
b9679cd7 924 UINTN UrlSize;\r
d933e70a 925 CHAR16 *Url;\r
7552c24e
FS
926 BOOLEAN IdentityMode;\r
927 UINTN ReceivedSize;\r
d933e70a
JW
928 \r
929 ASSERT (Private != NULL);\r
930 ASSERT (Private->HttpCreated);\r
931\r
587d204c 932 if (BufferSize == NULL || ImageType == NULL) {\r
d933e70a
JW
933 return EFI_INVALID_PARAMETER;\r
934 }\r
935\r
936 if (*BufferSize != 0 && Buffer == NULL) {\r
937 return EFI_INVALID_PARAMETER;\r
938 }\r
939\r
940 //\r
941 // First, check whether we already cached the requested Uri.\r
942 //\r
b9679cd7
SZ
943 UrlSize = AsciiStrSize (Private->BootFileUri);\r
944 Url = AllocatePool (UrlSize * sizeof (CHAR16));\r
d933e70a
JW
945 if (Url == NULL) {\r
946 return EFI_OUT_OF_RESOURCES;\r
947 }\r
b9679cd7 948 AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize);\r
951c6e63 949 if (!HeaderOnly && Buffer != NULL) {\r
587d204c 950 Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType);\r
d933e70a
JW
951 if (Status != EFI_NOT_FOUND) {\r
952 FreePool (Url);\r
953 return Status;\r
954 }\r
955 }\r
956\r
957 //\r
958 // Not found in cache, try to download it through HTTP.\r
959 //\r
960\r
961 //\r
7fd71047 962 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.\r
d933e70a
JW
963 //\r
964 Cache = NULL;\r
7fd71047 965 if ((!HeaderOnly) && (*BufferSize == 0)) {\r
d933e70a
JW
966 Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));\r
967 if (Cache == NULL) {\r
968 Status = EFI_OUT_OF_RESOURCES;\r
969 goto ERROR_1;\r
970 }\r
587d204c 971 Cache->ImageType = ImageTypeMax;\r
d933e70a
JW
972 InitializeListHead (&Cache->EntityDataList);\r
973 }\r
974\r
975 //\r
976 // 2. Send HTTP request message.\r
977 //\r
978\r
979 //\r
980 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:\r
981 // Host\r
982 // Accept\r
983 // User-Agent\r
984 //\r
985 HttpIoHeader = HttpBootCreateHeader (3);\r
986 if (HttpIoHeader == NULL) {\r
987 Status = EFI_OUT_OF_RESOURCES;\r
988 goto ERROR_2;\r
989 }\r
990\r
991 //\r
992 // Add HTTP header field 1: Host\r
993 //\r
994 HostName = NULL;\r
995 Status = HttpUrlGetHostName (\r
996 Private->BootFileUri,\r
997 Private->BootFileUriParser,\r
998 &HostName\r
999 );\r
1000 if (EFI_ERROR (Status)) {\r
1001 goto ERROR_3;\r
1002 }\r
1003 Status = HttpBootSetHeader (\r
1004 HttpIoHeader,\r
90f658c4 1005 HTTP_HEADER_HOST,\r
d933e70a
JW
1006 HostName\r
1007 );\r
1008 FreePool (HostName);\r
1009 if (EFI_ERROR (Status)) {\r
1010 goto ERROR_3;\r
1011 }\r
1012\r
1013 //\r
1014 // Add HTTP header field 2: Accept\r
1015 //\r
1016 Status = HttpBootSetHeader (\r
1017 HttpIoHeader,\r
90f658c4 1018 HTTP_HEADER_ACCEPT,\r
d933e70a
JW
1019 "*/*"\r
1020 );\r
1021 if (EFI_ERROR (Status)) {\r
1022 goto ERROR_3;\r
1023 }\r
1024\r
1025 //\r
1026 // Add HTTP header field 3: User-Agent\r
1027 //\r
1028 Status = HttpBootSetHeader (\r
1029 HttpIoHeader,\r
90f658c4 1030 HTTP_HEADER_USER_AGENT,\r
d933e70a
JW
1031 HTTP_USER_AGENT_EFI_HTTP_BOOT\r
1032 );\r
1033 if (EFI_ERROR (Status)) {\r
1034 goto ERROR_3;\r
1035 }\r
1036\r
1037 //\r
1038 // 2.2 Build the rest of HTTP request info.\r
1039 //\r
1040 RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));\r
1041 if (RequestData == NULL) {\r
1042 Status = EFI_OUT_OF_RESOURCES;\r
1043 goto ERROR_3;\r
1044 }\r
1045 RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;\r
1046 RequestData->Url = Url;\r
d933e70a
JW
1047\r
1048 //\r
1049 // 2.3 Record the request info in a temp cache item.\r
1050 //\r
7fd71047 1051 if (Cache != NULL) {\r
d933e70a
JW
1052 Cache->RequestData = RequestData;\r
1053 }\r
1054\r
1055 //\r
1056 // 2.4 Send out the request to HTTP server.\r
1057 //\r
1058 HttpIo = &Private->HttpIo;\r
1059 Status = HttpIoSendRequest (\r
1060 HttpIo,\r
1061 RequestData,\r
1062 HttpIoHeader->HeaderCount,\r
1063 HttpIoHeader->Headers,\r
1064 0,\r
1065 NULL\r
1066 );\r
1067 if (EFI_ERROR (Status)) {\r
1068 goto ERROR_4;\r
1069 }\r
1070\r
1071 //\r
1072 // 3. Receive HTTP response message.\r
1073 //\r
1074\r
1075 //\r
1076 // 3.1 First step, use zero BodyLength to only receive the response headers.\r
1077 //\r
ef422fc5 1078 ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA));\r
d933e70a
JW
1079 if (ResponseData == NULL) {\r
1080 Status = EFI_OUT_OF_RESOURCES;\r
1081 goto ERROR_4;\r
1082 }\r
1083 Status = HttpIoRecvResponse (\r
1084 &Private->HttpIo,\r
1085 TRUE,\r
1086 ResponseData\r
1087 );\r
072289f4
ZL
1088 if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) {\r
1089 if (EFI_ERROR (ResponseData->Status)) {\r
1090 StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;\r
1091 HttpBootPrintErrorMessage (StatusCode);\r
1092 Status = ResponseData->Status;\r
1093 }\r
d933e70a
JW
1094 goto ERROR_5;\r
1095 }\r
1096\r
587d204c
FS
1097 //\r
1098 // Check the image type according to server's response.\r
1099 //\r
1100 Status = HttpBootCheckImageType (\r
1101 Private->BootFileUri,\r
1102 Private->BootFileUriParser,\r
1103 ResponseData->HeaderCount,\r
1104 ResponseData->Headers,\r
1105 ImageType\r
1106 );\r
1107 if (EFI_ERROR (Status)) {\r
1108 goto ERROR_5;\r
1109 }\r
1110\r
d933e70a
JW
1111 //\r
1112 // 3.2 Cache the response header.\r
1113 //\r
7fd71047 1114 if (Cache != NULL) {\r
d933e70a 1115 Cache->ResponseData = ResponseData;\r
587d204c 1116 Cache->ImageType = *ImageType;\r
d933e70a
JW
1117 }\r
1118 \r
1119 //\r
1120 // 3.3 Init a message-body parser from the header information.\r
1121 //\r
1122 Parser = NULL;\r
1123 Context.NewBlock = FALSE;\r
1124 Context.Block = NULL;\r
1125 Context.CopyedSize = 0;\r
1126 Context.Buffer = Buffer;\r
1127 Context.BufferSize = *BufferSize;\r
1128 Context.Cache = Cache;\r
95b5c32f 1129 Context.Private = Private;\r
d933e70a 1130 Status = HttpInitMsgParser (\r
951c6e63 1131 HeaderOnly ? HttpMethodHead : HttpMethodGet,\r
d933e70a
JW
1132 ResponseData->Response.StatusCode,\r
1133 ResponseData->HeaderCount,\r
1134 ResponseData->Headers,\r
1135 HttpBootGetBootFileCallback,\r
1136 (VOID*) &Context,\r
1137 &Parser\r
1138 );\r
1139 if (EFI_ERROR (Status)) {\r
1140 goto ERROR_6;\r
1141 }\r
1142\r
1143 //\r
1144 // 3.4 Continue to receive and parse message-body if needed.\r
1145 //\r
7fd71047 1146 Block = NULL;\r
d933e70a 1147 if (!HeaderOnly) {\r
7552c24e
FS
1148 //\r
1149 // 3.4.1, check whether we are in identity transfer-coding.\r
1150 //\r
1151 ContentLength = 0;\r
1152 Status = HttpGetEntityLength (Parser, &ContentLength);\r
1153 if (!EFI_ERROR (Status)) {\r
1154 IdentityMode = TRUE;\r
1155 } else {\r
1156 IdentityMode = FALSE;\r
1157 }\r
1158\r
1159 //\r
1160 // 3.4.2, start the message-body download, the identity and chunked transfer-coding\r
1161 // is handled in different path here.\r
1162 //\r
ef422fc5 1163 ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA));\r
7552c24e 1164 if (IdentityMode) {\r
d933e70a 1165 //\r
7552c24e
FS
1166 // In identity transfer-coding there is no need to parse the message body,\r
1167 // just download the message body to the user provided buffer directly.\r
d933e70a 1168 //\r
7552c24e
FS
1169 ReceivedSize = 0;\r
1170 while (ReceivedSize < ContentLength) {\r
1171 ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize;\r
1172 ResponseBody.BodyLength = *BufferSize - ReceivedSize;\r
1173 Status = HttpIoRecvResponse (\r
1174 &Private->HttpIo,\r
1175 FALSE,\r
1176 &ResponseBody\r
1177 );\r
7570696c
JW
1178 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {\r
1179 if (EFI_ERROR (ResponseBody.Status)) {\r
1180 Status = ResponseBody.Status;\r
1181 }\r
7fd71047
FS
1182 goto ERROR_6;\r
1183 }\r
7552c24e 1184 ReceivedSize += ResponseBody.BodyLength;\r
95b5c32f
FS
1185 if (Private->HttpBootCallback != NULL) {\r
1186 Status = Private->HttpBootCallback->Callback (\r
1187 Private->HttpBootCallback,\r
1188 HttpBootHttpEntityBody,\r
1189 TRUE,\r
1190 (UINT32)ResponseBody.BodyLength,\r
1191 ResponseBody.Body\r
1192 );\r
1193 if (EFI_ERROR (Status)) {\r
1194 goto ERROR_6;\r
1195 }\r
1196 }\r
d933e70a 1197 }\r
7552c24e 1198 } else {\r
d933e70a 1199 //\r
7552c24e
FS
1200 // In "chunked" transfer-coding mode, so we need to parse the received\r
1201 // data to get the real entity content.\r
d933e70a 1202 //\r
7552c24e
FS
1203 Block = NULL;\r
1204 while (!HttpIsMessageComplete (Parser)) {\r
1205 //\r
1206 // Allocate a buffer in Block to hold the message-body.\r
1207 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().\r
1208 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before\r
1209 // every HttpIoRecvResponse().\r
1210 //\r
1211 if (Block == NULL || Context.BufferSize == 0) {\r
1212 Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);\r
1213 if (Block == NULL) {\r
1214 Status = EFI_OUT_OF_RESOURCES;\r
1215 goto ERROR_6;\r
1216 }\r
1217 Context.NewBlock = TRUE;\r
1218 Context.Block = Block;\r
1219 } else {\r
1220 Context.NewBlock = FALSE;\r
1221 }\r
1222\r
1223 ResponseBody.Body = (CHAR8*) Block;\r
1224 ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;\r
1225 Status = HttpIoRecvResponse (\r
1226 &Private->HttpIo,\r
1227 FALSE,\r
1228 &ResponseBody\r
1229 );\r
7570696c
JW
1230 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {\r
1231 if (EFI_ERROR (ResponseBody.Status)) {\r
1232 Status = ResponseBody.Status;\r
1233 }\r
7552c24e
FS
1234 goto ERROR_6;\r
1235 }\r
1236\r
1237 //\r
1238 // Parse the new received block of the message-body, the block will be saved in cache.\r
1239 //\r
1240 Status = HttpParseMessageBody (\r
1241 Parser,\r
1242 ResponseBody.BodyLength,\r
1243 ResponseBody.Body\r
1244 );\r
1245 if (EFI_ERROR (Status)) {\r
1246 goto ERROR_6;\r
1247 }\r
d933e70a
JW
1248 }\r
1249 }\r
1250 }\r
7552c24e 1251\r
d933e70a 1252 //\r
7552c24e 1253 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.\r
d933e70a
JW
1254 //\r
1255 Status = HttpGetEntityLength (Parser, &ContentLength);\r
1256 if (EFI_ERROR (Status)) {\r
1257 goto ERROR_6;\r
1258 }\r
1259\r
1260 if (*BufferSize < ContentLength) {\r
1261 Status = EFI_BUFFER_TOO_SMALL;\r
a93786ae
GL
1262 } else {\r
1263 Status = EFI_SUCCESS;\r
d933e70a
JW
1264 }\r
1265 *BufferSize = ContentLength;\r
1266\r
1267 //\r
1268 // 4. Save the cache item to driver's cache list and return.\r
1269 //\r
7fd71047 1270 if (Cache != NULL) {\r
d933e70a
JW
1271 Cache->EntityLength = ContentLength;\r
1272 InsertTailList (&Private->CacheList, &Cache->Link);\r
1273 }\r
1274\r
1275 if (Parser != NULL) {\r
1276 HttpFreeMsgParser (Parser);\r
1277 }\r
1278\r
a93786ae 1279 return Status;\r
d933e70a
JW
1280 \r
1281ERROR_6:\r
1282 if (Parser != NULL) {\r
1283 HttpFreeMsgParser (Parser);\r
1284 }\r
1285 if (Context.Block != NULL) {\r
1286 FreePool (Context.Block);\r
1287 }\r
1288 HttpBootFreeCache (Cache);\r
1289 \r
1290ERROR_5:\r
1291 if (ResponseData != NULL) {\r
1292 FreePool (ResponseData);\r
1293 }\r
1294ERROR_4:\r
1295 if (RequestData != NULL) {\r
1296 FreePool (RequestData);\r
1297 }\r
1298ERROR_3:\r
1299 HttpBootFreeHeader (HttpIoHeader);\r
1300ERROR_2:\r
1301 if (Cache != NULL) {\r
1302 FreePool (Cache);\r
1303 }\r
1304ERROR_1:\r
1305 if (Url != NULL) {\r
1306 FreePool (Url);\r
1307 }\r
1308\r
1309 return Status;\r
1310}\r
fa848a40 1311\r