X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=NetworkPkg%2FHttpBootDxe%2FHttpBootClient.c;h=90a6acb0d4ed4203e154c04289e1e9635c0d0fe8;hp=b81b03c96070400db493e148f53ebfcb1e4cb2a4;hb=f33d5d68abc02727dc828c1079e72ab65e1d63af;hpb=b659408b933f40765960e877de3e1f8ceaab52cb diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.c b/NetworkPkg/HttpBootDxe/HttpBootClient.c index b81b03c960..90a6acb0d4 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootClient.c +++ b/NetworkPkg/HttpBootDxe/HttpBootClient.c @@ -1,13 +1,14 @@ /** @file Implementation of the boot file download function. -Copyright (c) 2015, Intel Corporation. All rights reserved.
-This program and the accompanying materials are licensed and made available under -the terms and conditions of the BSD License that accompanies this distribution. +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php. - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ @@ -15,14 +16,14 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "HttpBootDxe.h" /** - Update the IP and URL device path node to include the boot resource information. + Update the device path node to include the boot resource information. @param[in] Private The pointer to the driver's private data. @retval EFI_SUCCESS Device patch successfully updated. @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. @retval Others Unexpected error happened. - + **/ EFI_STATUS HttpBootUpdateDevicePath ( @@ -30,13 +31,15 @@ HttpBootUpdateDevicePath ( ) { EFI_DEV_PATH *Node; - EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TmpIpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TmpDnsDevicePath; EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; UINTN Length; EFI_STATUS Status; - TmpDevicePath = NULL; - + TmpIpDevicePath = NULL; + TmpDnsDevicePath = NULL; + // // Update the IP node with DHCP assigned information. // @@ -64,36 +67,72 @@ HttpBootUpdateDevicePath ( SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH)); Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH; Node->Ipv6.RemotePort = Private->Port; - Node->Ipv6.Protocol = EFI_IP_PROTO_TCP; + Node->Ipv6.Protocol = EFI_IP_PROTO_TCP; Node->Ipv6.IpAddressOrigin = 0; CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS)); CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS)); } - - TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + + TmpIpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); FreePool (Node); - if (TmpDevicePath == NULL) { + if (TmpIpDevicePath == NULL) { return EFI_OUT_OF_RESOURCES; } + // + // Update the DNS node with DNS server IP list if existed. + // + if (Private->DnsServerIp != NULL) { + Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6) + Private->DnsServerCount * sizeof (EFI_IP_ADDRESS); + Node = AllocatePool (Length); + if (Node == NULL) { + FreePool (TmpIpDevicePath); + return EFI_OUT_OF_RESOURCES; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_DNS_DP; + SetDevicePathNodeLength (Node, Length); + Node->Dns.IsIPv6 = Private->UsingIpv6 ? 0x01 : 0x00; + CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6), Private->DnsServerIp, Private->DnsServerCount * sizeof (EFI_IP_ADDRESS)); + + TmpDnsDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (TmpIpDevicePath); + TmpIpDevicePath = NULL; + if (TmpDnsDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + // // Update the URI node with the boot file URI. // Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri); Node = AllocatePool (Length); if (Node == NULL) { - FreePool (TmpDevicePath); + if (TmpIpDevicePath != NULL) { + FreePool (TmpIpDevicePath); + } + if (TmpDnsDevicePath != NULL) { + FreePool (TmpDnsDevicePath); + } return EFI_OUT_OF_RESOURCES; } Node->DevPath.Type = MESSAGING_DEVICE_PATH; Node->DevPath.SubType = MSG_URI_DP; SetDevicePathNodeLength (Node, Length); CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri)); - - NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + + if (TmpDnsDevicePath != NULL) { + NewDevicePath = AppendDevicePathNode (TmpDnsDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (TmpDnsDevicePath); + } else { + ASSERT (TmpIpDevicePath != NULL); + NewDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (TmpIpDevicePath); + } FreePool (Node); - FreePool (TmpDevicePath); if (NewDevicePath == NULL) { return EFI_OUT_OF_RESOURCES; } @@ -111,7 +150,7 @@ HttpBootUpdateDevicePath ( if (EFI_ERROR (Status)) { return Status; } - + FreePool (Private->Ip4Nic->DevicePath); Private->Ip4Nic->DevicePath = NewDevicePath; } else { @@ -130,7 +169,7 @@ HttpBootUpdateDevicePath ( FreePool (Private->Ip6Nic->DevicePath); Private->Ip6Nic->DevicePath = NewDevicePath; } - + return EFI_SUCCESS; } @@ -152,6 +191,7 @@ HttpBootDhcp4ExtractUriInfo ( HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer; UINT32 SelectIndex; UINT32 ProxyIndex; + UINT32 DnsServerIndex; EFI_DHCP4_PACKET_OPTION *Option; EFI_STATUS Status; @@ -160,6 +200,8 @@ HttpBootDhcp4ExtractUriInfo ( SelectIndex = Private->SelectIndex - 1; ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM); + DnsServerIndex = 0; + Status = EFI_SUCCESS; // @@ -167,26 +209,74 @@ HttpBootDhcp4ExtractUriInfo ( // HttpOffer contains the boot file URL. // SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4; - if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { - HttpOffer = SelectOffer; + if (Private->FilePathUri == NULL) { + // + // In Corporate environment, we need a HttpOffer. + // + if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || + (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { + HttpOffer = SelectOffer; + } else { + ASSERT (Private->SelectProxyType != HttpOfferTypeMax); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4; + } + Private->BootFileUriParser = HttpOffer->UriParser; + Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data; } else { - ASSERT (Private->SelectProxyType != HttpOfferTypeMax); - ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; - HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4; + // + // In Home environment the BootFileUri comes from the FilePath. + // + Private->BootFileUriParser = Private->FilePathUriParser; + Private->BootFileUri = Private->FilePathUri; } // - // Configure the default DNS server if server assigned. + // Check the URI scheme. // - if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) { + Status = HttpBootCheckUriScheme (Private->BootFileUri); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status)); + if (Status == EFI_INVALID_PARAMETER) { + AsciiPrint ("\n Error: Invalid URI address.\n"); + } else if (Status == EFI_ACCESS_DENIED) { + AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n"); + } + return Status; + } + + if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) { Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER]; ASSERT (Option != NULL); + + // + // Record the Dns Server address list. + // + Private->DnsServerCount = (Option->Length) / sizeof (EFI_IPv4_ADDRESS); + + Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS)); + if (Private->DnsServerIp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) { + CopyMem (&(Private->DnsServerIp[DnsServerIndex].v4), &(((EFI_IPv4_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv4_ADDRESS)); + } + + // + // Configure the default DNS server if server assigned. + // Status = HttpBootRegisterIp4Dns ( Private, Option->Length, Option->Data ); if (EFI_ERROR (Status)) { + FreePool (Private->DnsServerIp); + Private->DnsServerIp = NULL; return Status; } } @@ -195,30 +285,26 @@ HttpBootDhcp4ExtractUriInfo ( // Extract the port from URL, and use default HTTP port 80 if not provided. // Status = HttpUrlGetPort ( - (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, - HttpOffer->UriParser, + Private->BootFileUri, + Private->BootFileUriParser, &Private->Port ); if (EFI_ERROR (Status) || Private->Port == 0) { Private->Port = 80; } - - // - // Record the URI of boot file from the selected HTTP offer. - // - Private->BootFileUriParser = HttpOffer->UriParser; - Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data; - // // All boot informations are valid here. // - AsciiPrint ("\n URI: %a", Private->BootFileUri); // - // Update the device path to include the IP and boot URI information. + // Update the device path to include the boot resource information. // Status = HttpBootUpdateDevicePath (Private); + if (EFI_ERROR (Status) && Private->DnsServerIp != NULL) { + FreePool (Private->DnsServerIp); + Private->DnsServerIp = NULL; + } return Status; } @@ -241,9 +327,11 @@ HttpBootDhcp6ExtractUriInfo ( HTTP_BOOT_DHCP6_PACKET_CACHE *HttpOffer; UINT32 SelectIndex; UINT32 ProxyIndex; + UINT32 DnsServerIndex; EFI_DHCP6_PACKET_OPTION *Option; EFI_IPv6_ADDRESS IpAddr; CHAR8 *HostName; + UINTN HostNameSize; CHAR16 *HostNameStr; EFI_STATUS Status; @@ -252,6 +340,8 @@ HttpBootDhcp6ExtractUriInfo ( SelectIndex = Private->SelectIndex - 1; ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM); + DnsServerIndex = 0; + Status = EFI_SUCCESS; HostName = NULL; // @@ -259,12 +349,41 @@ HttpBootDhcp6ExtractUriInfo ( // HttpOffer contains the boot file URL. // SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6; - if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { - HttpOffer = SelectOffer; + if (Private->FilePathUri == NULL) { + // + // In Corporate environment, we need a HttpOffer. + // + if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || + (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { + HttpOffer = SelectOffer; + } else { + ASSERT (Private->SelectProxyType != HttpOfferTypeMax); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6; + } + Private->BootFileUriParser = HttpOffer->UriParser; + Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data; } else { - ASSERT (Private->SelectProxyType != HttpOfferTypeMax); - ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; - HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6; + // + // In Home environment the BootFileUri comes from the FilePath. + // + Private->BootFileUriParser = Private->FilePathUriParser; + Private->BootFileUri = Private->FilePathUri; + } + + // + // Check the URI scheme. + // + Status = HttpBootCheckUriScheme (Private->BootFileUri); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status)); + if (Status == EFI_INVALID_PARAMETER) { + AsciiPrint ("\n Error: Invalid URI address.\n"); + } else if (Status == EFI_ACCESS_DENIED) { + AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n"); + } + return Status; } // @@ -274,104 +393,126 @@ HttpBootDhcp6ExtractUriInfo ( if (EFI_ERROR (Status)) { return Status; } - + // - // Configure the default DNS server if server assigned. + // Register the IPv6 gateway address to the network device. // - if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) { + Status = HttpBootSetIp6Gateway (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpDns) || + (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) { Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER]; ASSERT (Option != NULL); + + // + // Record the Dns Server address list. + // + Private->DnsServerCount = HTONS (Option->OpLen) / sizeof (EFI_IPv6_ADDRESS); + + Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS)); + if (Private->DnsServerIp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) { + CopyMem (&(Private->DnsServerIp[DnsServerIndex].v6), &(((EFI_IPv6_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv6_ADDRESS)); + } + + // + // Configure the default DNS server if server assigned. + // Status = HttpBootSetIp6Dns ( Private, HTONS (Option->OpLen), Option->Data ); if (EFI_ERROR (Status)) { - return Status; + goto Error; } } - + // - // Extract the HTTP server Ip frome URL. This is used to Check route table + // Extract the HTTP server Ip from URL. This is used to Check route table // whether can send message to HTTP Server Ip through the GateWay. // Status = HttpUrlGetIp6 ( - (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, - HttpOffer->UriParser, + Private->BootFileUri, + Private->BootFileUriParser, &IpAddr ); - + if (EFI_ERROR (Status)) { // // The Http server address is expressed by Name Ip, so perform DNS resolution // Status = HttpUrlGetHostName ( - (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, - HttpOffer->UriParser, + Private->BootFileUri, + Private->BootFileUriParser, &HostName ); if (EFI_ERROR (Status)) { - return Status; + goto Error; } - - HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (CHAR16)); + + HostNameSize = AsciiStrSize (HostName); + HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16)); if (HostNameStr == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } - - AsciiStrToUnicodeStr (HostName, HostNameStr); + + AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize); + + if (HostName != NULL) { + FreePool (HostName); + } + Status = HttpBootDns (Private, HostNameStr, &IpAddr); FreePool (HostNameStr); if (EFI_ERROR (Status)) { + AsciiPrint ("\n Error: Could not retrieve the host address from DNS server.\n"); goto Error; - } - } - - CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS)); - - // - // register the IPv6 gateway address to the network device. - // - Status = HttpBootSetIp6Gateway (Private); - if (EFI_ERROR (Status)) { - return Status; + } } - + + CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS)); + // // Extract the port from URL, and use default HTTP port 80 if not provided. // Status = HttpUrlGetPort ( - (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, - HttpOffer->UriParser, + Private->BootFileUri, + Private->BootFileUriParser, &Private->Port ); if (EFI_ERROR (Status) || Private->Port == 0) { Private->Port = 80; } - - // - // Record the URI of boot file from the selected HTTP offer. - // - Private->BootFileUriParser = HttpOffer->UriParser; - Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data; - // // All boot informations are valid here. // - AsciiPrint ("\n URI: %a", Private->BootFileUri); + // - // Update the device path to include the IP and boot URI information. + // Update the device path to include the boot resource information. // Status = HttpBootUpdateDevicePath (Private); + if (EFI_ERROR (Status)) { + goto Error; + } + + return Status; Error: - - if (HostName != NULL) { - FreePool (HostName); + if (Private->DnsServerIp != NULL) { + FreePool (Private->DnsServerIp); + Private->DnsServerIp = NULL; } - + return Status; } @@ -391,7 +532,7 @@ HttpBootDiscoverBootInfo ( ) { EFI_STATUS Status; - + // // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and // other Http boot information. @@ -410,6 +551,40 @@ HttpBootDiscoverBootInfo ( return Status; } +/** + HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened. + + @param[in] EventType Indicate the Event type that occurs in the current callback. + @param[in] Message HTTP message which will be send to, or just received from HTTP server. + @param[in] Context The Callback Context pointer. + + @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process. + @retval Others Tells the HttpIo to abort the current HTTP process. +**/ +EFI_STATUS +EFIAPI +HttpBootHttpIoCallback ( + IN HTTP_IO_CALLBACK_EVENT EventType, + IN EFI_HTTP_MESSAGE *Message, + IN VOID *Context + ) +{ + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_STATUS Status; + Private = (HTTP_BOOT_PRIVATE_DATA *) Context; + if (Private->HttpBootCallback != NULL) { + Status = Private->HttpBootCallback->Callback ( + Private->HttpBootCallback, + EventType == HttpIoRequest ? HttpBootHttpRequest : HttpBootHttpResponse, + EventType == HttpIoRequest ? FALSE : TRUE, + sizeof (EFI_HTTP_MESSAGE), + (VOID *) Message + ); + return Status; + } + return EFI_SUCCESS; +} + /** Create a HttpIo instance for the file download. @@ -426,6 +601,7 @@ HttpBootCreateHttpIo ( { HTTP_IO_CONFIG_DATA ConfigData; EFI_STATUS Status; + EFI_HANDLE ImageHandle; ASSERT (Private != NULL); @@ -435,17 +611,21 @@ HttpBootCreateHttpIo ( ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT; IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4); IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4); + ImageHandle = Private->Ip4Nic->ImageHandle; } else { ConfigData.Config6.HttpVersion = HttpVersion11; ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT; IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6); + ImageHandle = Private->Ip6Nic->ImageHandle; } Status = HttpIoCreateIo ( - Private->Image, + ImageHandle, Private->Controller, Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4, &ConfigData, + HttpBootHttpIoCallback, + (VOID *) Private, &Private->HttpIo ); if (EFI_ERROR (Status)) { @@ -456,81 +636,6 @@ HttpBootCreateHttpIo ( return EFI_SUCCESS; } -/** - Get the file content from cached data. - - @param[in] Private The pointer to the driver's private data. - @param[in] Uri Uri of the file to be retrieved from cache. - @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return - code of EFI_SUCCESS, the amount of data transferred to - Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, - the size of Buffer required to retrieve the requested file. - @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, - then the size of the requested file is returned in - BufferSize. - - @retval EFI_SUCCESS Successfully created. - @retval Others Failed to create HttpIo. - -**/ -EFI_STATUS -HttpBootGetFileFromCache ( - IN HTTP_BOOT_PRIVATE_DATA *Private, - IN CHAR16 *Uri, - IN OUT UINTN *BufferSize, - OUT UINT8 *Buffer - ) -{ - LIST_ENTRY *Entry; - LIST_ENTRY *Entry2; - HTTP_BOOT_CACHE_CONTENT *Cache; - HTTP_BOOT_ENTITY_DATA *EntityData; - UINTN CopyedSize; - - if (Uri == NULL || BufferSize == 0 || Buffer == NULL) { - return EFI_INVALID_PARAMETER; - } - - NET_LIST_FOR_EACH (Entry, &Private->CacheList) { - Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link); - // - // Compare the URI to see whether we already have a cache for this file. - // - if ((Cache->RequestData != NULL) && - (Cache->RequestData->Url != NULL) && - (StrCmp (Uri, Cache->RequestData->Url) == 0)) - { - // - // Hit cache, check buffer size. - // - if (*BufferSize < Cache->EntityLength) { - *BufferSize = Cache->EntityLength; - return EFI_BUFFER_TOO_SMALL; - } - - // - // Fill data to buffer. - // - CopyedSize = 0; - NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) { - EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link); - if (*BufferSize > CopyedSize) { - CopyMem ( - Buffer + CopyedSize, - EntityData->DataStart, - MIN (EntityData->DataLength, *BufferSize - CopyedSize) - ); - CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize); - } - } - *BufferSize = CopyedSize; - return EFI_SUCCESS; - } - } - - return EFI_NOT_FOUND; -} - /** Release all the resource of a cache item. @@ -601,7 +706,7 @@ HttpBootFreeCacheList ( LIST_ENTRY *Entry; LIST_ENTRY *NextEntry; HTTP_BOOT_CACHE_CONTENT *Cache; - + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) { Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link); RemoveEntryList (&Cache->Link); @@ -609,6 +714,87 @@ HttpBootFreeCacheList ( } } +/** + Get the file content from cached data. + + @param[in] Private The pointer to the driver's private data. + @param[in] Uri Uri of the file to be retrieved from cache. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + @param[out] ImageType The image type of the downloaded file. + + @retval EFI_SUCCESS Successfully created. + @retval Others Failed to create HttpIo. + +**/ +EFI_STATUS +HttpBootGetFileFromCache ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN CHAR16 *Uri, + IN OUT UINTN *BufferSize, + OUT UINT8 *Buffer, + OUT HTTP_BOOT_IMAGE_TYPE *ImageType + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + HTTP_BOOT_CACHE_CONTENT *Cache; + HTTP_BOOT_ENTITY_DATA *EntityData; + UINTN CopyedSize; + + if (Uri == NULL || BufferSize == NULL || Buffer == NULL || ImageType == NULL) { + return EFI_INVALID_PARAMETER; + } + + NET_LIST_FOR_EACH (Entry, &Private->CacheList) { + Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link); + // + // Compare the URI to see whether we already have a cache for this file. + // + if ((Cache->RequestData != NULL) && + (Cache->RequestData->Url != NULL) && + (StrCmp (Uri, Cache->RequestData->Url) == 0)) { + // + // Hit in cache, record image type. + // + *ImageType = Cache->ImageType; + + // + // Check buffer size. + // + if (*BufferSize < Cache->EntityLength) { + *BufferSize = Cache->EntityLength; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fill data to buffer. + // + CopyedSize = 0; + NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) { + EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link); + if (*BufferSize > CopyedSize) { + CopyMem ( + Buffer + CopyedSize, + EntityData->DataStart, + MIN (EntityData->DataLength, *BufferSize - CopyedSize) + ); + CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize); + } + } + *BufferSize = CopyedSize; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + /** A callback function to intercept events during message parser. @@ -622,7 +808,7 @@ HttpBootFreeCacheList ( @retval EFI_SUCCESS Continue to parser the message body. @retval Others Abort the parse. - + **/ EFI_STATUS EFIAPI @@ -635,6 +821,8 @@ HttpBootGetBootFileCallback ( { HTTP_BOOT_CALLBACK_DATA *CallbackData; HTTP_BOOT_ENTITY_DATA *NewEntityData; + EFI_STATUS Status; + EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback; // // We only care about the entity data. @@ -644,6 +832,19 @@ HttpBootGetBootFileCallback ( } CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context; + HttpBootCallback = CallbackData->Private->HttpBootCallback; + if (HttpBootCallback != NULL) { + Status = HttpBootCallback->Callback ( + HttpBootCallback, + HttpBootHttpEntityBody, + TRUE, + (UINT32)Length, + Data + ); + if (EFI_ERROR (Status)) { + return Status; + } + } // // Copy data if caller has provided a buffer. // @@ -677,7 +878,7 @@ HttpBootGetBootFileCallback ( /** This function download the boot file by using UEFI HTTP protocol. - + @param[in] Private The pointer to the driver's private data. @param[in] HeaderOnly Only request the response header, it could save a lot of time if the caller only want to know the size of the requested file. @@ -688,6 +889,7 @@ HttpBootGetBootFileCallback ( @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, then the size of the requested file is returned in BufferSize. + @param[out] ImageType The image type of the downloaded file. @retval EFI_SUCCESS The file was loaded. @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL. @@ -703,14 +905,16 @@ HttpBootGetBootFile ( IN HTTP_BOOT_PRIVATE_DATA *Private, IN BOOLEAN HeaderOnly, IN OUT UINTN *BufferSize, - OUT UINT8 *Buffer + OUT UINT8 *Buffer, + OUT HTTP_BOOT_IMAGE_TYPE *ImageType ) { EFI_STATUS Status; + EFI_HTTP_STATUS_CODE StatusCode; CHAR8 *HostName; EFI_HTTP_REQUEST_DATA *RequestData; - HTTP_IO_RESOPNSE_DATA *ResponseData; - HTTP_IO_RESOPNSE_DATA ResponseBody; + HTTP_IO_RESPONSE_DATA *ResponseData; + HTTP_IO_RESPONSE_DATA ResponseBody; HTTP_IO *HttpIo; HTTP_IO_HEADER *HttpIoHeader; VOID *Parser; @@ -718,12 +922,15 @@ HttpBootGetBootFile ( UINTN ContentLength; HTTP_BOOT_CACHE_CONTENT *Cache; UINT8 *Block; + UINTN UrlSize; CHAR16 *Url; - + BOOLEAN IdentityMode; + UINTN ReceivedSize; + ASSERT (Private != NULL); ASSERT (Private->HttpCreated); - if (BufferSize == NULL) { + if (BufferSize == NULL || ImageType == NULL) { return EFI_INVALID_PARAMETER; } @@ -734,13 +941,14 @@ HttpBootGetBootFile ( // // First, check whether we already cached the requested Uri. // - Url = AllocatePool ((AsciiStrLen (Private->BootFileUri) + 1) * sizeof (CHAR16)); + UrlSize = AsciiStrSize (Private->BootFileUri); + Url = AllocatePool (UrlSize * sizeof (CHAR16)); if (Url == NULL) { return EFI_OUT_OF_RESOURCES; } - AsciiStrToUnicodeStr (Private->BootFileUri, Url); - if (!HeaderOnly) { - Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer); + AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize); + if (!HeaderOnly && Buffer != NULL) { + Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType); if (Status != EFI_NOT_FOUND) { FreePool (Url); return Status; @@ -761,6 +969,7 @@ HttpBootGetBootFile ( Status = EFI_OUT_OF_RESOURCES; goto ERROR_1; } + Cache->ImageType = ImageTypeMax; InitializeListHead (&Cache->EntityDataList); } @@ -794,7 +1003,7 @@ HttpBootGetBootFile ( } Status = HttpBootSetHeader ( HttpIoHeader, - HTTP_FIELD_NAME_HOST, + HTTP_HEADER_HOST, HostName ); FreePool (HostName); @@ -807,7 +1016,7 @@ HttpBootGetBootFile ( // Status = HttpBootSetHeader ( HttpIoHeader, - HTTP_FIELD_NAME_ACCEPT, + HTTP_HEADER_ACCEPT, "*/*" ); if (EFI_ERROR (Status)) { @@ -819,7 +1028,7 @@ HttpBootGetBootFile ( // Status = HttpBootSetHeader ( HttpIoHeader, - HTTP_FIELD_NAME_USER_AGENT, + HTTP_HEADER_USER_AGENT, HTTP_USER_AGENT_EFI_HTTP_BOOT ); if (EFI_ERROR (Status)) { @@ -836,11 +1045,6 @@ HttpBootGetBootFile ( } RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet; RequestData->Url = Url; - if (RequestData->Url == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ERROR_4; - } - AsciiStrToUnicodeStr (Private->BootFileUri, RequestData->Url); // // 2.3 Record the request info in a temp cache item. @@ -872,7 +1076,7 @@ HttpBootGetBootFile ( // // 3.1 First step, use zero BodyLength to only receive the response headers. // - ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESOPNSE_DATA)); + ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA)); if (ResponseData == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ERROR_4; @@ -882,6 +1086,25 @@ HttpBootGetBootFile ( TRUE, ResponseData ); + if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) { + if (EFI_ERROR (ResponseData->Status)) { + StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode; + HttpBootPrintErrorMessage (StatusCode); + Status = ResponseData->Status; + } + goto ERROR_5; + } + + // + // Check the image type according to server's response. + // + Status = HttpBootCheckImageType ( + Private->BootFileUri, + Private->BootFileUriParser, + ResponseData->HeaderCount, + ResponseData->Headers, + ImageType + ); if (EFI_ERROR (Status)) { goto ERROR_5; } @@ -891,8 +1114,9 @@ HttpBootGetBootFile ( // if (Cache != NULL) { Cache->ResponseData = ResponseData; + Cache->ImageType = *ImageType; } - + // // 3.3 Init a message-body parser from the header information. // @@ -903,8 +1127,9 @@ HttpBootGetBootFile ( Context.Buffer = Buffer; Context.BufferSize = *BufferSize; Context.Cache = Cache; + Context.Private = Private; Status = HttpInitMsgParser ( - HeaderOnly? HttpMethodHead : HttpMethodGet, + HeaderOnly ? HttpMethodHead : HttpMethodGet, ResponseData->Response.StatusCode, ResponseData->HeaderCount, ResponseData->Headers, @@ -921,51 +1146,112 @@ HttpBootGetBootFile ( // Block = NULL; if (!HeaderOnly) { - ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA)); - while (!HttpIsMessageComplete (Parser)) { + // + // 3.4.1, check whether we are in identity transfer-coding. + // + ContentLength = 0; + Status = HttpGetEntityLength (Parser, &ContentLength); + if (!EFI_ERROR (Status)) { + IdentityMode = TRUE; + } else { + IdentityMode = FALSE; + } + + // + // 3.4.2, start the message-body download, the identity and chunked transfer-coding + // is handled in different path here. + // + ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA)); + if (IdentityMode) { // - // Allocate a block to hold the message-body, if caller doesn't provide - // a buffer, the block will be cached and we will allocate a new one here. + // In identity transfer-coding there is no need to parse the message body, + // just download the message body to the user provided buffer directly. // - if (Block == NULL || Context.BufferSize == 0) { - Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE); - if (Block == NULL) { - Status = EFI_OUT_OF_RESOURCES; + ReceivedSize = 0; + while (ReceivedSize < ContentLength) { + ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize; + ResponseBody.BodyLength = *BufferSize - ReceivedSize; + Status = HttpIoRecvResponse ( + &Private->HttpIo, + FALSE, + &ResponseBody + ); + if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) { + if (EFI_ERROR (ResponseBody.Status)) { + Status = ResponseBody.Status; + } goto ERROR_6; } - Context.NewBlock = TRUE; - Context.Block = Block; - } else { - Context.NewBlock = FALSE; - } - - ResponseBody.Body = (CHAR8*) Block; - ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE; - Status = HttpIoRecvResponse ( - &Private->HttpIo, - FALSE, - &ResponseBody - ); - if (EFI_ERROR (Status)) { - goto ERROR_6; + ReceivedSize += ResponseBody.BodyLength; + if (Private->HttpBootCallback != NULL) { + Status = Private->HttpBootCallback->Callback ( + Private->HttpBootCallback, + HttpBootHttpEntityBody, + TRUE, + (UINT32)ResponseBody.BodyLength, + ResponseBody.Body + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + } } - + } else { // - // Parse the new received block of the message-body, the block will be saved in cache. + // In "chunked" transfer-coding mode, so we need to parse the received + // data to get the real entity content. // - Status = HttpParseMessageBody ( - Parser, - ResponseBody.BodyLength, - ResponseBody.Body - ); - if (EFI_ERROR (Status)) { - goto ERROR_6; + Block = NULL; + while (!HttpIsMessageComplete (Parser)) { + // + // Allocate a buffer in Block to hold the message-body. + // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse(). + // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before + // every HttpIoRecvResponse(). + // + if (Block == NULL || Context.BufferSize == 0) { + Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE); + if (Block == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_6; + } + Context.NewBlock = TRUE; + Context.Block = Block; + } else { + Context.NewBlock = FALSE; + } + + ResponseBody.Body = (CHAR8*) Block; + ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE; + Status = HttpIoRecvResponse ( + &Private->HttpIo, + FALSE, + &ResponseBody + ); + if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) { + if (EFI_ERROR (ResponseBody.Status)) { + Status = ResponseBody.Status; + } + goto ERROR_6; + } + + // + // Parse the new received block of the message-body, the block will be saved in cache. + // + Status = HttpParseMessageBody ( + Parser, + ResponseBody.BodyLength, + ResponseBody.Body + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } } } } - + // - // 3.5 Message-body receive & parse is completed, get the file size. + // 3.5 Message-body receive & parse is completed, we should be able to get the file size now. // Status = HttpGetEntityLength (Parser, &ContentLength); if (EFI_ERROR (Status)) { @@ -974,6 +1260,8 @@ HttpBootGetBootFile ( if (*BufferSize < ContentLength) { Status = EFI_BUFFER_TOO_SMALL; + } else { + Status = EFI_SUCCESS; } *BufferSize = ContentLength; @@ -989,8 +1277,8 @@ HttpBootGetBootFile ( HttpFreeMsgParser (Parser); } - return EFI_SUCCESS; - + return Status; + ERROR_6: if (Parser != NULL) { HttpFreeMsgParser (Parser); @@ -999,7 +1287,7 @@ ERROR_6: FreePool (Context.Block); } HttpBootFreeCache (Cache); - + ERROR_5: if (ResponseData != NULL) { FreePool (ResponseData); @@ -1021,3 +1309,4 @@ ERROR_1: return Status; } +