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