diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe')
15 files changed, 9115 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/ComponentName.c new file mode 100644 index 00000000..d25db6ad --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/ComponentName.c @@ -0,0 +1,132 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL protocol. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpDriver.h" + +/// +/// Component Name Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME_PROTOCOL gHttpDxeComponentName = { + (EFI_COMPONENT_NAME_GET_DRIVER_NAME) HttpDxeComponentNameGetDriverName, + (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) HttpDxeComponentNameGetControllerName, + "eng" +}; + +/// +/// Component Name 2 Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME2_PROTOCOL gHttpDxeComponentName2 = { + HttpDxeComponentNameGetDriverName, + HttpDxeComponentNameGetControllerName, + "en" +}; + +/// +/// Table of driver names +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_UNICODE_STRING_TABLE mHttpDxeDriverNameTable[] = { + { "eng;en", (CHAR16 *) L"HttpDxe" }, + { NULL, NULL } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mHttpDxeDriverNameTable, + DriverName, + (BOOLEAN)(This != &gHttpDxeComponentName2) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/ComponentName.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/ComponentName.h new file mode 100644 index 00000000..ee75bf99 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/ComponentName.h @@ -0,0 +1,92 @@ +/** @file + Header file for implementation of UEFI Component Name(2) protocol. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTP_COMPONENT_NAME_H__ +#define __EFI_HTTP_COMPONENT_NAME_H__ + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDns.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDns.c new file mode 100644 index 00000000..fc8c7a4d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDns.c @@ -0,0 +1,409 @@ +/** @file + Routines for HttpDxe driver to perform DNS resolution based on UEFI DNS protocols. + +Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpDriver.h" + +/** + Retrieve the host address using the EFI_DNS4_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv4 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv4_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + EFI_DNS4_PROTOCOL *Dns4; + EFI_DNS4_CONFIG_DATA Dns4CfgData; + EFI_DNS4_COMPLETION_TOKEN Token; + BOOLEAN IsDone; + HTTP_SERVICE *Service; + EFI_HANDLE Dns4Handle; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + UINTN DnsServerListCount; + EFI_IPv4_ADDRESS *DnsServerList; + UINTN DataSize; + + + Service = HttpInstance->Service; + ASSERT (Service != NULL); + + DnsServerList = NULL; + DnsServerListCount = 0; + ZeroMem (&Token, sizeof (EFI_DNS4_COMPLETION_TOKEN)); + + // + // Get DNS server list from EFI IPv4 Configuration II protocol. + // + Status = gBS->HandleProtocol (Service->ControllerHandle, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2); + if (!EFI_ERROR (Status)) { + // + // Get the required size. + // + DataSize = 0; + Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeDnsServer, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + DnsServerList = AllocatePool (DataSize); + if (DnsServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeDnsServer, &DataSize, DnsServerList); + if (EFI_ERROR (Status)) { + FreePool (DnsServerList); + DnsServerList = NULL; + } else { + DnsServerListCount = DataSize / sizeof (EFI_IPv4_ADDRESS); + } + } + } + + Dns4Handle = NULL; + Dns4 = NULL; + + // + // Create a DNS child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Service->ControllerHandle, + Service->Ip4DriverBindingHandle, + &gEfiDns4ServiceBindingProtocolGuid, + &Dns4Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns4Handle, + &gEfiDns4ProtocolGuid, + (VOID **) &Dns4, + Service->Ip4DriverBindingHandle, + Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS4 instance for the DNS server address and protocol. + // + ZeroMem (&Dns4CfgData, sizeof (Dns4CfgData)); + Dns4CfgData.DnsServerListCount = DnsServerListCount; + Dns4CfgData.DnsServerList = DnsServerList; + Dns4CfgData.UseDefaultSetting = HttpInstance->IPv4Node.UseDefaultAddress; + if (!Dns4CfgData.UseDefaultSetting) { + IP4_COPY_ADDRESS (&Dns4CfgData.StationIp, &HttpInstance->IPv4Node.LocalAddress); + IP4_COPY_ADDRESS (&Dns4CfgData.SubnetMask, &HttpInstance->IPv4Node.LocalSubnet); + } + Dns4CfgData.EnableDnsCache = TRUE; + Dns4CfgData.Protocol = EFI_IP_PROTO_UDP; + Status = Dns4->Configure ( + Dns4, + &Dns4CfgData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Create event to set the is done flag when name resolution is finished. + // + ZeroMem (&Token, sizeof (Token)); + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + Status = Dns4->HostNameToIp (Dns4, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns4->Poll (Dns4); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IP address from DNS protocol. + // + IP4_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } + +Exit: + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns4 != NULL) { + Dns4->Configure (Dns4, NULL); + + gBS->CloseProtocol ( + Dns4Handle, + &gEfiDns4ProtocolGuid, + Service->Ip4DriverBindingHandle, + Service->ControllerHandle + ); + } + + if (Dns4Handle != NULL) { + NetLibDestroyServiceChild ( + Service->ControllerHandle, + Service->Ip4DriverBindingHandle, + &gEfiDns4ServiceBindingProtocolGuid, + Dns4Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} + +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + HTTP_SERVICE *Service; + EFI_DNS6_PROTOCOL *Dns6; + EFI_DNS6_CONFIG_DATA Dns6ConfigData; + EFI_DNS6_COMPLETION_TOKEN Token; + EFI_HANDLE Dns6Handle; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_IPv6_ADDRESS *DnsServerList; + UINTN DnsServerListCount; + UINTN DataSize; + BOOLEAN IsDone; + + + Service = HttpInstance->Service; + ASSERT (Service != NULL); + + DnsServerList = NULL; + DnsServerListCount = 0; + Dns6 = NULL; + Dns6Handle = NULL; + ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN)); + + // + // Get DNS server list from EFI IPv6 Configuration protocol. + // + Status = gBS->HandleProtocol (Service->ControllerHandle, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config); + if (!EFI_ERROR (Status)) { + // + // Get the required size. + // + DataSize = 0; + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + DnsServerList = AllocatePool (DataSize); + if (DnsServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList); + if (EFI_ERROR (Status)) { + FreePool (DnsServerList); + DnsServerList = NULL; + } else { + DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + } + } + } + + // + // Create a DNSv6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Service->ControllerHandle, + Service->Ip6DriverBindingHandle, + &gEfiDns6ServiceBindingProtocolGuid, + &Dns6Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + (VOID **) &Dns6, + Service->Ip6DriverBindingHandle, + Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS6 instance for the DNS server address and protocol. + // + ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA)); + Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount; + Dns6ConfigData.DnsServerList = DnsServerList; + Dns6ConfigData.EnableDnsCache = TRUE; + Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP; + IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &HttpInstance->Ipv6Node.LocalAddress); + Status = Dns6->Configure ( + Dns6, + &Dns6ConfigData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + // + // Create event to set the IsDone flag when name resolution is finished. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Status = Dns6->HostNameToIp (Dns6, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns6->Poll (Dns6); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IPv6 address from DNS protocol. + // + IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } + +Exit: + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns6 != NULL) { + Dns6->Configure (Dns6, NULL); + + gBS->CloseProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + Service->Ip6DriverBindingHandle, + Service->ControllerHandle + ); + } + + if (Dns6Handle != NULL) { + NetLibDestroyServiceChild ( + Service->ControllerHandle, + Service->Ip6DriverBindingHandle, + &gEfiDns6ServiceBindingProtocolGuid, + Dns6Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDns.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDns.h new file mode 100644 index 00000000..d2e2b6f6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDns.h @@ -0,0 +1,52 @@ +/** @file + The header file of routines for HttpDxe driver to perform DNS resolution based on UEFI DNS protocols. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTP_DNS_H__ +#define __EFI_HTTP_DNS_H__ + +/** + Retrieve the host address using the EFI_DNS4_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv4 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv4_ADDRESS *IpAddress + ); + +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDriver.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDriver.c new file mode 100644 index 00000000..1fd84780 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDriver.c @@ -0,0 +1,1056 @@ +/** @file + The driver binding and service binding protocol for HttpDxe driver. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpDriver.h" + +EFI_HTTP_UTILITIES_PROTOCOL *mHttpUtilities = NULL; + +/// +/// Driver Binding Protocol instance +/// +EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp4DriverBinding = { + HttpDxeIp4DriverBindingSupported, + HttpDxeIp4DriverBindingStart, + HttpDxeIp4DriverBindingStop, + HTTP_DRIVER_VERSION, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp6DriverBinding = { + HttpDxeIp6DriverBindingSupported, + HttpDxeIp6DriverBindingStart, + HttpDxeIp6DriverBindingStop, + HTTP_DRIVER_VERSION, + NULL, + NULL +}; + + +/** + Create a HTTP driver service binding private instance. + + @param[in] Controller The controller that has TCP4 service binding + installed. + @param[out] ServiceData Point to HTTP driver private instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new HTTP driver private instance is created. + +**/ +EFI_STATUS +HttpCreateService ( + IN EFI_HANDLE Controller, + OUT HTTP_SERVICE **ServiceData + ) +{ + HTTP_SERVICE *HttpService; + + ASSERT (ServiceData != NULL); + *ServiceData = NULL; + + HttpService = AllocateZeroPool (sizeof (HTTP_SERVICE)); + if (HttpService == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + HttpService->Signature = HTTP_SERVICE_SIGNATURE; + HttpService->ServiceBinding.CreateChild = HttpServiceBindingCreateChild; + HttpService->ServiceBinding.DestroyChild = HttpServiceBindingDestroyChild; + HttpService->ControllerHandle = Controller; + HttpService->ChildrenNumber = 0; + InitializeListHead (&HttpService->ChildrenList); + + *ServiceData = HttpService; + return EFI_SUCCESS; +} + +/** + Release all the resource used the HTTP service binding instance. + + @param[in] HttpService The HTTP private instance. + @param[in] UsingIpv6 Indicate use TCP4 protocol or TCP6 protocol. + if TRUE, use Tcp6 protocol. + if FALSE, use Tcp4 protocol. +**/ +VOID +HttpCleanService ( + IN HTTP_SERVICE *HttpService, + IN BOOLEAN UsingIpv6 + ) +{ + + if (HttpService == NULL) { + return ; + } + if (!UsingIpv6) { + if (HttpService->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpService->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpService->Ip4DriverBindingHandle, + HttpService->ControllerHandle + ); + + NetLibDestroyServiceChild ( + HttpService->ControllerHandle, + HttpService->Ip4DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpService->Tcp4ChildHandle + ); + + HttpService->Tcp4ChildHandle = NULL; + } + } else { + if (HttpService->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpService->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpService->Ip6DriverBindingHandle, + HttpService->ControllerHandle + ); + + NetLibDestroyServiceChild ( + HttpService->ControllerHandle, + HttpService->Ip6DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpService->Tcp6ChildHandle + ); + + HttpService->Tcp6ChildHandle = NULL; + } + } + +} + +/** + The event process routine when the http utilities protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP4 config2 instance data or IP6 Config instance data. + +**/ +VOID +EFIAPI +HttpUtilitiesInstalledCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + gBS->LocateProtocol ( + &gEfiHttpUtilitiesProtocolGuid, + NULL, + (VOID **) &mHttpUtilities + ); + + // + // Close the event if Http utilities protocol is located. + // + if (mHttpUtilities != NULL && Event != NULL) { + gBS->CloseEvent (Event); + } +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param ImageHandle The firmware allocated handle for the UEFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *Registration; + + gBS->LocateProtocol ( + &gEfiHttpUtilitiesProtocolGuid, + NULL, + (VOID **) &mHttpUtilities + ); + + if (mHttpUtilities == NULL) { + // + // No Http utilities protocol, register a notify. + // + EfiCreateProtocolNotifyEvent ( + &gEfiHttpUtilitiesProtocolGuid, + TPL_CALLBACK, + HttpUtilitiesInstalledCallback, + NULL, + &Registration + ); + } + + // + // Install UEFI Driver Model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpDxeIp4DriverBinding, + ImageHandle, + &gHttpDxeComponentName, + &gHttpDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpDxeIp6DriverBinding, + NULL, + &gHttpDxeComponentName, + &gHttpDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + EfiLibUninstallDriverBindingComponentName2 ( + &gHttpDxeIp4DriverBinding, + &gHttpDxeComponentName, + &gHttpDxeComponentName2 + ); + } + return Status; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_INVALID_PARAMETER Any input parameter is NULL. + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +HttpDestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = NET_LIST_USER_STRUCT_S (Entry, HTTP_PROTOCOL, Link, HTTP_PROTOCOL_SIGNATURE); + ServiceBinding = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (HttpInstance->Handle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, HttpInstance->Handle); +} + +/** + Test to see if this driver supports ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingSupported. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *TcpServiceBindingProtocolGuid; + + if (IpVersion == IP_VERSION_4) { + TcpServiceBindingProtocolGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + TcpServiceBindingProtocolGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + TcpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Start this driver on ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingStart. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + HTTP_SERVICE *HttpService; + VOID *Interface; + BOOLEAN UsingIpv6; + + UsingIpv6 = FALSE; + + // + // Test for the Http service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + HttpService = HTTP_SERVICE_FROM_PROTOCOL (ServiceBinding); + } else { + Status = HttpCreateService (ControllerHandle, &HttpService); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (HttpService != NULL); + + // + // Install the HttpServiceBinding Protocol onto Controller + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + &HttpService->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + if (IpVersion == IP_VERSION_4) { + HttpService->Ip4DriverBindingHandle = This->DriverBindingHandle; + + if (HttpService->Tcp4ChildHandle == NULL) { + // + // Create a TCP4 child instance, but do not configure it. This will establish the parent-child relationship. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpService->Tcp4ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpService->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + &Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } else { + return EFI_ALREADY_STARTED; + } + + } else { + UsingIpv6 = TRUE; + HttpService->Ip6DriverBindingHandle = This->DriverBindingHandle; + + if (HttpService->Tcp6ChildHandle == NULL) { + // + // Create a TCP6 child instance, but do not configure it. This will establish the parent-child relationship. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + &HttpService->Tcp6ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpService->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + &Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } else { + return EFI_ALREADY_STARTED; + } + + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (HttpService != NULL) { + HttpCleanService (HttpService, UsingIpv6); + if (HttpService->Tcp4ChildHandle == NULL && HttpService->Tcp6ChildHandle == NULL) { + FreePool (HttpService); + } + } + + return Status; + + +} + +/** + Stop this driver on ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingStop. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver was removed ControllerHandle. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +HttpDxeStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer, + IN UINT8 IpVersion + ) +{ + EFI_HANDLE NicHandle; + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + HTTP_SERVICE *HttpService; + LIST_ENTRY *List; + HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + BOOLEAN UsingIpv6; + + // + // HTTP driver opens TCP4(6) child, So, Controller is a TCP4(6) + // child handle. Locate the Nic handle first. Then get the + // HTTP private data back. + // + if (IpVersion == IP_VERSION_4) { + UsingIpv6 = FALSE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid); + } else { + UsingIpv6 = TRUE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiTcp6ProtocolGuid); + } + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiHttpServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (ServiceBinding); + + if (NumberOfChildren != 0) { + // + // Destroy the HTTP child instance in ChildHandleBuffer. + // + List = &HttpService->ChildrenList; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + HttpDestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } else { + + HttpCleanService (HttpService, UsingIpv6); + + if (HttpService->Tcp4ChildHandle == NULL && HttpService->Tcp6ChildHandle == NULL) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiHttpServiceBindingProtocolGuid, + ServiceBinding + ); + FreePool (HttpService); + } + Status = EFI_SUCCESS; + } + } + + return Status; + +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return HttpDxeStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_4 + ); +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); + +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return HttpDxeStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_6 + ); +} +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCESS The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL, or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + HTTP_SERVICE *HttpService; + HTTP_PROTOCOL *HttpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (This); + HttpInstance = AllocateZeroPool (sizeof (HTTP_PROTOCOL)); + if (HttpInstance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + HttpInstance->Signature = HTTP_PROTOCOL_SIGNATURE; + HttpInstance->Service = HttpService; + HttpInstance->Method = HttpMethodMax; + + CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http)); + NetMapInit (&HttpInstance->TxTokens); + NetMapInit (&HttpInstance->RxTokens); + + // + // Install HTTP protocol onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiHttpProtocolGuid, + &HttpInstance->Http, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + HttpInstance->Handle = *ChildHandle; + + // + // Add it to the HTTP service's child list. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&HttpService->ChildrenList, &HttpInstance->Link); + HttpService->ChildrenNumber++; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + NetMapClean (&HttpInstance->TxTokens); + NetMapClean (&HttpInstance->RxTokens); + FreePool (HttpInstance); + + return Status; +} + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCESS The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + HTTP_SERVICE *HttpService; + HTTP_PROTOCOL *HttpInstance; + EFI_HTTP_PROTOCOL *Http; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (This); + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiHttpProtocolGuid, + (VOID **) &Http, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (Http); + if (HttpInstance->Service != HttpService) { + return EFI_INVALID_PARAMETER; + } + + if (HttpInstance->InDestroy) { + return EFI_SUCCESS; + } + + HttpInstance->InDestroy = TRUE; + + // + // Uninstall the HTTP protocol. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiHttpProtocolGuid, + Http + ); + + if (EFI_ERROR (Status)) { + HttpInstance->InDestroy = FALSE; + return Status; + } + + HttpCleanProtocol (HttpInstance); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + RemoveEntryList (&HttpInstance->Link); + HttpService->ChildrenNumber--; + + gBS->RestoreTPL (OldTpl); + + FreePool (HttpInstance); + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDriver.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDriver.h new file mode 100644 index 00000000..cfe5b660 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDriver.h @@ -0,0 +1,399 @@ +/** @file + The header files of the driver binding and service binding protocol for HttpDxe driver. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTP_DRIVER_H__ +#define __EFI_HTTP_DRIVER_H__ + +#include <Uefi.h> +#include <IndustryStandard/Http11.h> + +// +// Libraries +// +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/BaseLib.h> +#include <Library/UefiLib.h> +#include <Library/DebugLib.h> +#include <Library/NetLib.h> +#include <Library/HttpLib.h> +#include <Library/DpcLib.h> + +// +// UEFI Driver Model Protocols +// +#include <Protocol/DriverBinding.h> +#include <Protocol/ServiceBinding.h> +#include <Protocol/ComponentName2.h> +#include <Protocol/ComponentName.h> + +// +// Consumed Protocols +// +#include <Protocol/HttpUtilities.h> +#include <Protocol/Tcp4.h> +#include <Protocol/Tcp6.h> +#include <Protocol/Dns4.h> +#include <Protocol/Dns6.h> +#include <Protocol/Ip4Config2.h> +#include <Protocol/Ip6Config.h> +#include <Protocol/Tls.h> +#include <Protocol/TlsConfig.h> + +#include <Guid/ImageAuthentication.h> +// +// Produced Protocols +// +#include <Protocol/Http.h> + +#include <Guid/TlsAuthentication.h> +#include <Guid/HttpTlsCipherList.h> + +#include <IndustryStandard/Tls1.h> + +// +// Driver Version +// +#define HTTP_DRIVER_VERSION 0xa + +// +// Protocol instances +// +extern EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp4DriverBinding; +extern EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp6DriverBinding; + +extern EFI_COMPONENT_NAME2_PROTOCOL gHttpDxeComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gHttpDxeComponentName; + +extern EFI_HTTP_UTILITIES_PROTOCOL *mHttpUtilities; + +// +// Include files with function prototypes +// +#include "ComponentName.h" +#include "HttpImpl.h" +#include "HttpProto.h" +#include "HttpsSupport.h" +#include "HttpDns.h" + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCESS The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL, or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCESS The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxe.inf new file mode 100644 index 00000000..9abe2ff7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxe.inf @@ -0,0 +1,78 @@ +## @file +# Implementation of EFI HTTP protocol interfaces. +# +# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HttpDxe + FILE_GUID = 2366c20f-e15a-11e3-8bf1-e4115b28bc50 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = HttpDxeDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = HttpDxe.uni + +[Packages] + MdePkg/MdePkg.dec + NetworkPkg/NetworkPkg.dec + +[Sources] + ComponentName.h + ComponentName.c + HttpDns.h + HttpDns.c + HttpDriver.h + HttpDriver.c + HttpImpl.h + HttpImpl.c + HttpProto.h + HttpProto.c + HttpsSupport.h + HttpsSupport.c + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + MemoryAllocationLib + BaseLib + UefiLib + DebugLib + NetLib + HttpLib + DpcLib + +[Protocols] + gEfiHttpServiceBindingProtocolGuid ## BY_START + gEfiHttpProtocolGuid ## BY_START + gEfiHttpUtilitiesProtocolGuid ## CONSUMES + gEfiTcp4ServiceBindingProtocolGuid ## TO_START + gEfiTcp4ProtocolGuid ## TO_START + gEfiTcp6ServiceBindingProtocolGuid ## TO_START + gEfiTcp6ProtocolGuid ## TO_START + gEfiDns4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns4ProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp6ConfigProtocolGuid ## SOMETIMES_CONSUMES + gEfiTlsServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiTlsProtocolGuid ## SOMETIMES_CONSUMES + gEfiTlsConfigurationProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiTlsCaCertificateGuid ## SOMETIMES_CONSUMES ## Variable:L"TlsCaCertificate" + gEdkiiHttpTlsCipherListGuid ## SOMETIMES_CONSUMES ## Variable:L"HttpTlsCipherList" + gEfiCertX509Guid ## SOMETIMES_CONSUMES ## GUID # Check the cert type + +[Pcd] + gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + HttpDxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxe.uni new file mode 100644 index 00000000..e649a884 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxe.uni @@ -0,0 +1,17 @@ +// /** @file
+// UEFI HTTP DXE Driver.
+//
+// This driver provides UEFI 2.5 HTTP protocols. It could work with an IPv4 or IPv6 stack.
+//
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI HTTP service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver provides EFI HTTP Protocol and EFI HTTP Service Binding Protocol. It could work with an IPv4 or IPv6 stack."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxeExtra.uni new file mode 100644 index 00000000..c7255d76 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// HttpDxe Localized Strings and Content
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UEFI HTTP DXE"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c new file mode 100644 index 00000000..a1ce556e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.c @@ -0,0 +1,1669 @@ +/** @file + Implementation of EFI_HTTP_PROTOCOL protocol interfaces. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpDriver.h" + +EFI_HTTP_PROTOCOL mEfiHttpTemplate = { + EfiHttpGetModeData, + EfiHttpConfigure, + EfiHttpRequest, + EfiHttpCancel, + EfiHttpResponse, + EfiHttpPoll +}; + +/** + Returns the operational parameters for the current HTTP child instance. + + The GetModeData() function is used to read the current mode data (operational + parameters) for this HTTP protocol instance. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[out] HttpConfigData Point to buffer for operational parameters of this + HTTP instance. It is the responsibility of the caller + to allocate the memory for HttpConfigData and + HttpConfigData->AccessPoint.IPv6Node/IPv4Node. In fact, + it is recommended to allocate sufficient memory to record + IPv6Node since it is big enough for all possibilities. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData is NULL. + HttpConfigData->AccessPoint.IPv4Node or + HttpConfigData->AccessPoint.IPv6Node is NULL. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + +**/ +EFI_STATUS +EFIAPI +EfiHttpGetModeData ( + IN EFI_HTTP_PROTOCOL *This, + OUT EFI_HTTP_CONFIG_DATA *HttpConfigData + ) +{ + HTTP_PROTOCOL *HttpInstance; + + // + // Check input parameters. + // + if ((This == NULL) || (HttpConfigData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + + if ((HttpConfigData->AccessPoint.IPv6Node == NULL) || + (HttpConfigData->AccessPoint.IPv4Node == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { + return EFI_NOT_STARTED; + } + + HttpConfigData->HttpVersion = HttpInstance->HttpVersion; + HttpConfigData->TimeOutMillisec = HttpInstance->TimeOutMillisec; + HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6; + + if (HttpInstance->LocalAddressIsIPv6) { + CopyMem ( + HttpConfigData->AccessPoint.IPv6Node, + &HttpInstance->Ipv6Node, + sizeof (HttpInstance->Ipv6Node) + ); + } else { + CopyMem ( + HttpConfigData->AccessPoint.IPv4Node, + &HttpInstance->IPv4Node, + sizeof (HttpInstance->IPv4Node) + ); + } + + return EFI_SUCCESS; +} + +/** + Initialize or brutally reset the operational parameters for this EFI HTTP instance. + + The Configure() function does the following: + When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring + timeout, local address, port, etc. + When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active + connections with remote hosts, canceling all asynchronous tokens, and flush request + and response buffers without informing the appropriate hosts. + + No other EFI HTTP function can be executed by this instance until the Configure() + function is executed and returns successfully. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] HttpConfigData Pointer to the configure data to configure the instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData->LocalAddressIsIPv6 is FALSE and + HttpConfigData->AccessPoint.IPv4Node is NULL. + HttpConfigData->LocalAddressIsIPv6 is TRUE and + HttpConfigData->AccessPoint.IPv6Node is NULL. + @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling + Configure() with NULL to reset it. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_UNSUPPORTED One or more options in HttpConfigData are not supported + in the implementation. +**/ +EFI_STATUS +EFIAPI +EfiHttpConfigure ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_CONFIG_DATA *HttpConfigData OPTIONAL + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_STATUS Status; + + // + // Check input parameters. + // + if (This == NULL || + (HttpConfigData != NULL && + ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) || + (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance->Service != NULL); + + if (HttpConfigData != NULL) { + + if (HttpConfigData->HttpVersion >= HttpVersionUnsupported) { + return EFI_UNSUPPORTED; + } + + // + // Now configure this HTTP instance. + // + if (HttpInstance->State != HTTP_STATE_UNCONFIGED) { + return EFI_ALREADY_STARTED; + } + + HttpInstance->HttpVersion = HttpConfigData->HttpVersion; + HttpInstance->TimeOutMillisec = HttpConfigData->TimeOutMillisec; + HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6; + + if (HttpConfigData->LocalAddressIsIPv6) { + CopyMem ( + &HttpInstance->Ipv6Node, + HttpConfigData->AccessPoint.IPv6Node, + sizeof (HttpInstance->Ipv6Node) + ); + } else { + CopyMem ( + &HttpInstance->IPv4Node, + HttpConfigData->AccessPoint.IPv4Node, + sizeof (HttpInstance->IPv4Node) + ); + } + + // + // Creat Tcp child + // + Status = HttpInitProtocol (HttpInstance, HttpInstance->LocalAddressIsIPv6); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_HTTP_CONFIGED; + return EFI_SUCCESS; + + } else { + // + // Reset all the resources related to HttpInstance. + // + HttpCleanProtocol (HttpInstance); + HttpInstance->State = HTTP_STATE_UNCONFIGED; + return EFI_SUCCESS; + } +} + + +/** + The Request() function queues an HTTP request to this HTTP instance. + + Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent + successfully, or if there is an error, Status in token will be updated and Event will + be signaled. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP request token. + + @retval EFI_SUCCESS Outgoing data was processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_UNSUPPORTED The HTTP method is not supported in current + implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Request()has not been completed successfully. +**/ +EFI_STATUS +EFIAPI +EfiHttpRequest ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_HTTP_MESSAGE *HttpMsg; + EFI_HTTP_REQUEST_DATA *Request; + VOID *UrlParser; + EFI_STATUS Status; + CHAR8 *HostName; + UINTN HostNameSize; + UINT16 RemotePort; + HTTP_PROTOCOL *HttpInstance; + BOOLEAN Configure; + BOOLEAN ReConfigure; + BOOLEAN TlsConfigure; + CHAR8 *RequestMsg; + CHAR8 *Url; + UINTN UrlLen; + CHAR16 *HostNameStr; + HTTP_TOKEN_WRAP *Wrap; + CHAR8 *FileUrl; + UINTN RequestMsgSize; + EFI_HANDLE ImageHandle; + + // + // Initializations + // + Url = NULL; + UrlParser = NULL; + RemotePort = 0; + HostName = NULL; + RequestMsg = NULL; + HostNameStr = NULL; + Wrap = NULL; + FileUrl = NULL; + TlsConfigure = FALSE; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpMsg = Token->Message; + if (HttpMsg == NULL) { + return EFI_INVALID_PARAMETER; + } + + Request = HttpMsg->Data.Request; + + // + // Only support GET, HEAD, DELETE, PATCH, PUT and POST method in current implementation. + // + if ((Request != NULL) && (Request->Method != HttpMethodGet) && + (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodDelete) && + (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost) && + (Request->Method != HttpMethodPatch)) { + return EFI_UNSUPPORTED; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + + // + // Capture the method into HttpInstance. + // + if (Request != NULL) { + HttpInstance->Method = Request->Method; + } + + if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (Request == NULL) { + // + // Request would be NULL only for PUT/POST/PATCH operation (in the current implementation) + // + if ((HttpInstance->Method != HttpMethodPut) && + (HttpInstance->Method != HttpMethodPost) && + (HttpInstance->Method != HttpMethodPatch)) { + return EFI_INVALID_PARAMETER; + } + + // + // For PUT/POST/PATCH, we need to have the TCP already configured. Bail out if it is not! + // + if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) { + return EFI_INVALID_PARAMETER; + } + + // + // We need to have the Message Body for sending the HTTP message across in these cases. + // + if (HttpMsg->Body == NULL || HttpMsg->BodyLength == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Use existing TCP instance to transmit the packet. + // + Configure = FALSE; + ReConfigure = FALSE; + } else { + // + // Check whether the token already existed. + // + if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) { + return EFI_ACCESS_DENIED; + } + + // + // Parse the URI of the remote host. + // + Url = HttpInstance->Url; + UrlLen = StrLen (Request->Url) + 1; + if (UrlLen > HTTP_URL_BUFFER_LEN) { + Url = AllocateZeroPool (UrlLen); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + FreePool (HttpInstance->Url); + HttpInstance->Url = Url; + } + + + UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen); + + // + // From the information in Url, the HTTP instance will + // be able to determine whether to use http or https. + // + HttpInstance->UseHttps = IsHttpsUrl (Url); + + // + // HTTP is disabled, return directly if the URI is not HTTPS. + // + if (!PcdGetBool (PcdAllowHttpConnections) && !(HttpInstance->UseHttps)) { + + DEBUG ((EFI_D_ERROR, "EfiHttpRequest: HTTP is disabled.\n")); + + return EFI_ACCESS_DENIED; + } + + // + // Check whether we need to create Tls child and open the TLS protocol. + // + if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) { + // + // Use TlsSb to create Tls child and open the TLS protocol. + // + if (HttpInstance->LocalAddressIsIPv6) { + ImageHandle = HttpInstance->Service->Ip6DriverBindingHandle; + } else { + ImageHandle = HttpInstance->Service->Ip4DriverBindingHandle; + } + + HttpInstance->TlsChildHandle = TlsCreateChild ( + ImageHandle, + &(HttpInstance->TlsSb), + &(HttpInstance->Tls), + &(HttpInstance->TlsConfiguration) + ); + if (HttpInstance->TlsChildHandle == NULL) { + return EFI_DEVICE_ERROR; + } + + TlsConfigure = TRUE; + } + + UrlParser = NULL; + Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Status = HttpUrlGetHostName (Url, UrlParser, &HostName); + if (EFI_ERROR (Status)) { + goto Error1; + } + + if (HttpInstance->LocalAddressIsIPv6) { + HostNameSize = AsciiStrSize (HostName); + + if (HostNameSize > 2 && HostName[0] == '[' && HostName[HostNameSize - 2] == ']') { + // + // HostName format is expressed as IPv6, so, remove '[' and ']'. + // + HostNameSize -= 2; + CopyMem (HostName, HostName + 1, HostNameSize - 1); + HostName[HostNameSize - 1] = '\0'; + } + } + + Status = HttpUrlGetPort (Url, UrlParser, &RemotePort); + if (EFI_ERROR (Status)) { + if (HttpInstance->UseHttps) { + RemotePort = HTTPS_DEFAULT_PORT; + } else { + RemotePort = HTTP_DEFAULT_PORT; + } + } + // + // If Configure is TRUE, it indicates the first time to call Request(); + // If ReConfigure is TRUE, it indicates the request URL is not same + // with the previous call to Request(); + // + Configure = TRUE; + ReConfigure = TRUE; + + if (HttpInstance->RemoteHost == NULL) { + // + // Request() is called the first time. + // + ReConfigure = FALSE; + } else { + if ((HttpInstance->RemotePort == RemotePort) && + (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) && + (!HttpInstance->UseHttps || (HttpInstance->UseHttps && + !TlsConfigure && + HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) { + // + // Host Name and port number of the request URL are the same with previous call to Request(). + // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring. + // Check whether previous TCP packet sent out. + // + + if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) { + // + // Wrap the HTTP token in HTTP_TOKEN_WRAP + // + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + Wrap->HttpToken = Token; + Wrap->HttpInstance = HttpInstance; + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Wrap->TcpWrap.Method = Request->Method; + + FreePool (HostName); + + HttpUrlFreeParser (UrlParser); + + // + // Queue the HTTP token and return. + // + return EFI_SUCCESS; + } else { + // + // Use existing TCP instance to transmit the packet. + // + Configure = FALSE; + ReConfigure = FALSE; + } + } else { + // + // Need close existing TCP instance and create a new TCP instance for data transmit. + // + if (HttpInstance->RemoteHost != NULL) { + FreePool (HttpInstance->RemoteHost); + HttpInstance->RemoteHost = NULL; + HttpInstance->RemotePort = 0; + } + } + } + } + + if (Configure) { + // + // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution. + // + if (!HttpInstance->LocalAddressIsIPv6) { + Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr); + } else { + Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr); + } + + if (EFI_ERROR (Status)) { + HostNameSize = AsciiStrSize (HostName); + HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16)); + if (HostNameStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize); + if (!HttpInstance->LocalAddressIsIPv6) { + Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr); + } else { + Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr); + } + + FreePool (HostNameStr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Error: Could not retrieve the host address from DNS server.\n")); + goto Error1; + } + } + + // + // Save the RemotePort and RemoteHost. + // + ASSERT (HttpInstance->RemoteHost == NULL); + HttpInstance->RemotePort = RemotePort; + HttpInstance->RemoteHost = HostName; + HostName = NULL; + } + + if (ReConfigure) { + // + // The request URL is different from previous calls to Request(), close existing TCP instance. + // + if (!HttpInstance->LocalAddressIsIPv6) { + ASSERT (HttpInstance->Tcp4 != NULL); + } else { + ASSERT (HttpInstance->Tcp6 != NULL); + } + + if (HttpInstance->UseHttps && !TlsConfigure) { + Status = TlsCloseSession (HttpInstance); + if (EFI_ERROR (Status)) { + goto Error1; + } + + TlsCloseTxRxEvent (HttpInstance); + } + + HttpCloseConnection (HttpInstance); + EfiHttpCancel (This, NULL); + } + + // + // Wrap the HTTP token in HTTP_TOKEN_WRAP + // + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + Wrap->HttpToken = Token; + Wrap->HttpInstance = HttpInstance; + if (Request != NULL) { + Wrap->TcpWrap.Method = Request->Method; + } + + Status = HttpInitSession ( + HttpInstance, + Wrap, + Configure || ReConfigure, + TlsConfigure + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + + if (!Configure && !ReConfigure && !TlsConfigure) { + // + // For the new HTTP token, create TX TCP token events. + // + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + } + + // + // Create request message. + // + FileUrl = Url; + if (Url != NULL && *FileUrl != '/') { + // + // Convert the absolute-URI to the absolute-path + // + while (*FileUrl != ':') { + FileUrl++; + } + if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) { + FileUrl += 3; + while (*FileUrl != '/') { + FileUrl++; + } + } else { + Status = EFI_INVALID_PARAMETER; + goto Error3; + } + } + + Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize); + + if (EFI_ERROR (Status) || NULL == RequestMsg) { + goto Error3; + } + + // + // Every request we insert a TxToken and a response call would remove the TxToken. + // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a + // continuous request without a response call. So, in such cases, where Request + // structure is NULL, we would not insert a TxToken. + // + if (Request != NULL) { + Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error4; + } + } + + // + // Transmit the request message. + // + Status = HttpTransmitTcp ( + HttpInstance, + Wrap, + (UINT8*) RequestMsg, + RequestMsgSize + ); + if (EFI_ERROR (Status)) { + goto Error5; + } + + DispatchDpc (); + + if (HostName != NULL) { + FreePool (HostName); + } + + if (UrlParser != NULL) { + HttpUrlFreeParser (UrlParser); + } + + return EFI_SUCCESS; + +Error5: + // + // We would have inserted a TxToken only if Request structure is not NULL. + // Hence check before we do a remove in this error case. + // + if (Request != NULL) { + NetMapRemoveTail (&HttpInstance->TxTokens, NULL); + } + +Error4: + if (RequestMsg != NULL) { + FreePool (RequestMsg); + } + +Error3: + if (HttpInstance->UseHttps) { + TlsCloseSession (HttpInstance); + TlsCloseTxRxEvent (HttpInstance); + } + +Error2: + HttpCloseConnection (HttpInstance); + + HttpCloseTcpConnCloseEvent (HttpInstance); + if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL; + } + if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL; + } + +Error1: + if (HostName != NULL) { + FreePool (HostName); + } + if (Wrap != NULL) { + FreePool (Wrap); + } + if (UrlParser != NULL) { + HttpUrlFreeParser (UrlParser); + } + + return Status; + +} + +/** + Cancel a user's Token. + + @param[in] Map The HTTP instance's token queue. + @param[in] Item Object container for one HTTP token and token's wrap. + @param[in] Context The user's token to cancel. + + @retval EFI_SUCCESS Continue to check the next Item. + @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled. + +**/ +EFI_STATUS +EFIAPI +HttpCancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_HTTP_TOKEN *Token; + HTTP_TOKEN_WRAP *Wrap; + HTTP_PROTOCOL *HttpInstance; + + Token = (EFI_HTTP_TOKEN *) Context; + + // + // Return EFI_SUCCESS to check the next item in the map if + // this one doesn't match. + // + if ((Token != NULL) && (Token != Item->Key)) { + return EFI_SUCCESS; + } + + Wrap = (HTTP_TOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; + + if (!HttpInstance->LocalAddressIsIPv6) { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + // + // Cancel the Token before close its Event. + // + HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken); + + // + // Dispatch the DPC queued by the NotifyFunction of the canceled token's events. + // + DispatchDpc (); + } + } else { + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + // + // Cancel the Token before close its Event. + // + HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken); + + // + // Dispatch the DPC queued by the NotifyFunction of the canceled token's events. + // + DispatchDpc (); + } + } + + // + // If only one item is to be cancel, return EFI_ABORTED to stop + // iterating the map any more. + // + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Cancel the user's receive/transmit request. It is the worker function of + EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the + token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Token The token to cancel. If NULL, all token will be + cancelled. + + @retval EFI_SUCCESS The token is cancelled. + @retval EFI_NOT_FOUND The asynchronous request or response token is not found. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCancel ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_STATUS Status; + + // + // First check the tokens queued by EfiHttpRequest(). + // + Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token); + if (EFI_ERROR (Status)) { + if (Token != NULL) { + if (Status == EFI_ABORTED) { + return EFI_SUCCESS; + } + } else { + return Status; + } + } + + if (!HttpInstance->UseHttps) { + // + // Then check the tokens queued by EfiHttpResponse(), except for Https. + // + Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token); + if (EFI_ERROR (Status)) { + if (Token != NULL) { + if (Status == EFI_ABORTED) { + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } + } else { + return Status; + } + } + } else { + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken); + } else { + HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken); + } + } + + return EFI_SUCCESS; +} + + +/** + Abort an asynchronous HTTP request or response token. + + The Cancel() function aborts a pending HTTP request or response transaction. If + Token is not NULL and the token is in transmit or receive queues when it is being + cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL, + all asynchronous tokens issued by Request() or Response() will be aborted. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Point to storage containing HTTP request or response + token. + + @retval EFI_SUCCESS Request and Response queues are successfully flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance hasn't been configured. + @retval EFI_NOT_FOUND The asynchronous request or response token is not + found. + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +EfiHttpCancel ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + HTTP_PROTOCOL *HttpInstance; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + return HttpCancel (HttpInstance, Token); + +} + +/** + A callback function to intercept events during message parser. + + This function will be invoked during HttpParseMessageBody() with various events type. An error + return status of the callback function will cause the HttpParseMessageBody() aborted. + + @param[in] EventType Event type of this callback call. + @param[in] Data A pointer to data buffer. + @param[in] Length Length in bytes of the Data. + @param[in] Context Callback context set by HttpInitMsgParser(). + + @retval EFI_SUCCESS Continue to parser the message body. + +**/ +EFI_STATUS +EFIAPI +HttpBodyParserCallback ( + IN HTTP_BODY_PARSE_EVENT EventType, + IN CHAR8 *Data, + IN UINTN Length, + IN VOID *Context + ) +{ + HTTP_CALLBACK_DATA *CallbackData; + HTTP_TOKEN_WRAP *Wrap; + UINTN BodyLength; + CHAR8 *Body; + + if (EventType != BodyParseEventOnComplete) { + return EFI_SUCCESS; + } + + if (Data == NULL || Length != 0 || Context == NULL) { + return EFI_SUCCESS; + } + + CallbackData = (HTTP_CALLBACK_DATA *) Context; + + Wrap = (HTTP_TOKEN_WRAP *) (CallbackData->Wrap); + Body = CallbackData->ParseData; + BodyLength = CallbackData->ParseDataLength; + + if (Data < Body + BodyLength) { + Wrap->HttpInstance->NextMsg = Data; + } else { + Wrap->HttpInstance->NextMsg = NULL; + } + + return EFI_SUCCESS; +} + +/** + The work function of EfiHttpResponse(). + + @param[in] Wrap Pointer to HTTP token's wrap data. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to complete the operation due to lack of resources. + @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or + the EFI_HTTP_UTILITIES_PROTOCOL is not available. + +**/ +EFI_STATUS +HttpResponseWorker ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_HTTP_MESSAGE *HttpMsg; + CHAR8 *EndofHeader; + CHAR8 *HttpHeaders; + UINTN SizeofHeaders; + UINTN BufferSize; + UINTN StatusCode; + CHAR8 *Tmp; + CHAR8 *HeaderTmp; + CHAR8 *StatusCodeStr; + UINTN BodyLen; + HTTP_PROTOCOL *HttpInstance; + EFI_HTTP_TOKEN *Token; + NET_MAP_ITEM *Item; + HTTP_TOKEN_WRAP *ValueInItem; + UINTN HdrLen; + NET_FRAGMENT Fragment; + + if (Wrap == NULL || Wrap->HttpInstance == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = Wrap->HttpInstance; + Token = Wrap->HttpToken; + HttpMsg = Token->Message; + + HttpInstance->EndofHeader = NULL; + HttpInstance->HttpHeaders = NULL; + HttpMsg->Headers = NULL; + HttpHeaders = NULL; + SizeofHeaders = 0; + BufferSize = 0; + EndofHeader = NULL; + ValueInItem = NULL; + Fragment.Len = 0; + Fragment.Bulk = NULL; + + if (HttpMsg->Data.Response != NULL) { + // + // Check whether we have cached header from previous call. + // + if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) { + // + // The data is stored at [NextMsg, CacheBody + CacheLen]. + // + HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg; + HttpHeaders = AllocateZeroPool (HdrLen); + if (HttpHeaders == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen); + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + HttpInstance->CacheOffset = 0; + SizeofHeaders = HdrLen; + BufferSize = HttpInstance->CacheLen; + + // + // Check whether we cached the whole HTTP headers. + // + EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); + } + + HttpInstance->EndofHeader = &EndofHeader; + HttpInstance->HttpHeaders = &HttpHeaders; + + + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for response + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } + + // + // Start the timer, and wait Timeout seconds to receive the header packet. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto Error; + } + + Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + goto Error; + } + + ASSERT (HttpHeaders != NULL); + + // + // Cache the part of body. + // + BodyLen = BufferSize - (EndofHeader - HttpHeaders); + if (BodyLen > 0) { + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + } + + HttpInstance->CacheBody = AllocateZeroPool (BodyLen); + if (HttpInstance->CacheBody == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen); + HttpInstance->CacheLen = BodyLen; + } + + // + // Search for Status Code. + // + StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1; + if (StatusCodeStr == NULL) { + Status = EFI_NOT_READY; + goto Error; + } + + StatusCode = AsciiStrDecimalToUintn (StatusCodeStr); + + // + // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n". + // + Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR); + if (Tmp == NULL) { + Status = EFI_NOT_READY; + goto Error; + } + + // + // We could have response with just a HTTP message and no headers. For Example, + // "100 Continue". In such cases, we would not want to unnecessarily call a Parse + // method. A "\r\n" following Tmp string again would indicate an end. Compare and + // set SizeofHeaders to 0. + // + Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); + if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) { + SizeofHeaders = 0; + } else { + SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); + } + + HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode); + HttpInstance->StatusCode = StatusCode; + + Status = EFI_NOT_READY; + ValueInItem = NULL; + + // + // In cases of PUT/POST/PATCH, after an initial request-response pair, we would do a + // continuous request without a response call. So, we would not do an insert of + // TxToken. After we have sent the complete file, we will call a response to get + // a final response from server. In such a case, we would not have any TxTokens. + // Hence, check that case before doing a NetMapRemoveHead. + // + if (!NetMapIsEmpty (&HttpInstance->TxTokens)) { + NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem); + if (ValueInItem == NULL) { + goto Error; + } + + // + // The first Tx Token not transmitted yet, insert back and return error. + // + if (!ValueInItem->TcpWrap.IsTxDone) { + goto Error2; + } + } + + if (SizeofHeaders != 0) { + HeaderTmp = AllocateZeroPool (SizeofHeaders); + if (HeaderTmp == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error2; + } + + CopyMem (HeaderTmp, Tmp, SizeofHeaders); + FreePool (HttpHeaders); + HttpHeaders = HeaderTmp; + + // + // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available. + // + if (mHttpUtilities == NULL) { + Status = EFI_NOT_READY; + goto Error2; + } + + // + // Parse the HTTP header into array of key/value pairs. + // + Status = mHttpUtilities->Parse ( + mHttpUtilities, + HttpHeaders, + SizeofHeaders, + &HttpMsg->Headers, + &HttpMsg->HeaderCount + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + + FreePool (HttpHeaders); + HttpHeaders = NULL; + + + // + // Init message-body parser by header information. + // + Status = HttpInitMsgParser ( + HttpInstance->Method, + HttpMsg->Data.Response->StatusCode, + HttpMsg->HeaderCount, + HttpMsg->Headers, + HttpBodyParserCallback, + (VOID *) (&HttpInstance->CallbackData), + &HttpInstance->MsgParser + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + + // + // Check whether we received a complete HTTP message. + // + if (HttpInstance->CacheBody != NULL) { + // + // Record the CallbackData data. + // + HttpInstance->CallbackData.Wrap = (VOID *) Wrap; + HttpInstance->CallbackData.ParseData = (VOID *) HttpInstance->CacheBody; + HttpInstance->CallbackData.ParseDataLength = HttpInstance->CacheLen; + + // + // Parse message with CallbackData data. + // + Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody); + if (EFI_ERROR (Status)) { + goto Error2; + } + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + } + + if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { + Status = EFI_SUCCESS; + goto Exit; + } + } + + // + // Receive the response body. + // + BodyLen = 0; + + // + // First check whether we cached some data. + // + if (HttpInstance->CacheBody != NULL) { + // + // Calculate the length of the cached data. + // + if (HttpInstance->NextMsg != NULL) { + // + // We have a cached HTTP message which includes a part of HTTP header of next message. + // + BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); + } else { + BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset; + } + + if (BodyLen > 0) { + // + // We have some cached data. Just copy the data and return. + // + if (HttpMsg->BodyLength < BodyLen) { + CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength); + HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength; + } else { + // + // Copy all cached data out. + // + CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen); + HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset; + HttpMsg->BodyLength = BodyLen; + + if (HttpInstance->NextMsg == NULL) { + // + // There is no HTTP header of next message. Just free the cache buffer. + // + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + HttpInstance->CacheOffset = 0; + } + } + // + // Return since we already received required data. + // + Status = EFI_SUCCESS; + goto Exit; + } + + if (BodyLen == 0 && HttpInstance->MsgParser == NULL) { + // + // We received a complete HTTP message, and we don't have more data to return to caller. + // + HttpMsg->BodyLength = 0; + Status = EFI_SUCCESS; + goto Exit; + } + } + + ASSERT (HttpInstance->MsgParser != NULL); + + // + // We still need receive more data when there is no cache data and MsgParser is not NULL; + // + if (!HttpInstance->UseHttps) { + Status = HttpTcpReceiveBody (Wrap, HttpMsg); + + if (EFI_ERROR (Status)) { + goto Error2; + } + + } else { + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for response + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + } + + // + // Start the timer, and wait Timeout seconds to receive the body packet. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto Error2; + } + + Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + goto Error2; + } + + // + // Process the received the body packet. + // + HttpMsg->BodyLength = MIN ((UINTN) Fragment.Len, HttpMsg->BodyLength); + + CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength); + + // + // Record the CallbackData data. + // + HttpInstance->CallbackData.Wrap = (VOID *) Wrap; + HttpInstance->CallbackData.ParseData = HttpMsg->Body; + HttpInstance->CallbackData.ParseDataLength = HttpMsg->BodyLength; + + // + // Parse Body with CallbackData data. + // + Status = HttpParseMessageBody ( + HttpInstance->MsgParser, + HttpMsg->BodyLength, + HttpMsg->Body + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + // + // Check whether there is the next message header in the HttpMsg->Body. + // + if (HttpInstance->NextMsg != NULL) { + HttpMsg->BodyLength = HttpInstance->NextMsg - (CHAR8 *) HttpMsg->Body; + } + + HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength; + if (HttpInstance->CacheLen != 0) { + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + } + + HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen); + if (HttpInstance->CacheBody == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error2; + } + + CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen); + HttpInstance->CacheOffset = 0; + if (HttpInstance->NextMsg != NULL) { + HttpInstance->NextMsg = HttpInstance->CacheBody; + } + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + goto Exit; + } + + return Status; + +Exit: + Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); + } + + if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) { + Token->Status = EFI_HTTP_ERROR; + } else { + Token->Status = Status; + } + + gBS->SignalEvent (Token->Event); + HttpCloseTcpRxEvent (Wrap); + FreePool (Wrap); + return Status; + +Error2: + if (ValueInItem != NULL) { + NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); + } + +Error: + Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); + } + + if (!HttpInstance->UseHttps) { + HttpTcpTokenCleanup (Wrap); + } else { + FreePool (Wrap); + } + + if (HttpHeaders != NULL) { + FreePool (HttpHeaders); + HttpHeaders = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + if (HttpMsg->Headers != NULL) { + FreePool (HttpMsg->Headers); + HttpMsg->Headers = NULL; + } + + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + } + + if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) { + Token->Status = EFI_HTTP_ERROR; + } else { + Token->Status = Status; + } + + gBS->SignalEvent (Token->Event); + + return Status; + +} + + +/** + The Response() function queues an HTTP response to this HTTP instance, similar to + Receive() function in the EFI TCP driver. When the HTTP response is received successfully, + or if there is an error, Status in token will be updated and Event will be signaled. + + The HTTP driver will queue a receive token to the underlying TCP instance. When data + is received in the underlying TCP instance, the data will be parsed and Token will + be populated with the response data. If the data received from the remote host + contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting + (asynchronously) for more data to be sent from the remote host before signaling + Event in Token. + + It is the responsibility of the caller to allocate a buffer for Body and specify the + size in BodyLength. If the remote host provides a response that contains a content + body, up to BodyLength bytes will be copied from the receive buffer into Body and + BodyLength will be updated with the amount of bytes received and copied to Body. This + allows the client to download a large file in chunks instead of into one contiguous + block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is + non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive + token to underlying TCP instance. If data arrives in the receive buffer, up to + BodyLength bytes of data will be copied to Body. The HTTP driver will then update + BodyLength with the amount of bytes received and copied to Body. + + If the HTTP driver does not have an open underlying TCP connection with the host + specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is + consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain + an open TCP connection between client and host. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP response token. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message->Headers is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Response() has not been completed successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host + specified by response URL. +**/ +EFI_STATUS +EFIAPI +EfiHttpResponse ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_STATUS Status; + EFI_HTTP_MESSAGE *HttpMsg; + HTTP_PROTOCOL *HttpInstance; + HTTP_TOKEN_WRAP *Wrap; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpMsg = Token->Message; + if (HttpMsg == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + // + // Check whether the token already existed. + // + if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) { + return EFI_ACCESS_DENIED; + } + + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Wrap->HttpInstance = HttpInstance; + Wrap->HttpToken = Token; + + // + // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to + // receive the https response. A special TlsRxToken is used for receiving TLS + // related messages. It should be a blocking response. + // + if (!HttpInstance->UseHttps) { + Status = HttpCreateTcpRxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error; + } + } + + Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // If already have pending RxTokens, return directly. + // + if (NetMapGetCount (&HttpInstance->RxTokens) > 1) { + return EFI_SUCCESS; + } + + return HttpResponseWorker (Wrap); + +Error: + if (Wrap != NULL) { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + FreePool (Wrap); + } + + return Status; +} + +/** + The Poll() function can be used by network drivers and applications to increase the + rate that data packets are moved between the communication devices and the transmit + and receive queues. + + In some systems, the periodic timer event in the managed network driver may not poll + the underlying communications device fast enough to transmit and/or receive all data + packets without missing incoming packets or dropping outgoing packets. Drivers and + applications that are experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + +**/ +EFI_STATUS +EFIAPI +EfiHttpPoll ( + IN EFI_HTTP_PROTOCOL *This + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + if (HttpInstance->LocalAddressIsIPv6) { + if (HttpInstance->Tcp6 == NULL) { + return EFI_NOT_STARTED; + } + Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } else { + if (HttpInstance->Tcp4 == NULL) { + return EFI_NOT_STARTED; + } + Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + DispatchDpc (); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.h new file mode 100644 index 00000000..b4cba939 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpImpl.h @@ -0,0 +1,233 @@ +/** @file + The header files of implementation of EFI_HTTP_PROTOCOL protocol interfaces. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTP_IMPL_H__ +#define __EFI_HTTP_IMPL_H__ + +#define HTTP_DEFAULT_PORT 80 +#define HTTP_END_OF_HDR_STR "\r\n\r\n" +#define HTTP_CRLF_STR "\r\n" +#define HTTP_VERSION_STR HTTP_VERSION +#define HTTP_VERSION_CRLF_STR " HTTP/1.1\r\n" +#define HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE 300 + + +/** + Returns the operational parameters for the current HTTP child instance. + + The GetModeData() function is used to read the current mode data (operational + parameters) for this HTTP protocol instance. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[out] HttpConfigData Point to buffer for operational parameters of this + HTTP instance. It is the responsibility of the caller + to allocate the memory for HttpConfigData and + HttpConfigData->AccessPoint.IPv6Node/IPv4Node. In fact, + it is recommended to allocate sufficient memory to record + IPv6Node since it is big enough for all possibilities. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData is NULL. + HttpConfigData->AccessPoint.IPv4Node or + HttpConfigData->AccessPoint.IPv6Node is NULL. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + +**/ +EFI_STATUS +EFIAPI +EfiHttpGetModeData ( + IN EFI_HTTP_PROTOCOL *This, + OUT EFI_HTTP_CONFIG_DATA *HttpConfigData + ); + +/** + Initialize or brutally reset the operational parameters for this EFI HTTP instance. + + The Configure() function does the following: + When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring + timeout, local address, port, etc. + When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active + connections with remote hosts, canceling all asynchronous tokens, and flush request + and response buffers without informing the appropriate hosts. + + No other EFI HTTP function can be executed by this instance until the Configure() + function is executed and returns successfully. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] HttpConfigData Pointer to the configure data to configure the instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData->LocalAddressIsIPv6 is FALSE and + HttpConfigData->AccessPoint.IPv4Node is NULL. + HttpConfigData->LocalAddressIsIPv6 is TRUE and + HttpConfigData->AccessPoint.IPv6Node is NULL. + @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling + Configure() with NULL to reset it. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_UNSUPPORTED One or more options in ConfigData are not supported + in the implementation. +**/ +EFI_STATUS +EFIAPI +EfiHttpConfigure ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_CONFIG_DATA *HttpConfigData + ); + +/** + The Request() function queues an HTTP request to this HTTP instance. + + Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent + successfully, or if there is an error, Status in token will be updated and Event will + be signaled. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP request token. + + @retval EFI_SUCCESS Outgoing data was processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_UNSUPPORTED The HTTP method is not supported in current + implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Request()has not been completed successfully. +**/ +EFI_STATUS +EFIAPI +EfiHttpRequest ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + Abort an asynchronous HTTP request or response token. + + The Cancel() function aborts a pending HTTP request or response transaction. If + Token is not NULL and the token is in transmit or receive queues when it is being + cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL, + all asynchronous tokens issued by Request() or Response() will be aborted. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Point to storage containing HTTP request or response + token. + + @retval EFI_SUCCESS Request and Response queues are successfully flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance hasn't been configured. + @retval EFI_NOT_FOUND The asynchronous request or response token is not + found. + @retval EFI_UNSUPPORTED The implementation does not support this function. +**/ +EFI_STATUS +EFIAPI +EfiHttpCancel ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + The Response() function queues an HTTP response to this HTTP instance, similar to + Receive() function in the EFI TCP driver. When the HTTP response is received successfully, + or if there is an error, Status in token will be updated and Event will be signaled. + + The HTTP driver will queue a receive token to the underlying TCP instance. When data + is received in the underlying TCP instance, the data will be parsed and Token will + be populated with the response data. If the data received from the remote host + contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting + (asynchronously) for more data to be sent from the remote host before signaling + Event in Token. + + It is the responsibility of the caller to allocate a buffer for Body and specify the + size in BodyLength. If the remote host provides a response that contains a content + body, up to BodyLength bytes will be copied from the receive buffer into Body and + BodyLength will be updated with the amount of bytes received and copied to Body. This + allows the client to download a large file in chunks instead of into one contiguous + block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is + non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive + token to underlying TCP instance. If data arrives in the receive buffer, up to + BodyLength bytes of data will be copied to Body. The HTTP driver will then update + BodyLength with the amount of bytes received and copied to Body. + + If the HTTP driver does not have an open underlying TCP connection with the host + specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is + consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain + an open TCP connection between client and host. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP response token. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message->Headers is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Response() has not been completed successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host + specified by response URL. +**/ +EFI_STATUS +EFIAPI +EfiHttpResponse ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + The Poll() function can be used by network drivers and applications to increase the + rate that data packets are moved between the communication devices and the transmit + and receive queues. + + In some systems, the periodic timer event in the managed network driver may not poll + the underlying communications device fast enough to transmit and/or receive all data + packets without missing incoming packets or dropping outgoing packets. Drivers and + applications that are experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + +**/ +EFI_STATUS +EFIAPI +EfiHttpPoll ( + IN EFI_HTTP_PROTOCOL *This + ); + +extern EFI_HTTP_PROTOCOL mEfiHttpTemplate; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpProto.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpProto.c new file mode 100644 index 00000000..598742a9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpProto.c @@ -0,0 +1,2197 @@ +/** @file + Miscellaneous routines for HttpDxe driver. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpDriver.h" + +/** + The common notify function used in HTTP driver. + + @param[in] Event The event signaled. + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if ((Event == NULL) || (Context == NULL)) { + return ; + } + + *((BOOLEAN *) Context) = TRUE; +} + +/** + The notify function associated with Tx4Token for Tcp4->Transmit() or Tx6Token for Tcp6->Transmit(). + + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpTcpTransmitNotifyDpc ( + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + HTTP_PROTOCOL *HttpInstance; + + if (Context == NULL) { + return ; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + HttpInstance = Wrap->HttpInstance; + + if (!HttpInstance->LocalAddressIsIPv6) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Tx4Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.Tx4Token.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Tx4Token.Packet.TxData->FragmentTable[0].FragmentBuffer); + } + + if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + } + + } else { + Wrap->HttpToken->Status = Wrap->TcpWrap.Tx6Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.Tx6Token.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Tx6Token.Packet.TxData->FragmentTable[0].FragmentBuffer); + } + + if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + } + } + + + Wrap->TcpWrap.IsTxDone = TRUE; + + // + // Check pending TxTokens and sent out. + // + NetMapIterate (&Wrap->HttpInstance->TxTokens, HttpTcpTransmit, NULL); + +} + +/** + Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to TCP for transmit. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +HttpTcpTransmitNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, HttpTcpTransmitNotifyDpc, Context); +} + +/** + The notify function associated with Rx4Token for Tcp4->Receive () or Rx6Token for Tcp6->Receive(). + + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpTcpReceiveNotifyDpc ( + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + UINTN Length; + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + BOOLEAN UsingIpv6; + + if (Context == NULL) { + return ; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + HttpInstance = Wrap->HttpInstance; + UsingIpv6 = HttpInstance->LocalAddressIsIPv6; + + if (UsingIpv6) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + Wrap->TcpWrap.Rx6Token.CompletionToken.Event = NULL; + + if (EFI_ERROR (Wrap->TcpWrap.Rx6Token.CompletionToken.Status)) { + DEBUG ((EFI_D_ERROR, "HttpTcpReceiveNotifyDpc: %r!\n", Wrap->TcpWrap.Rx6Token.CompletionToken.Status)); + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + Item = NetMapFindKey (&HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&HttpInstance->RxTokens, Item, NULL); + } + + FreePool (Wrap); + Wrap = NULL; + + return ; + } + + } else { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + Wrap->TcpWrap.Rx4Token.CompletionToken.Event = NULL; + + if (EFI_ERROR (Wrap->TcpWrap.Rx4Token.CompletionToken.Status)) { + DEBUG ((EFI_D_ERROR, "HttpTcpReceiveNotifyDpc: %r!\n", Wrap->TcpWrap.Rx4Token.CompletionToken.Status)); + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + Item = NetMapFindKey (&HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&HttpInstance->RxTokens, Item, NULL); + } + + FreePool (Wrap); + Wrap = NULL; + + return ; + } + } + + // + // Check whether we receive a complete HTTP message. + // + ASSERT (HttpInstance->MsgParser != NULL); + if (UsingIpv6) { + Length = (UINTN) Wrap->TcpWrap.Rx6Data.FragmentTable[0].FragmentLength; + } else { + Length = (UINTN) Wrap->TcpWrap.Rx4Data.FragmentTable[0].FragmentLength; + } + + // + // Record the CallbackData data. + // + HttpInstance->CallbackData.Wrap = (VOID *) Wrap; + HttpInstance->CallbackData.ParseData = Wrap->HttpToken->Message->Body; + HttpInstance->CallbackData.ParseDataLength = Length; + + // + // Parse Body with CallbackData data. + // + Status = HttpParseMessageBody ( + HttpInstance->MsgParser, + Length, + Wrap->HttpToken->Message->Body + ); + if (EFI_ERROR (Status)) { + return ; + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + Wrap->HttpToken->Message->BodyLength = Length; + ASSERT (HttpInstance->CacheBody == NULL); + // + // We receive part of header of next HTTP msg. + // + if (HttpInstance->NextMsg != NULL) { + Wrap->HttpToken->Message->BodyLength = HttpInstance->NextMsg - + (CHAR8 *) Wrap->HttpToken->Message->Body; + HttpInstance->CacheLen = Length - Wrap->HttpToken->Message->BodyLength; + if (HttpInstance->CacheLen != 0) { + HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen); + if (HttpInstance->CacheBody == NULL) { + return ; + } + CopyMem (HttpInstance->CacheBody, HttpInstance->NextMsg, HttpInstance->CacheLen); + HttpInstance->NextMsg = HttpInstance->CacheBody; + HttpInstance->CacheOffset = 0; + } + } + + Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); + } + + + Wrap->TcpWrap.IsRxDone = TRUE; + if (UsingIpv6) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status; + } else { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status; + } + + + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Check pending RxTokens and receive the HTTP message. + // + NetMapIterate (&Wrap->HttpInstance->RxTokens, HttpTcpReceive, NULL); + + FreePool (Wrap); + Wrap = NULL; +} + +/** + Request HttpTcpReceiveNotifyDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to TCP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +HttpTcpReceiveNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, HttpTcpReceiveNotifyDpc, Context); +} + +/** + Create events for the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events are created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // Create events for various asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp4ConnDone, + &HttpInstance->Tcp4ConnToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Initialize Tcp4CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp4CloseDone, + &HttpInstance->Tcp4CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + } else { + // + // Create events for various asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp6ConnDone, + &HttpInstance->Tcp6ConnToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Initialize Tcp6CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp6CloseDone, + &HttpInstance->Tcp6CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + } + + return EFI_SUCCESS; + +ERROR: + // + // Error handling + // + HttpCloseTcpConnCloseEvent (HttpInstance); + + return Status; +} + + +/** + Close events in the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +HttpCloseTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + ASSERT (HttpInstance != NULL); + + if (HttpInstance->LocalAddressIsIPv6) { + if (NULL != HttpInstance->Tcp6ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp6ConnToken.CompletionToken.Event); + HttpInstance->Tcp6ConnToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp6CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp6CloseToken.CompletionToken.Event); + HttpInstance->Tcp6CloseToken.CompletionToken.Event = NULL; + } + + } else { + if (NULL != HttpInstance->Tcp4ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp4ConnToken.CompletionToken.Event); + HttpInstance->Tcp4ConnToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp4CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp4CloseToken.CompletionToken.Event); + HttpInstance->Tcp4CloseToken.CompletionToken.Event = NULL; + } + } + +} + +/** + Create event for the TCP transmit token. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpTxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + HttpInstance = Wrap->HttpInstance; + TcpWrap = &Wrap->TcpWrap; + + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->Tx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Tx4Data.Push = TRUE; + TcpWrap->Tx4Data.Urgent = FALSE; + TcpWrap->Tx4Data.FragmentCount = 1; + TcpWrap->Tx4Token.Packet.TxData = &Wrap->TcpWrap.Tx4Data; + TcpWrap->Tx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->Tx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Tx6Data.Push = TRUE; + TcpWrap->Tx6Data.Urgent = FALSE; + TcpWrap->Tx6Data.FragmentCount = 1; + TcpWrap->Tx6Token.Packet.TxData = &Wrap->TcpWrap.Tx6Data; + TcpWrap->Tx6Token.CompletionToken.Status =EFI_NOT_READY; + + } + + return EFI_SUCCESS; +} + +/** + Create event for the TCP receive token which is used to receive HTTP header. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEventForHeader ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->Rx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->Rx4Data.FragmentCount = 1; + HttpInstance->Rx4Token.Packet.RxData = &HttpInstance->Rx4Data; + HttpInstance->Rx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->Rx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->Rx6Data.FragmentCount =1; + HttpInstance->Rx6Token.Packet.RxData = &HttpInstance->Rx6Data; + HttpInstance->Rx6Token.CompletionToken.Status = EFI_NOT_READY; + + } + + + return EFI_SUCCESS; +} + +/** + Create event for the TCP receive token which is used to receive HTTP body. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + HttpInstance = Wrap->HttpInstance; + TcpWrap = &Wrap->TcpWrap; + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->Rx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Rx4Data.FragmentCount = 1; + TcpWrap->Rx4Token.Packet.RxData = &Wrap->TcpWrap.Rx4Data; + TcpWrap->Rx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->Rx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Rx6Data.FragmentCount = 1; + TcpWrap->Rx6Token.Packet.RxData = &Wrap->TcpWrap.Rx6Data; + TcpWrap->Rx6Token.CompletionToken.Status = EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +/** + Close Events for Tcp Receive Tokens for HTTP body and HTTP header. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpCloseTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + HTTP_PROTOCOL *HttpInstance; + + ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; + + if (HttpInstance->LocalAddressIsIPv6) { + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + + if (HttpInstance->Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx6Token.CompletionToken.Event); + HttpInstance->Rx6Token.CompletionToken.Event = NULL; + } + } else { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + + if (HttpInstance->Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx4Token.CompletionToken.Event); + HttpInstance->Rx4Token.CompletionToken.Event = NULL; + } + } +} + +/** + Initialize the HTTP_PROTOCOL structure to the unconfigured state. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] IpVersion Indicate us TCP4 protocol or TCP6 protocol. + + @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitProtocol ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN BOOLEAN IpVersion + ) +{ + EFI_STATUS Status; + VOID *Interface; + BOOLEAN UsingIpv6; + + ASSERT (HttpInstance != NULL); + UsingIpv6 = IpVersion; + + if (!UsingIpv6) { + // + // Create TCP4 child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip4DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpInstance->Tcp4ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &HttpInstance->Tcp4, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + } else { + // + // Create TCP6 Child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip6DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + &HttpInstance->Tcp6ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &HttpInstance->Tcp6, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + } + + HttpInstance->Url = AllocateZeroPool (HTTP_URL_BUFFER_LEN); + if (HttpInstance->Url == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (HttpInstance->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip4DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpInstance->Tcp4ChildHandle + ); + } + + if (HttpInstance->Service->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle + ); + } + + if (HttpInstance->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip6DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpInstance->Tcp6ChildHandle + ); + } + + if (HttpInstance->Service->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle + ); + } + + return EFI_UNSUPPORTED; + +} + +/** + Clean up the HTTP child, release all the resources used by it. + + @param[in] HttpInstance The HTTP child to clean up. + +**/ +VOID +HttpCleanProtocol ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + HttpCloseConnection (HttpInstance); + + HttpCloseTcpConnCloseEvent (HttpInstance); + + if (HttpInstance->TimeoutEvent != NULL) { + gBS->CloseEvent (HttpInstance->TimeoutEvent); + HttpInstance->TimeoutEvent = NULL; + } + + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + } + + if (HttpInstance->RemoteHost != NULL) { + FreePool (HttpInstance->RemoteHost); + HttpInstance->RemoteHost = NULL; + } + + if (HttpInstance->MsgParser != NULL) { + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + if (HttpInstance->Url != NULL) { + FreePool (HttpInstance->Url); + HttpInstance->Url = NULL; + } + + NetMapClean (&HttpInstance->TxTokens); + NetMapClean (&HttpInstance->RxTokens); + + if (HttpInstance->TlsSb != NULL && HttpInstance->TlsChildHandle != NULL) { + // + // Destroy the TLS instance. + // + HttpInstance->TlsSb->DestroyChild (HttpInstance->TlsSb, HttpInstance->TlsChildHandle); + HttpInstance->TlsChildHandle = NULL; + } + + if (HttpInstance->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip4DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpInstance->Tcp4ChildHandle + ); + } + + if (HttpInstance->Service->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle + ); + } + + if (HttpInstance->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip6DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpInstance->Tcp6ChildHandle + ); + } + + if (HttpInstance->Service->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle + ); + } + + TlsCloseTxRxEvent (HttpInstance); +} + +/** + Establish TCP connection with HTTP server. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateConnection ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + // + // Connect to Http server + // + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->IsTcp4ConnDone = FALSE; + HttpInstance->Tcp4ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp4->Connect (HttpInstance->Tcp4, &HttpInstance->Tcp4ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp4->Connect() = %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsTcp4ConnDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + Status = HttpInstance->Tcp4ConnToken.CompletionToken.Status; + + } else { + HttpInstance->IsTcp6ConnDone = FALSE; + HttpInstance->Tcp6ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp6->Connect (HttpInstance->Tcp6, &HttpInstance->Tcp6ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp6->Connect() = %r\n", Status)); + return Status; + } + + while(!HttpInstance->IsTcp6ConnDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + Status = HttpInstance->Tcp6ConnToken.CompletionToken.Status; + } + + if (!EFI_ERROR (Status)) { + HttpInstance->State = HTTP_STATE_TCP_CONNECTED; + } + + return Status; +} + +/** + Close existing TCP connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is closed. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCloseConnection ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (HttpInstance->State == HTTP_STATE_TCP_CONNECTED) { + + if (HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp6CloseToken.AbortOnClose = TRUE; + HttpInstance->IsTcp6CloseDone = FALSE; + Status = HttpInstance->Tcp6->Close (HttpInstance->Tcp6, &HttpInstance->Tcp6CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsTcp6CloseDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + } else { + HttpInstance->Tcp4CloseToken.AbortOnClose = TRUE; + HttpInstance->IsTcp4CloseDone = FALSE; + Status = HttpInstance->Tcp4->Close (HttpInstance->Tcp4, &HttpInstance->Tcp4CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsTcp4CloseDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + } + + } + + HttpInstance->State = HTTP_STATE_TCP_CLOSED; + return EFI_SUCCESS; +} + +/** + Configure TCP4 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP4 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_TCP4_CONFIG_DATA *Tcp4CfgData; + EFI_TCP4_ACCESS_POINT *Tcp4AP; + EFI_TCP4_OPTION *Tcp4Option; + + ASSERT (HttpInstance != NULL); + + + Tcp4CfgData = &HttpInstance->Tcp4CfgData; + ZeroMem (Tcp4CfgData, sizeof (EFI_TCP4_CONFIG_DATA)); + + Tcp4CfgData->TypeOfService = HTTP_TOS_DEAULT; + Tcp4CfgData->TimeToLive = HTTP_TTL_DEAULT; + Tcp4CfgData->ControlOption = &HttpInstance->Tcp4Option; + + Tcp4AP = &Tcp4CfgData->AccessPoint; + Tcp4AP->UseDefaultAddress = HttpInstance->IPv4Node.UseDefaultAddress; + if (!Tcp4AP->UseDefaultAddress) { + IP4_COPY_ADDRESS (&Tcp4AP->StationAddress, &HttpInstance->IPv4Node.LocalAddress); + IP4_COPY_ADDRESS (&Tcp4AP->SubnetMask, &HttpInstance->IPv4Node.LocalSubnet); + } + + Tcp4AP->StationPort = HttpInstance->IPv4Node.LocalPort; + Tcp4AP->RemotePort = HttpInstance->RemotePort; + Tcp4AP->ActiveFlag = TRUE; + IP4_COPY_ADDRESS (&Tcp4AP->RemoteAddress, &HttpInstance->RemoteAddr); + + Tcp4Option = Tcp4CfgData->ControlOption; + Tcp4Option->ReceiveBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp4Option->SendBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp4Option->MaxSynBackLog = HTTP_MAX_SYN_BACK_LOG; + Tcp4Option->ConnectionTimeout = HTTP_CONNECTION_TIMEOUT; + Tcp4Option->DataRetries = HTTP_DATA_RETRIES; + Tcp4Option->FinTimeout = HTTP_FIN_TIMEOUT; + Tcp4Option->KeepAliveProbes = HTTP_KEEP_ALIVE_PROBES; + Tcp4Option->KeepAliveTime = HTTP_KEEP_ALIVE_TIME; + Tcp4Option->KeepAliveInterval = HTTP_KEEP_ALIVE_INTERVAL; + Tcp4Option->EnableNagle = TRUE; + Tcp4CfgData->ControlOption = Tcp4Option; + + Status = HttpInstance->Tcp4->Configure (HttpInstance->Tcp4, Tcp4CfgData); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpConfigureTcp4 - %r\n", Status)); + return Status; + } + + Status = HttpCreateTcpConnCloseEvent (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_TCP_CONFIGED; + + return EFI_SUCCESS; +} + +/** + Configure TCP6 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP6 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_TCP6_CONFIG_DATA *Tcp6CfgData; + EFI_TCP6_ACCESS_POINT *Tcp6Ap; + EFI_TCP6_OPTION *Tcp6Option; + + ASSERT (HttpInstance != NULL); + + Tcp6CfgData = &HttpInstance->Tcp6CfgData; + ZeroMem (Tcp6CfgData, sizeof (EFI_TCP6_CONFIG_DATA)); + + Tcp6CfgData->TrafficClass = 0; + Tcp6CfgData->HopLimit = 255; + Tcp6CfgData->ControlOption = &HttpInstance->Tcp6Option; + + Tcp6Ap = &Tcp6CfgData->AccessPoint; + Tcp6Ap->ActiveFlag = TRUE; + Tcp6Ap->StationPort = HttpInstance->Ipv6Node.LocalPort; + Tcp6Ap->RemotePort = HttpInstance->RemotePort; + IP6_COPY_ADDRESS (&Tcp6Ap->StationAddress, &HttpInstance->Ipv6Node.LocalAddress); + IP6_COPY_ADDRESS (&Tcp6Ap->RemoteAddress , &HttpInstance->RemoteIpv6Addr); + + Tcp6Option = Tcp6CfgData->ControlOption; + Tcp6Option->ReceiveBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp6Option->SendBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp6Option->MaxSynBackLog = HTTP_MAX_SYN_BACK_LOG; + Tcp6Option->ConnectionTimeout = HTTP_CONNECTION_TIMEOUT; + Tcp6Option->DataRetries = HTTP_DATA_RETRIES; + Tcp6Option->FinTimeout = HTTP_FIN_TIMEOUT; + Tcp6Option->KeepAliveProbes = HTTP_KEEP_ALIVE_PROBES; + Tcp6Option->KeepAliveTime = HTTP_KEEP_ALIVE_TIME; + Tcp6Option->KeepAliveInterval = HTTP_KEEP_ALIVE_INTERVAL; + Tcp6Option->EnableNagle = TRUE; + + Status = HttpInstance->Tcp6->Configure (HttpInstance->Tcp6, Tcp6CfgData); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpConfigureTcp6 - %r\n", Status)); + return Status; + } + + Status = HttpCreateTcpConnCloseEvent (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_TCP_CONFIGED; + + return EFI_SUCCESS; + +} + +/** + Check existing TCP connection, if in error state, recover TCP4 connection. Then, + connect one TLS session if required. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP4 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp4 ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + EFI_TCP4_CONNECTION_STATE Tcp4State; + + + if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED || HttpInstance->Tcp4 == NULL) { + return EFI_NOT_READY; + } + + Status = HttpInstance->Tcp4->GetModeData( + HttpInstance->Tcp4, + &Tcp4State, + NULL, + NULL, + NULL, + NULL + ); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp4 GetModeData fail - %x\n", Status)); + return Status; + } + + if (Tcp4State == Tcp4StateEstablished) { + return EFI_SUCCESS; + } else if (Tcp4State > Tcp4StateEstablished ) { + HttpCloseConnection(HttpInstance); + } + + Status = HttpCreateConnection (HttpInstance); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp4 Connection fail - %x\n", Status)); + return Status; + } + + // + // Tls session connection. + // + if (HttpInstance->UseHttps) { + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for TLS connection. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + // + // Start the timer, and wait Timeout seconds for connection. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_CONNECTION_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + + Status = TlsConnectSession (HttpInstance, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + return Status; +} + +/** + Check existing TCP connection, if in error state, recover TCP6 connection. Then, + connect one TLS session if required. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP6 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp6 ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + EFI_TCP6_CONNECTION_STATE Tcp6State; + + if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED || HttpInstance->Tcp6 == NULL) { + return EFI_NOT_READY; + } + + Status = HttpInstance->Tcp6->GetModeData ( + HttpInstance->Tcp6, + &Tcp6State, + NULL, + NULL, + NULL, + NULL + ); + + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp6 GetModeData fail - %x\n", Status)); + return Status; + } + + if (Tcp6State == Tcp6StateEstablished) { + return EFI_SUCCESS; + } else if (Tcp6State > Tcp6StateEstablished ) { + HttpCloseConnection(HttpInstance); + } + + Status = HttpCreateConnection (HttpInstance); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp6 Connection fail - %x\n", Status)); + return Status; + } + + // + // Tls session connection. + // + if (HttpInstance->UseHttps) { + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for TLS connection. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + // + // Start the timer, and wait Timeout seconds for connection. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_CONNECTION_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + + Status = TlsConnectSession (HttpInstance, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + return Status; +} + +/** + Initialize Http session. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] Configure The Flag indicates whether need to initialize session. + @param[in] TlsConfigure The Flag indicates whether it's the new Tls session. + + @retval EFI_SUCCESS The initialization of session is done. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitSession ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN BOOLEAN Configure, + IN BOOLEAN TlsConfigure + ) +{ + EFI_STATUS Status; + ASSERT (HttpInstance != NULL); + + // + // Configure Tls session. + // + if (TlsConfigure) { + Status = TlsConfigureSession (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // Configure TCP instance. + // + if (Configure) { + Status = HttpConfigureTcp4 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Connect TCP. + // + Status = HttpConnectTcp4 (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Configure TCP instance. + // + if (Configure) { + Status = HttpConfigureTcp6 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Connect TCP. + // + Status = HttpConnectTcp6 (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; + +} + +/** + Send the HTTP or HTTPS message through TCP4 or TCP6. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] TxString Buffer containing the HTTP message string. + @param[in] TxStringLen Length of the HTTP message string in bytes. + + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTransmitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN UINT8 *TxString, + IN UINTN TxStringLen + ) +{ + EFI_STATUS Status; + EFI_TCP4_IO_TOKEN *Tx4Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_IO_TOKEN *Tx6Token; + EFI_TCP6_PROTOCOL *Tcp6; + UINT8 *TlsRecord; + UINT16 PayloadSize; + NET_FRAGMENT TempFragment; + NET_FRAGMENT Fragment; + UINTN RecordCount; + UINTN RemainingLen; + + Status = EFI_SUCCESS; + TlsRecord = NULL; + PayloadSize = 0; + TempFragment.Len = 0; + TempFragment.Bulk = NULL; + Fragment.Len = 0; + Fragment.Bulk = NULL; + RecordCount = 0; + RemainingLen = 0; + + // + // Need to encrypt data. + // + if (HttpInstance->UseHttps) { + // + // Allocate enough buffer for each TLS plaintext records. + // + TlsRecord = AllocateZeroPool (TLS_RECORD_HEADER_LENGTH + TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH); + if (TlsRecord == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + // + // Allocate enough buffer for all TLS ciphertext records. + // + RecordCount = TxStringLen / TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH + 1; + Fragment.Bulk = AllocateZeroPool (RecordCount * (TLS_RECORD_HEADER_LENGTH + TLS_CIPHERTEXT_RECORD_MAX_PAYLOAD_LENGTH)); + if (Fragment.Bulk == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Encrypt each TLS plaintext records. + // + RemainingLen = TxStringLen; + while (RemainingLen != 0) { + PayloadSize = (UINT16) MIN (TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH, RemainingLen); + + ((TLS_RECORD_HEADER *) TlsRecord)->ContentType = TlsContentTypeApplicationData; + ((TLS_RECORD_HEADER *) TlsRecord)->Version.Major = HttpInstance->TlsConfigData.Version.Major; + ((TLS_RECORD_HEADER *) TlsRecord)->Version.Minor = HttpInstance->TlsConfigData.Version.Minor; + ((TLS_RECORD_HEADER *) TlsRecord)->Length = PayloadSize; + + CopyMem (TlsRecord + TLS_RECORD_HEADER_LENGTH, TxString + (TxStringLen - RemainingLen), PayloadSize); + + Status = TlsProcessMessage ( + HttpInstance, + TlsRecord, + TLS_RECORD_HEADER_LENGTH + PayloadSize, + EfiTlsEncrypt, + &TempFragment + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Record the processed/encrypted Packet. + // + CopyMem (Fragment.Bulk + Fragment.Len, TempFragment.Bulk, TempFragment.Len); + Fragment.Len += TempFragment.Len; + + FreePool (TempFragment.Bulk); + TempFragment.Len = 0; + TempFragment.Bulk = NULL; + + RemainingLen -= (UINTN) PayloadSize; + ZeroMem (TlsRecord, TLS_RECORD_HEADER_LENGTH + TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH); + } + + FreePool (TlsRecord); + TlsRecord = NULL; + } + + if (!HttpInstance->LocalAddressIsIPv6) { + Tcp4 = HttpInstance->Tcp4; + Tx4Token = &Wrap->TcpWrap.Tx4Token; + + if (HttpInstance->UseHttps) { + Tx4Token->Packet.TxData->DataLength = Fragment.Len; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = Fragment.Len; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) Fragment.Bulk; + } else { + Tx4Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + } + + Tx4Token->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp4->Transmit (Tcp4, Tx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + goto ON_ERROR; + } + + } else { + Tcp6 = HttpInstance->Tcp6; + Tx6Token = &Wrap->TcpWrap.Tx6Token; + + if (HttpInstance->UseHttps) { + Tx6Token->Packet.TxData->DataLength = Fragment.Len; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = Fragment.Len; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) Fragment.Bulk; + } else { + Tx6Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + } + + Tx6Token->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp6->Transmit (Tcp6, Tx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + goto ON_ERROR; + } + } + + return Status; + +ON_ERROR: + + if (HttpInstance->UseHttps) { + if (TlsRecord != NULL) { + FreePool (TlsRecord); + TlsRecord = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + } + + return Status; +} + +/** + Check whether the user's token or event has already + been enqueue on HTTP Tx or Rx Token list. + + @param[in] Map The container of either user's transmit or receive + token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +EFI_STATUS +EFIAPI +HttpTokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_HTTP_TOKEN *Token; + EFI_HTTP_TOKEN *TokenInItem; + + Token = (EFI_HTTP_TOKEN *) Context; + TokenInItem = (EFI_HTTP_TOKEN *) Item->Key; + + if (Token == TokenInItem || Token->Event == TokenInItem->Event) { + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + +/** + Check whether the HTTP message associated with Tx4Token or Tx6Token is already sent out. + + @param[in] Map The container of Tx4Token or Tx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_NOT_READY The HTTP message is still queued in the list. + @retval EFI_SUCCESS The HTTP message has been sent out. + +**/ +EFI_STATUS +EFIAPI +HttpTcpNotReady ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *ValueInItem; + + ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value; + + if (!ValueInItem->TcpWrap.IsTxDone) { + return EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +/** + Transmit the HTTP or HTTPS message by processing the associated HTTP token. + + @param[in] Map The container of Tx4Token or Tx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit + queue. + +**/ +EFI_STATUS +EFIAPI +HttpTcpTransmit ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *ValueInItem; + EFI_STATUS Status; + CHAR8 *RequestMsg; + CHAR8 *Url; + UINTN UrlSize; + UINTN RequestMsgSize; + + RequestMsg = NULL; + + ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value; + if (ValueInItem->TcpWrap.IsTxDone) { + return EFI_SUCCESS; + } + + // + // Parse the URI of the remote host. + // + UrlSize = StrLen (ValueInItem->HttpToken->Message->Data.Request->Url) + 1; + Url = AllocatePool (UrlSize); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (ValueInItem->HttpToken->Message->Data.Request->Url, Url, UrlSize); + + // + // Create request message. + // + Status = HttpGenRequestMessage ( + ValueInItem->HttpToken->Message, + Url, + &RequestMsg, + &RequestMsgSize + ); + FreePool (Url); + + if (EFI_ERROR (Status) || NULL == RequestMsg){ + return Status; + } + + ASSERT (RequestMsg != NULL); + + // + // Transmit the request message. + // + Status = HttpTransmitTcp ( + ValueInItem->HttpInstance, + ValueInItem, + (UINT8*) RequestMsg, + RequestMsgSize + ); + FreePool (RequestMsg); + return Status; +} + +/** + Receive the HTTP response by processing the associated HTTP token. + + @param[in] Map The container of Rx4Token or Rx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_SUCCESS The HTTP response is queued into TCP receive + queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +HttpTcpReceive ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + // + // Process the queued HTTP response. + // + return HttpResponseWorker ((HTTP_TOKEN_WRAP *) Item->Value); +} + +/** + Receive the HTTP header by processing the associated HTTP token. + + @param[in] HttpInstance The HTTP instance private data. + @param[in, out] SizeofHeaders The HTTP header length. + @param[in, out] BufferSize The size of buffer to cache the header message. + @param[in] Timeout The time to wait for receiving the header packet. + + @retval EFI_SUCCESS The HTTP header is received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveHeader ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT UINTN *SizeofHeaders, + IN OUT UINTN *BufferSize, + IN EFI_EVENT Timeout + ) +{ + EFI_STATUS Status; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_IO_TOKEN *Rx6Token; + EFI_TCP6_PROTOCOL *Tcp6; + CHAR8 **EndofHeader; + CHAR8 **HttpHeaders; + CHAR8 *Buffer; + NET_FRAGMENT Fragment; + + ASSERT (HttpInstance != NULL); + + EndofHeader = HttpInstance->EndofHeader; + HttpHeaders = HttpInstance->HttpHeaders; + Tcp4 = HttpInstance->Tcp4; + Tcp6 = HttpInstance->Tcp6; + Buffer = NULL; + Rx4Token = NULL; + Rx6Token = NULL; + Fragment.Len = 0; + Fragment.Bulk = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + ASSERT (Tcp6 != NULL); + } else { + ASSERT (Tcp4 != NULL); + } + + if (!HttpInstance->UseHttps) { + Status = HttpCreateTcpRxEventForHeader (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (!HttpInstance->LocalAddressIsIPv6) { + if (!HttpInstance->UseHttps) { + Rx4Token = &HttpInstance->Rx4Token; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (*EndofHeader == NULL) { + if (!HttpInstance->UseHttps) { + HttpInstance->IsRxDone = FALSE; + Rx4Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + Tcp4->Poll (Tcp4); + } + + if (!HttpInstance->IsRxDone) { + // + // Cancel the Token before close its Event. + // + Tcp4->Cancel (HttpInstance->Tcp4, &Rx4Token->CompletionToken); + gBS->CloseEvent (Rx4Token->CompletionToken.Event); + Rx4Token->CompletionToken.Status = EFI_TIMEOUT; + } + + Status = Rx4Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + Fragment.Len = Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength; + Fragment.Bulk = (UINT8 *) Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer; + } else { + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + Status = HttpsReceive (HttpInstance, &Fragment, Timeout); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + } + + // + // Append the response string along with a Null-terminator. + // + *BufferSize = *SizeofHeaders + Fragment.Len; + Buffer = AllocatePool (*BufferSize + 1); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + if (*HttpHeaders != NULL) { + CopyMem (Buffer, *HttpHeaders, *SizeofHeaders); + FreePool (*HttpHeaders); + } + + CopyMem ( + Buffer + *SizeofHeaders, + Fragment.Bulk, + Fragment.Len + ); + *(Buffer + *BufferSize) = '\0'; + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + + // + // Check whether we received end of HTTP headers. + // + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + }; + + // + // Free the buffer. + // + if (Rx4Token != NULL && Rx4Token->Packet.RxData != NULL && Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + Fragment.Bulk = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + } else { + if (!HttpInstance->UseHttps) { + Rx6Token = &HttpInstance->Rx6Token; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (*EndofHeader == NULL) { + if (!HttpInstance->UseHttps) { + HttpInstance->IsRxDone = FALSE; + Rx6Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + Tcp6->Poll (Tcp6); + } + + if (!HttpInstance->IsRxDone) { + // + // Cancel the Token before close its Event. + // + Tcp6->Cancel (HttpInstance->Tcp6, &Rx6Token->CompletionToken); + gBS->CloseEvent (Rx6Token->CompletionToken.Event); + Rx6Token->CompletionToken.Status = EFI_TIMEOUT; + } + + Status = Rx6Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + Fragment.Len = Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength; + Fragment.Bulk = (UINT8 *) Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer; + } else { + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + Status = HttpsReceive (HttpInstance, &Fragment, Timeout); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + } + + // + // Append the response string along with a Null-terminator. + // + *BufferSize = *SizeofHeaders + Fragment.Len; + Buffer = AllocatePool (*BufferSize + 1); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + if (*HttpHeaders != NULL) { + CopyMem (Buffer, *HttpHeaders, *SizeofHeaders); + FreePool (*HttpHeaders); + } + + CopyMem ( + Buffer + *SizeofHeaders, + Fragment.Bulk, + Fragment.Len + ); + *(Buffer + *BufferSize) = '\0'; + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + + // + // Check whether we received end of HTTP headers. + // + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + }; + + // + // Free the buffer. + // + if (Rx6Token != NULL && Rx6Token->Packet.RxData != NULL && Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + Fragment.Bulk = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + } + + // + // Skip the CRLF after the HTTP headers. + // + *EndofHeader = *EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR); + + *SizeofHeaders = *EndofHeader - *HttpHeaders; + + return EFI_SUCCESS; +} + +/** + Receive the HTTP body by processing the associated HTTP token. + + @param[in] Wrap The HTTP token's wrap data. + @param[in] HttpMsg The HTTP message data. + + @retval EFI_SUCCESS The HTTP body is received. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveBody ( + IN HTTP_TOKEN_WRAP *Wrap, + IN EFI_HTTP_MESSAGE *HttpMsg + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + EFI_TCP6_PROTOCOL *Tcp6; + EFI_TCP6_IO_TOKEN *Rx6Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP4_IO_TOKEN *Rx4Token; + + HttpInstance = Wrap->HttpInstance; + Tcp4 = HttpInstance->Tcp4; + Tcp6 = HttpInstance->Tcp6; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + ASSERT (Tcp6 != NULL); + } else { + ASSERT (Tcp4 != NULL); + } + + if (HttpInstance->LocalAddressIsIPv6) { + Rx6Token = &Wrap->TcpWrap.Rx6Token; + Rx6Token ->Packet.RxData->DataLength = (UINT32) MIN (MAX_UINT32, HttpMsg->BodyLength); + Rx6Token ->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) MIN (MAX_UINT32, HttpMsg->BodyLength); + Rx6Token ->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + Rx6Token->CompletionToken.Status = EFI_NOT_READY; + + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + } else { + Rx4Token = &Wrap->TcpWrap.Rx4Token; + Rx4Token->Packet.RxData->DataLength = (UINT32) MIN (MAX_UINT32, HttpMsg->BodyLength); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) MIN (MAX_UINT32, HttpMsg->BodyLength); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + + Rx4Token->CompletionToken.Status = EFI_NOT_READY; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + } + + return EFI_SUCCESS; + +} + +/** + Clean up Tcp Tokens while the Tcp transmission error occurs. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpTcpTokenCleanup ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP6_IO_TOKEN *Rx6Token; + + ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + Rx6Token = &Wrap->TcpWrap.Rx6Token; + + if (Rx6Token->CompletionToken.Event != NULL) { + gBS->CloseEvent (Rx6Token->CompletionToken.Event); + Rx6Token->CompletionToken.Event = NULL; + } + + FreePool (Wrap); + + Rx6Token = &HttpInstance->Rx6Token; + + if (Rx6Token->CompletionToken.Event != NULL) { + gBS->CloseEvent (Rx6Token->CompletionToken.Event); + Rx6Token->CompletionToken.Event = NULL; + } + + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + + } else { + Rx4Token = &Wrap->TcpWrap.Rx4Token; + + if (Rx4Token->CompletionToken.Event != NULL) { + gBS->CloseEvent (Rx4Token->CompletionToken.Event); + Rx4Token->CompletionToken.Event = NULL; + } + + FreePool (Wrap); + + Rx4Token = &HttpInstance->Rx4Token; + + if (Rx4Token->CompletionToken.Event != NULL) { + gBS->CloseEvent (Rx4Token->CompletionToken.Event); + Rx4Token->CompletionToken.Event = NULL; + } + + + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + } + +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpProto.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpProto.h new file mode 100644 index 00000000..552692f5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpProto.h @@ -0,0 +1,612 @@ +/** @file + The header files of miscellaneous routines for HttpDxe driver. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTP_PROTO_H__ +#define __EFI_HTTP_PROTO_H__ + +#define DEF_BUF_LEN 2048 + +#define HTTP_SERVICE_SIGNATURE SIGNATURE_32('H', 't', 't', 'S') + +#define HTTP_SERVICE_FROM_PROTOCOL(a) \ + CR ( \ + (a), \ + HTTP_SERVICE, \ + ServiceBinding, \ + HTTP_SERVICE_SIGNATURE \ + ) + + +// +// The state of HTTP protocol. It starts from UNCONFIGED. +// +#define HTTP_STATE_UNCONFIGED 0 +#define HTTP_STATE_HTTP_CONFIGED 1 +#define HTTP_STATE_TCP_CONFIGED 2 +#define HTTP_STATE_TCP_UNCONFIGED 3 +#define HTTP_STATE_TCP_CONNECTED 4 +#define HTTP_STATE_TCP_CLOSED 5 + +// +// TCP configured data. +// +#define HTTP_TOS_DEAULT 8 +#define HTTP_TTL_DEAULT 255 +#define HTTP_BUFFER_SIZE_DEAULT 65535 +#define HTTP_MAX_SYN_BACK_LOG 5 +#define HTTP_CONNECTION_TIMEOUT 60 +#define HTTP_RESPONSE_TIMEOUT 5 +#define HTTP_DATA_RETRIES 12 +#define HTTP_FIN_TIMEOUT 2 +#define HTTP_KEEP_ALIVE_PROBES 6 +#define HTTP_KEEP_ALIVE_TIME 7200 +#define HTTP_KEEP_ALIVE_INTERVAL 30 + +#define HTTP_URL_BUFFER_LEN 4096 + +typedef struct _HTTP_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE Ip4DriverBindingHandle; + EFI_HANDLE Ip6DriverBindingHandle; + EFI_HANDLE ControllerHandle; + EFI_HANDLE Tcp4ChildHandle; + EFI_HANDLE Tcp6ChildHandle; + LIST_ENTRY ChildrenList; + UINTN ChildrenNumber; + INTN State; +} HTTP_SERVICE; + +typedef struct { + EFI_TCP4_IO_TOKEN Tx4Token; + EFI_TCP4_TRANSMIT_DATA Tx4Data; + EFI_TCP6_IO_TOKEN Tx6Token; + EFI_TCP6_TRANSMIT_DATA Tx6Data; + EFI_TCP4_IO_TOKEN Rx4Token; + EFI_TCP4_RECEIVE_DATA Rx4Data; + EFI_TCP6_IO_TOKEN Rx6Token; + EFI_TCP6_RECEIVE_DATA Rx6Data; + BOOLEAN IsTxDone; + BOOLEAN IsRxDone; + UINTN BodyLen; + EFI_HTTP_METHOD Method; +} HTTP_TCP_TOKEN_WRAP; + +typedef struct { + EFI_TLS_VERSION Version; + EFI_TLS_CONNECTION_END ConnectionEnd; + EFI_TLS_VERIFY VerifyMethod; + EFI_TLS_VERIFY_HOST VerifyHost; + EFI_TLS_SESSION_STATE SessionState; +} TLS_CONFIG_DATA; + +// +// Callback data for HTTP_PARSER_CALLBACK() +// +typedef struct { + UINTN ParseDataLength; + VOID *ParseData; + VOID *Wrap; +} HTTP_CALLBACK_DATA; + +typedef struct _HTTP_PROTOCOL { + UINT32 Signature; + EFI_HTTP_PROTOCOL Http; + EFI_HANDLE Handle; + HTTP_SERVICE *Service; + LIST_ENTRY Link; // Link to all HTTP instance from the service. + BOOLEAN InDestroy; + INTN State; + EFI_HTTP_METHOD Method; + + UINTN StatusCode; + + EFI_EVENT TimeoutEvent; + + EFI_HANDLE Tcp4ChildHandle; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP4_CONFIG_DATA Tcp4CfgData; + EFI_TCP4_OPTION Tcp4Option; + + EFI_TCP4_CONNECTION_TOKEN Tcp4ConnToken; + BOOLEAN IsTcp4ConnDone; + EFI_TCP4_CLOSE_TOKEN Tcp4CloseToken; + BOOLEAN IsTcp4CloseDone; + CHAR8 *RemoteHost; + UINT16 RemotePort; + EFI_IPv4_ADDRESS RemoteAddr; + + EFI_HANDLE Tcp6ChildHandle; + EFI_TCP6_PROTOCOL *Tcp6; + EFI_TCP6_CONFIG_DATA Tcp6CfgData; + EFI_TCP6_OPTION Tcp6Option; + + EFI_TCP6_CONNECTION_TOKEN Tcp6ConnToken; + BOOLEAN IsTcp6ConnDone; + EFI_TCP6_CLOSE_TOKEN Tcp6CloseToken; + BOOLEAN IsTcp6CloseDone; + EFI_IPv6_ADDRESS RemoteIpv6Addr; + + // + // Rx4Token or Rx6Token used for receiving HTTP header. + // + EFI_TCP4_IO_TOKEN Rx4Token; + EFI_TCP4_RECEIVE_DATA Rx4Data; + EFI_TCP6_IO_TOKEN Rx6Token; + EFI_TCP6_RECEIVE_DATA Rx6Data; + BOOLEAN IsRxDone; + + CHAR8 **EndofHeader; + CHAR8 **HttpHeaders; + CHAR8 *CacheBody; + CHAR8 *NextMsg; + UINTN CacheLen; + UINTN CacheOffset; + + // + // HTTP message-body parser. + // + VOID *MsgParser; + HTTP_CALLBACK_DATA CallbackData; + + EFI_HTTP_VERSION HttpVersion; + UINT32 TimeOutMillisec; + BOOLEAN LocalAddressIsIPv6; + + EFI_HTTPv4_ACCESS_POINT IPv4Node; + EFI_HTTPv6_ACCESS_POINT Ipv6Node; + + NET_MAP TxTokens; + NET_MAP RxTokens; + + CHAR8 *Url; + + // + // Https Support + // + BOOLEAN UseHttps; + + EFI_SERVICE_BINDING_PROTOCOL *TlsSb; + EFI_HANDLE TlsChildHandle; /// Tls ChildHandle + TLS_CONFIG_DATA TlsConfigData; + EFI_TLS_PROTOCOL *Tls; + EFI_TLS_CONFIGURATION_PROTOCOL *TlsConfiguration; + EFI_TLS_SESSION_STATE TlsSessionState; + + // + // TlsTxData used for transmitting TLS related messages. + // + EFI_TCP4_IO_TOKEN Tcp4TlsTxToken; + EFI_TCP4_TRANSMIT_DATA Tcp4TlsTxData; + EFI_TCP6_IO_TOKEN Tcp6TlsTxToken; + EFI_TCP6_TRANSMIT_DATA Tcp6TlsTxData; + BOOLEAN TlsIsTxDone; + + // + // TlsRxData used for receiving TLS related messages. + // + EFI_TCP4_IO_TOKEN Tcp4TlsRxToken; + EFI_TCP4_RECEIVE_DATA Tcp4TlsRxData; + EFI_TCP6_IO_TOKEN Tcp6TlsRxToken; + EFI_TCP6_RECEIVE_DATA Tcp6TlsRxData; + BOOLEAN TlsIsRxDone; +} HTTP_PROTOCOL; + +typedef struct { + EFI_HTTP_TOKEN *HttpToken; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP TcpWrap; +} HTTP_TOKEN_WRAP; + + +#define HTTP_PROTOCOL_SIGNATURE SIGNATURE_32('H', 't', 't', 'P') + +#define HTTP_INSTANCE_FROM_PROTOCOL(a) \ + CR ( \ + (a), \ + HTTP_PROTOCOL, \ + Http, \ + HTTP_PROTOCOL_SIGNATURE \ + ) + +/** + The common notify function used in HTTP driver. + + @param[in] Event The event signaled. + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Create events for the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events are created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Close events in the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +HttpCloseTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Create event for the TCP transmit token. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpTxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Create event for the TCP receive token which is used to receive HTTP header. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEventForHeader ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Create event for the TCP receive token which is used to receive HTTP body. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Close Events for Tcp Receive Tokens for HTTP body and HTTP header. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpCloseTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Initialize the HTTP_PROTOCOL structure to the unconfigured state. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] IpVersion Indicate us TCP4 protocol or TCP6 protocol. + + @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitProtocol ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN BOOLEAN IpVersion + ); + +/** + Clean up the HTTP child, release all the resources used by it. + + @param[in] HttpInstance The HTTP child to clean up. + +**/ +VOID +HttpCleanProtocol ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Establish TCP connection with HTTP server. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateConnection ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Close existing TCP connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is closed. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCloseConnection ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Configure TCP4 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP4 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Configure TCP6 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP6 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Check existing TCP connection, if in error state, recover TCP4 connection. Then, + connect one TLS session if required. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP4 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp4 ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Check existing TCP connection, if in error state, recover TCP6 connection. Then, + connect one TLS session if required. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP6 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp6 ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Send the HTTP or HTTPS message through TCP4 or TCP6. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] TxString Buffer containing the HTTP message string. + @param[in] TxStringLen Length of the HTTP message string in bytes. + + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTransmitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN UINT8 *TxString, + IN UINTN TxStringLen + ); + +/** + Check whether the user's token or event has already + been enqueue on HTTP Tx or Rx Token list. + + @param[in] Map The container of either user's transmit or receive + token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +EFI_STATUS +EFIAPI +HttpTokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + Check whether the HTTP message associated with TxToken or Tx6Token is already sent out. + + @param[in] Map The container of TxToken. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_NOT_READY The HTTP message is still queued in the list. + @retval EFI_SUCCESS The HTTP message has been sent out. + +**/ +EFI_STATUS +EFIAPI +HttpTcpNotReady ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + Initialize Http session. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] Configure The Flag indicates whether need to initialize session. + @param[in] TlsConfigure The Flag indicates whether it's the new Tls session. + + @retval EFI_SUCCESS The initialization of session is done. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitSession ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN BOOLEAN Configure, + IN BOOLEAN TlsConfigure + ); + +/** + Transmit the HTTP or HTTPS message by processing the associated HTTP token. + + @param[in] Map The container of TxToken or Tx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit + queue. + +**/ +EFI_STATUS +EFIAPI +HttpTcpTransmit ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + Receive the HTTP response by processing the associated HTTP token. + + @param[in] Map The container of Rx4Token or Rx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_SUCCESS The HTTP response is queued into TCP receive + queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +HttpTcpReceive ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + Receive the HTTP header by processing the associated HTTP token. + + @param[in] HttpInstance The HTTP instance private data. + @param[in, out] SizeofHeaders The HTTP header length. + @param[in, out] BufferSize The size of buffer to cache the header message. + @param[in] Timeout The time to wait for receiving the header packet. + + @retval EFI_SUCCESS The HTTP header is received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveHeader ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT UINTN *SizeofHeaders, + IN OUT UINTN *BufferSize, + IN EFI_EVENT Timeout + ); + +/** + Receive the HTTP body by processing the associated HTTP token. + + @param[in] Wrap The HTTP token's wrap data. + @param[in] HttpMsg The HTTP message data. + + @retval EFI_SUCCESS The HTTP body is received. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveBody ( + IN HTTP_TOKEN_WRAP *Wrap, + IN EFI_HTTP_MESSAGE *HttpMsg + ); + +/** + Clean up Tcp Tokens while the Tcp transmission error occurs. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpTcpTokenCleanup ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + The work function of EfiHttpResponse(). + + @param[in] Wrap Pointer to HTTP token's wrap data. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to complete the operation due to lack of resources. + @retval EFI_NOT_READY Can't find a corresponding TxToken. + +**/ +EFI_STATUS +HttpResponseWorker ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpsSupport.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpsSupport.c new file mode 100644 index 00000000..a53143a6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpsSupport.c @@ -0,0 +1,1890 @@ +/** @file + Miscellaneous routines specific to Https for HttpDxe driver. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "HttpDriver.h" + +/** + Returns the first occurrence of a Null-terminated ASCII sub-string in a Null-terminated + ASCII string and ignore case during the search process. + + This function scans the contents of the ASCII string specified by String + and returns the first occurrence of SearchString and ignore case during the search process. + If SearchString is not found in String, then NULL is returned. If the length of SearchString + is zero, then String is returned. + + If String is NULL, then ASSERT(). + If SearchString is NULL, then ASSERT(). + + @param[in] String A pointer to a Null-terminated ASCII string. + @param[in] SearchString A pointer to a Null-terminated ASCII string to search for. + + @retval NULL If the SearchString does not appear in String. + @retval others If there is a match return the first occurrence of SearchingString. + If the length of SearchString is zero,return String. + +**/ +CHAR8 * +AsciiStrCaseStr ( + IN CONST CHAR8 *String, + IN CONST CHAR8 *SearchString + ) +{ + CONST CHAR8 *FirstMatch; + CONST CHAR8 *SearchStringTmp; + + CHAR8 Src; + CHAR8 Dst; + + // + // ASSERT both strings are less long than PcdMaximumAsciiStringLength + // + ASSERT (AsciiStrSize (String) != 0); + ASSERT (AsciiStrSize (SearchString) != 0); + + if (*SearchString == '\0') { + return (CHAR8 *) String; + } + + while (*String != '\0') { + SearchStringTmp = SearchString; + FirstMatch = String; + + while ((*SearchStringTmp != '\0') + && (*String != '\0')) { + Src = *String; + Dst = *SearchStringTmp; + + if ((Src >= 'A') && (Src <= 'Z')) { + Src += ('a' - 'A'); + } + + if ((Dst >= 'A') && (Dst <= 'Z')) { + Dst += ('a' - 'A'); + } + + if (Src != Dst) { + break; + } + + String++; + SearchStringTmp++; + } + + if (*SearchStringTmp == '\0') { + return (CHAR8 *) FirstMatch; + } + + String = FirstMatch + 1; + } + + return NULL; +} + +/** + The callback function to free the net buffer list. + + @param[in] Arg The opaque parameter. + +**/ +VOID +EFIAPI +FreeNbufList ( + IN VOID *Arg + ) +{ + ASSERT (Arg != NULL); + + NetbufFreeList ((LIST_ENTRY *) Arg); + FreePool (Arg); +} + +/** + Check whether the Url is from Https. + + @param[in] Url The pointer to a HTTP or HTTPS URL string. + + @retval TRUE The Url is from HTTPS. + @retval FALSE The Url is from HTTP. + +**/ +BOOLEAN +IsHttpsUrl ( + IN CHAR8 *Url + ) +{ + CHAR8 *Tmp; + + Tmp = NULL; + + Tmp = AsciiStrCaseStr (Url, HTTPS_FLAG); + if (Tmp != NULL && Tmp == Url) { + return TRUE; + } + + return FALSE; +} + +/** + Creates a Tls child handle, open EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[out] TlsSb Pointer to the TLS SERVICE_BINDING_PROTOCOL. + @param[out] TlsProto Pointer to the EFI_TLS_PROTOCOL instance. + @param[out] TlsConfiguration Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance. + + @return The child handle with opened EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL. + +**/ +EFI_HANDLE +EFIAPI +TlsCreateChild ( + IN EFI_HANDLE ImageHandle, + OUT EFI_SERVICE_BINDING_PROTOCOL **TlsSb, + OUT EFI_TLS_PROTOCOL **TlsProto, + OUT EFI_TLS_CONFIGURATION_PROTOCOL **TlsConfiguration + ) +{ + EFI_STATUS Status; + EFI_HANDLE TlsChildHandle; + + TlsChildHandle = 0; + + // + // Locate TlsServiceBinding protocol. + // + gBS->LocateProtocol ( + &gEfiTlsServiceBindingProtocolGuid, + NULL, + (VOID **) TlsSb + ); + if (*TlsSb == NULL) { + return NULL; + } + + Status = (*TlsSb)->CreateChild (*TlsSb, &TlsChildHandle); + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = gBS->OpenProtocol ( + TlsChildHandle, + &gEfiTlsProtocolGuid, + (VOID **) TlsProto, + ImageHandle, + TlsChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + (*TlsSb)->DestroyChild (*TlsSb, TlsChildHandle); + return NULL; + } + + Status = gBS->OpenProtocol ( + TlsChildHandle, + &gEfiTlsConfigurationProtocolGuid, + (VOID **) TlsConfiguration, + ImageHandle, + TlsChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + (*TlsSb)->DestroyChild (*TlsSb, TlsChildHandle); + return NULL; + } + + return TlsChildHandle; +} + +/** + Create event for the TLS receive and transmit tokens which are used to receive and + transmit TLS related messages. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events are created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCreateTxRxEvent ( + IN OUT HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // For Tcp4TlsTxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->TlsIsTxDone, + &HttpInstance->Tcp4TlsTxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + HttpInstance->Tcp4TlsTxData.Push = TRUE; + HttpInstance->Tcp4TlsTxData.Urgent = FALSE; + HttpInstance->Tcp4TlsTxData.DataLength = 0; + HttpInstance->Tcp4TlsTxData.FragmentCount = 1; + HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsTxData.DataLength; + HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentBuffer = NULL; + HttpInstance->Tcp4TlsTxToken.Packet.TxData = &HttpInstance->Tcp4TlsTxData; + HttpInstance->Tcp4TlsTxToken.CompletionToken.Status = EFI_NOT_READY; + + // + // For Tcp4TlsRxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->TlsIsRxDone, + &HttpInstance->Tcp4TlsRxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + HttpInstance->Tcp4TlsRxData.DataLength = 0; + HttpInstance->Tcp4TlsRxData.FragmentCount = 1; + HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsRxData.DataLength ; + HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentBuffer = NULL; + HttpInstance->Tcp4TlsRxToken.Packet.RxData = &HttpInstance->Tcp4TlsRxData; + HttpInstance->Tcp4TlsRxToken.CompletionToken.Status = EFI_NOT_READY; + } else { + // + // For Tcp6TlsTxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->TlsIsTxDone, + &HttpInstance->Tcp6TlsTxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + HttpInstance->Tcp6TlsTxData.Push = TRUE; + HttpInstance->Tcp6TlsTxData.Urgent = FALSE; + HttpInstance->Tcp6TlsTxData.DataLength = 0; + HttpInstance->Tcp6TlsTxData.FragmentCount = 1; + HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsTxData.DataLength; + HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentBuffer = NULL; + HttpInstance->Tcp6TlsTxToken.Packet.TxData = &HttpInstance->Tcp6TlsTxData; + HttpInstance->Tcp6TlsTxToken.CompletionToken.Status = EFI_NOT_READY; + + // + // For Tcp6TlsRxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->TlsIsRxDone, + &HttpInstance->Tcp6TlsRxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + HttpInstance->Tcp6TlsRxData.DataLength = 0; + HttpInstance->Tcp6TlsRxData.FragmentCount = 1; + HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsRxData.DataLength ; + HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentBuffer = NULL; + HttpInstance->Tcp6TlsRxToken.Packet.RxData = &HttpInstance->Tcp6TlsRxData; + HttpInstance->Tcp6TlsRxToken.CompletionToken.Status = EFI_NOT_READY; + } + + return Status; + +ERROR: + // + // Error handling + // + TlsCloseTxRxEvent (HttpInstance); + + return Status; +} + +/** + Close events in the TlsTxToken and TlsRxToken. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +EFIAPI +TlsCloseTxRxEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + ASSERT (HttpInstance != NULL); + if (!HttpInstance->LocalAddressIsIPv6) { + if (NULL != HttpInstance->Tcp4TlsTxToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp4TlsTxToken.CompletionToken.Event); + HttpInstance->Tcp4TlsTxToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp4TlsRxToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp4TlsRxToken.CompletionToken.Event); + HttpInstance->Tcp4TlsRxToken.CompletionToken.Event = NULL; + } + } else { + if (NULL != HttpInstance->Tcp6TlsTxToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp6TlsTxToken.CompletionToken.Event); + HttpInstance->Tcp6TlsTxToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp6TlsRxToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp6TlsRxToken.CompletionToken.Event); + HttpInstance->Tcp6TlsRxToken.CompletionToken.Event = NULL; + } + } +} + +/** + Read the TlsCaCertificate variable and configure it. + + @param[in, out] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS TlsCaCertificate is configured. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_NOT_FOUND Fail to get 'TlsCaCertificate' variable. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +TlsConfigCertificate ( + IN OUT HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + UINT8 *CACert; + UINTN CACertSize; + UINT32 Index; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *Cert; + UINTN CertArraySizeInBytes; + UINTN CertCount; + UINT32 ItemDataSize; + + CACert = NULL; + CACertSize = 0; + + // + // Try to read the TlsCaCertificate variable. + // + Status = gRT->GetVariable ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + NULL, + &CACertSize, + NULL + ); + + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + // + // Allocate buffer and read the config variable. + // + CACert = AllocatePool (CACertSize); + if (CACert == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gRT->GetVariable ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + NULL, + &CACertSize, + CACert + ); + if (EFI_ERROR (Status)) { + // + // GetVariable still error or the variable is corrupted. + // + goto FreeCACert; + } + + ASSERT (CACert != NULL); + + // + // Sanity check + // + Status = EFI_INVALID_PARAMETER; + CertCount = 0; + ItemDataSize = (UINT32) CACertSize; + while (ItemDataSize > 0) { + if (ItemDataSize < sizeof (EFI_SIGNATURE_LIST)) { + DEBUG ((DEBUG_ERROR, "%a: truncated EFI_SIGNATURE_LIST header\n", + __FUNCTION__)); + goto FreeCACert; + } + + CertList = (EFI_SIGNATURE_LIST *) (CACert + (CACertSize - ItemDataSize)); + + if (CertList->SignatureListSize < sizeof (EFI_SIGNATURE_LIST)) { + DEBUG ((DEBUG_ERROR, + "%a: SignatureListSize too small for EFI_SIGNATURE_LIST\n", + __FUNCTION__)); + goto FreeCACert; + } + + if (CertList->SignatureListSize > ItemDataSize) { + DEBUG ((DEBUG_ERROR, "%a: truncated EFI_SIGNATURE_LIST body\n", + __FUNCTION__)); + goto FreeCACert; + } + + if (!CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { + DEBUG ((DEBUG_ERROR, "%a: only X509 certificates are supported\n", + __FUNCTION__)); + Status = EFI_UNSUPPORTED; + goto FreeCACert; + } + + if (CertList->SignatureHeaderSize != 0) { + DEBUG ((DEBUG_ERROR, "%a: SignatureHeaderSize must be 0 for X509\n", + __FUNCTION__)); + goto FreeCACert; + } + + if (CertList->SignatureSize < sizeof (EFI_SIGNATURE_DATA)) { + DEBUG ((DEBUG_ERROR, + "%a: SignatureSize too small for EFI_SIGNATURE_DATA\n", __FUNCTION__)); + goto FreeCACert; + } + + CertArraySizeInBytes = (CertList->SignatureListSize - + sizeof (EFI_SIGNATURE_LIST)); + if (CertArraySizeInBytes % CertList->SignatureSize != 0) { + DEBUG ((DEBUG_ERROR, + "%a: EFI_SIGNATURE_DATA array not a multiple of SignatureSize\n", + __FUNCTION__)); + goto FreeCACert; + } + + CertCount += CertArraySizeInBytes / CertList->SignatureSize; + ItemDataSize -= CertList->SignatureListSize; + } + if (CertCount == 0) { + DEBUG ((DEBUG_ERROR, "%a: no X509 certificates provided\n", __FUNCTION__)); + goto FreeCACert; + } + + // + // Enumerate all data and erasing the target item. + // + ItemDataSize = (UINT32) CACertSize; + CertList = (EFI_SIGNATURE_LIST *) CACert; + while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + for (Index = 0; Index < CertCount; Index++) { + // + // EfiTlsConfigDataTypeCACertificate + // + Status = HttpInstance->TlsConfiguration->SetData ( + HttpInstance->TlsConfiguration, + EfiTlsConfigDataTypeCACertificate, + Cert->SignatureData, + CertList->SignatureSize - sizeof (Cert->SignatureOwner) + ); + if (EFI_ERROR (Status)) { + goto FreeCACert; + } + + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + + ItemDataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + +FreeCACert: + FreePool (CACert); + return Status; +} + +/** + Read the HttpTlsCipherList variable and configure it for HTTPS session. + + @param[in, out] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The prefered HTTP TLS CipherList is configured. + @retval EFI_NOT_FOUND Fail to get 'HttpTlsCipherList' variable. + @retval EFI_INVALID_PARAMETER The contents of variable are invalid. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + + @retval Others Other error as indicated. + +**/ +EFI_STATUS +TlsConfigCipherList ( + IN OUT HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + UINT8 *CipherList; + UINTN CipherListSize; + + CipherList = NULL; + CipherListSize = 0; + + // + // Try to read the HttpTlsCipherList variable. + // + Status = gRT->GetVariable ( + EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE, + &gEdkiiHttpTlsCipherListGuid, + NULL, + &CipherListSize, + NULL + ); + ASSERT (EFI_ERROR (Status)); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + if (CipherListSize % sizeof (EFI_TLS_CIPHER) != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Allocate buffer and read the config variable. + // + CipherList = AllocatePool (CipherListSize); + if (CipherList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gRT->GetVariable ( + EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE, + &gEdkiiHttpTlsCipherListGuid, + NULL, + &CipherListSize, + CipherList + ); + if (EFI_ERROR (Status)) { + // + // GetVariable still error or the variable is corrupted. + // + goto ON_EXIT; + } + + ASSERT (CipherList != NULL); + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsCipherList, + CipherList, + CipherListSize + ); + +ON_EXIT: + FreePool (CipherList); + + return Status; +} + +/** + Configure TLS session data. + + @param[in, out] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS TLS session data is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsConfigureSession ( + IN OUT HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + // + // TlsConfigData initialization + // + HttpInstance->TlsConfigData.ConnectionEnd = EfiTlsClient; + HttpInstance->TlsConfigData.VerifyMethod = EFI_TLS_VERIFY_PEER; + HttpInstance->TlsConfigData.VerifyHost.Flags = EFI_TLS_VERIFY_FLAG_NO_WILDCARDS; + HttpInstance->TlsConfigData.VerifyHost.HostName = HttpInstance->RemoteHost; + HttpInstance->TlsConfigData.SessionState = EfiTlsSessionNotStarted; + + // + // EfiTlsConnectionEnd, + // EfiTlsVerifyMethod, + // EfiTlsVerifyHost, + // EfiTlsSessionState + // + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsConnectionEnd, + &(HttpInstance->TlsConfigData.ConnectionEnd), + sizeof (EFI_TLS_CONNECTION_END) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsVerifyMethod, + &HttpInstance->TlsConfigData.VerifyMethod, + sizeof (EFI_TLS_VERIFY) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsVerifyHost, + &HttpInstance->TlsConfigData.VerifyHost, + sizeof (EFI_TLS_VERIFY_HOST) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + &(HttpInstance->TlsConfigData.SessionState), + sizeof (EFI_TLS_SESSION_STATE) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Tls Cipher List + // + Status = TlsConfigCipherList (HttpInstance); + if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) { + DEBUG ((EFI_D_ERROR, "TlsConfigCipherList: return %r error.\n", Status)); + return Status; + } + + // + // Tls Config Certificate + // + Status = TlsConfigCertificate (HttpInstance); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TLS Certificate Config Error!\n")); + return Status; + } + + // + // TlsCreateTxRxEvent + // + Status = TlsCreateTxRxEvent (HttpInstance); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + return Status; + +ERROR: + TlsCloseTxRxEvent (HttpInstance); + + return Status; +} + +/** + Transmit the Packet by processing the associated HTTPS token. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Packet The packet to transmit. + + @retval EFI_SUCCESS The packet is transmitted. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCommonTransmit ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN NET_BUF *Packet + ) +{ + EFI_STATUS Status; + VOID *Data; + UINTN Size; + + if ((HttpInstance == NULL) || (Packet == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!HttpInstance->LocalAddressIsIPv6) { + Size = sizeof (EFI_TCP4_TRANSMIT_DATA) + + (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA); + } else { + Size = sizeof (EFI_TCP6_TRANSMIT_DATA) + + (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA); + } + + Data = AllocatePool (Size); + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (!HttpInstance->LocalAddressIsIPv6) { + ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push = TRUE; + ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent = FALSE; + ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize; + + // + // Build the fragment table. + // + ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum; + + NetbufBuildExt ( + Packet, + (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0], + &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount + ); + + HttpInstance->Tcp4TlsTxToken.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data; + + Status = EFI_DEVICE_ERROR; + + // + // Transmit the packet. + // + Status = HttpInstance->Tcp4->Transmit (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsTxToken); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + while (!HttpInstance->TlsIsTxDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + HttpInstance->TlsIsTxDone = FALSE; + Status = HttpInstance->Tcp4TlsTxToken.CompletionToken.Status; + } else { + ((EFI_TCP6_TRANSMIT_DATA *) Data)->Push = TRUE; + ((EFI_TCP6_TRANSMIT_DATA *) Data)->Urgent = FALSE; + ((EFI_TCP6_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize; + + // + // Build the fragment table. + // + ((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum; + + NetbufBuildExt ( + Packet, + (NET_FRAGMENT *) &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentTable[0], + &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount + ); + + HttpInstance->Tcp6TlsTxToken.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data; + + Status = EFI_DEVICE_ERROR; + + // + // Transmit the packet. + // + Status = HttpInstance->Tcp6->Transmit (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsTxToken); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + while (!HttpInstance->TlsIsTxDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + HttpInstance->TlsIsTxDone = FALSE; + Status = HttpInstance->Tcp6TlsTxToken.CompletionToken.Status; + } + +ON_EXIT: + FreePool (Data); + + return Status; +} + +/** + Receive the Packet by processing the associated HTTPS token. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Packet The packet to transmit. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS The Packet is received. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_TIMEOUT The operation is time out. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCommonReceive ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN NET_BUF *Packet, + IN EFI_EVENT Timeout + ) +{ + EFI_TCP4_RECEIVE_DATA *Tcp4RxData; + EFI_TCP6_RECEIVE_DATA *Tcp6RxData; + EFI_STATUS Status; + NET_FRAGMENT *Fragment; + UINT32 FragmentCount; + UINT32 CurrentFragment; + + Tcp4RxData = NULL; + Tcp6RxData = NULL; + + if ((HttpInstance == NULL) || (Packet == NULL)) { + return EFI_INVALID_PARAMETER; + } + + FragmentCount = Packet->BlockOpNum; + Fragment = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT)); + if (Fragment == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Build the fragment table. + // + NetbufBuildExt (Packet, Fragment, &FragmentCount); + + if (!HttpInstance->LocalAddressIsIPv6) { + Tcp4RxData = HttpInstance->Tcp4TlsRxToken.Packet.RxData; + if (Tcp4RxData == NULL) { + return EFI_INVALID_PARAMETER; + } + Tcp4RxData->FragmentCount = 1; + } else { + Tcp6RxData = HttpInstance->Tcp6TlsRxToken.Packet.RxData; + if (Tcp6RxData == NULL) { + return EFI_INVALID_PARAMETER; + } + Tcp6RxData->FragmentCount = 1; + } + + CurrentFragment = 0; + Status = EFI_SUCCESS; + + while (CurrentFragment < FragmentCount) { + if (!HttpInstance->LocalAddressIsIPv6) { + Tcp4RxData->DataLength = Fragment[CurrentFragment].Len; + Tcp4RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len; + Tcp4RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk; + Status = HttpInstance->Tcp4->Receive (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken); + } else { + Tcp6RxData->DataLength = Fragment[CurrentFragment].Len; + Tcp6RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len; + Tcp6RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk; + Status = HttpInstance->Tcp6->Receive (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken); + } + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + while (!HttpInstance->TlsIsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + // + // Poll until some data is received or an error occurs. + // + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } else { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + } + + if (!HttpInstance->TlsIsRxDone) { + // + // Timeout occurs, cancel the receive request. + // + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken); + } else { + HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken); + } + + Status = EFI_TIMEOUT; + goto ON_EXIT; + } else { + HttpInstance->TlsIsRxDone = FALSE; + } + + if (!HttpInstance->LocalAddressIsIPv6) { + Status = HttpInstance->Tcp4TlsRxToken.CompletionToken.Status; + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Fragment[CurrentFragment].Len -= Tcp4RxData->FragmentTable[0].FragmentLength; + if (Fragment[CurrentFragment].Len == 0) { + CurrentFragment++; + } else { + Fragment[CurrentFragment].Bulk += Tcp4RxData->FragmentTable[0].FragmentLength; + } + } else { + Status = HttpInstance->Tcp6TlsRxToken.CompletionToken.Status; + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Fragment[CurrentFragment].Len -= Tcp6RxData->FragmentTable[0].FragmentLength; + if (Fragment[CurrentFragment].Len == 0) { + CurrentFragment++; + } else { + Fragment[CurrentFragment].Bulk += Tcp6RxData->FragmentTable[0].FragmentLength; + } + } + } + +ON_EXIT: + + if (Fragment != NULL) { + FreePool (Fragment); + } + + return Status; +} + +/** + Receive one TLS PDU. An TLS PDU contains an TLS record header and its + corresponding record data. These two parts will be put into two blocks of buffers in the + net buffer. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[out] Pdu The received TLS PDU. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS An TLS PDU is received. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_PROTOCOL_ERROR An unexpected TLS packet was received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsReceiveOnePdu ( + IN OUT HTTP_PROTOCOL *HttpInstance, + OUT NET_BUF **Pdu, + IN EFI_EVENT Timeout + ) +{ + EFI_STATUS Status; + + LIST_ENTRY *NbufList; + + UINT32 Len; + + NET_BUF *PduHdr; + UINT8 *Header; + TLS_RECORD_HEADER RecordHeader; + + NET_BUF *DataSeg; + + NbufList = NULL; + PduHdr = NULL; + Header = NULL; + DataSeg = NULL; + + NbufList = AllocatePool (sizeof (LIST_ENTRY)); + if (NbufList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (NbufList); + + // + // Allocate buffer to receive one TLS header. + // + Len = TLS_RECORD_HEADER_LENGTH; + PduHdr = NetbufAlloc (Len); + if (PduHdr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL); + if (Header == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // First step, receive one TLS header. + // + Status = TlsCommonReceive (HttpInstance, PduHdr, Timeout); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + RecordHeader = *(TLS_RECORD_HEADER *) Header; + if ((RecordHeader.ContentType == TlsContentTypeHandshake || + RecordHeader.ContentType == TlsContentTypeAlert || + RecordHeader.ContentType == TlsContentTypeChangeCipherSpec || + RecordHeader.ContentType == TlsContentTypeApplicationData) && + (RecordHeader.Version.Major == 0x03) && /// Major versions are same. + (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor ==TLS11_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) + ) { + InsertTailList (NbufList, &PduHdr->List); + } else { + Status = EFI_PROTOCOL_ERROR; + goto ON_EXIT; + } + + Len = SwapBytes16(RecordHeader.Length); + if (Len == 0) { + // + // No TLS payload. + // + goto FORM_PDU; + } + + // + // Allocate buffer to receive one TLS payload. + // + DataSeg = NetbufAlloc (Len); + if (DataSeg == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL); + + // + // Second step, receive one TLS payload. + // + Status = TlsCommonReceive (HttpInstance, DataSeg, Timeout); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + InsertTailList (NbufList, &DataSeg->List); + +FORM_PDU: + // + // Form the PDU from a list of PDU. + // + *Pdu = NetbufFromBufList (NbufList, 0, 0, FreeNbufList, NbufList); + if (*Pdu == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + +ON_EXIT: + + if (EFI_ERROR (Status)) { + // + // Free the Nbufs in this NbufList and the NbufList itself. + // + FreeNbufList (NbufList); + } + + return Status; +} + +/** + Connect one TLS session by finishing the TLS handshake process. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS The TLS session is established. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_ABORTED TLS session state is incorrect. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsConnectSession ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_EVENT Timeout + ) +{ + EFI_STATUS Status; + UINT8 *BufferOut; + UINTN BufferOutSize; + NET_BUF *PacketOut; + UINT8 *DataOut; + NET_BUF *Pdu; + UINT8 *BufferIn; + UINTN BufferInSize; + UINT8 *GetSessionDataBuffer; + UINTN GetSessionDataBufferSize; + + BufferOut = NULL; + PacketOut = NULL; + DataOut = NULL; + Pdu = NULL; + BufferIn = NULL; + + // + // Initialize TLS state. + // + HttpInstance->TlsSessionState = EfiTlsSessionNotStarted; + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + &(HttpInstance->TlsSessionState), + sizeof (EFI_TLS_SESSION_STATE) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create ClientHello + // + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + } + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + + // + // Transmit ClientHello + // + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + FreePool (BufferOut); + NetbufFree (PacketOut); + + if (EFI_ERROR (Status)) { + return Status; + } + + while(HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring && \ + ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + // + // Receive one TLS record. + // + Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferInSize = Pdu->TotalSize; + BufferIn = AllocateZeroPool (BufferInSize); + if (BufferIn == NULL) { + NetbufFree (Pdu); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + NetbufCopy (Pdu, 0, (UINT32)BufferInSize, BufferIn); + + NetbufFree (Pdu); + + // + // Handle Receive data. + // + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + BufferIn, + BufferInSize, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + FreePool (BufferIn); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + BufferIn, + BufferInSize, + BufferOut, + &BufferOutSize + ); + } + + FreePool (BufferIn); + + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + + if (BufferOutSize != 0) { + // + // Transmit the response packet. + // + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + NetbufFree (PacketOut); + + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + } + + FreePool (BufferOut); + + // + // Get the session state, then decide whether need to continue handle received packet. + // + GetSessionDataBufferSize = DEF_BUF_LEN; + GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); + if (GetSessionDataBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->GetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + GetSessionDataBuffer, + &GetSessionDataBufferSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (GetSessionDataBuffer); + GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); + if (GetSessionDataBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->GetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + GetSessionDataBuffer, + &GetSessionDataBufferSize + ); + } + if (EFI_ERROR (Status)) { + FreePool(GetSessionDataBuffer); + return Status; + } + + ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE)); + HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer; + + FreePool (GetSessionDataBuffer); + + if(HttpInstance->TlsSessionState == EfiTlsSessionError) { + return EFI_ABORTED; + } + } + + if (HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring) { + Status = EFI_ABORTED; + } + + return Status; +} + +/** + Close the TLS session and send out the close notification message. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TLS session is closed. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCloseSession ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + UINT8 *BufferOut; + UINTN BufferOutSize; + + NET_BUF *PacketOut; + UINT8 *DataOut; + + Status = EFI_SUCCESS; + BufferOut = NULL; + PacketOut = NULL; + DataOut = NULL; + + if (HttpInstance == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance->TlsSessionState = EfiTlsSessionClosing; + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + &(HttpInstance->TlsSessionState), + sizeof (EFI_TLS_SESSION_STATE) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + } + + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + FreePool (BufferOut); + NetbufFree (PacketOut); + + return Status; +} + +/** + Process one message according to the CryptMode. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Message Pointer to the message buffer needed to processed. + If ProcessMode is EfiTlsEncrypt, the message contain the TLS + header and plain text TLS APP payload. + If ProcessMode is EfiTlsDecrypt, the message contain the TLS + header and cipher text TLS APP payload. + @param[in] MessageSize Pointer to the message buffer size. + @param[in] ProcessMode Process mode. + @param[in, out] Fragment Only one Fragment returned after the Message is + processed successfully. + If ProcessMode is EfiTlsEncrypt, the fragment contain the TLS + header and cipher text TLS APP payload. + If ProcessMode is EfiTlsDecrypt, the fragment contain the TLS + header and plain text TLS APP payload. + + @retval EFI_SUCCESS Message is processed successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsProcessMessage ( + IN HTTP_PROTOCOL *HttpInstance, + IN UINT8 *Message, + IN UINTN MessageSize, + IN EFI_TLS_CRYPT_MODE ProcessMode, + IN OUT NET_FRAGMENT *Fragment + ) +{ + EFI_STATUS Status; + UINT8 *Buffer; + UINT32 BufferSize; + UINT32 BytesCopied; + EFI_TLS_FRAGMENT_DATA *FragmentTable; + UINT32 FragmentCount; + EFI_TLS_FRAGMENT_DATA *OriginalFragmentTable; + UINTN Index; + + Status = EFI_SUCCESS; + Buffer = NULL; + BufferSize = 0; + BytesCopied = 0; + FragmentTable = NULL; + OriginalFragmentTable = NULL; + + // + // Rebuild fragment table from BufferIn. + // + FragmentCount = 1; + FragmentTable = AllocateZeroPool (FragmentCount * sizeof (EFI_TLS_FRAGMENT_DATA)); + if (FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + FragmentTable->FragmentLength = (UINT32) MessageSize; + FragmentTable->FragmentBuffer = Message; + + // + // Record the original FragmentTable. + // + OriginalFragmentTable = FragmentTable; + + // + // Process the Message. + // + Status = HttpInstance->Tls->ProcessPacket ( + HttpInstance->Tls, + &FragmentTable, + &FragmentCount, + ProcessMode + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Calculate the size according to FragmentTable. + // + for (Index = 0; Index < FragmentCount; Index++) { + BufferSize += FragmentTable[Index].FragmentLength; + } + + // + // Allocate buffer for processed data. + // + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Copy the new FragmentTable buffer into Buffer. + // + for (Index = 0; Index < FragmentCount; Index++) { + CopyMem ( + (Buffer + BytesCopied), + FragmentTable[Index].FragmentBuffer, + FragmentTable[Index].FragmentLength + ); + BytesCopied += FragmentTable[Index].FragmentLength; + + // + // Free the FragmentBuffer since it has been copied. + // + FreePool (FragmentTable[Index].FragmentBuffer); + } + + Fragment->Len = BufferSize; + Fragment->Bulk = Buffer; + +ON_EXIT: + + if (OriginalFragmentTable != NULL) { + if( FragmentTable == OriginalFragmentTable) { + FragmentTable = NULL; + } + FreePool (OriginalFragmentTable); + OriginalFragmentTable = NULL; + } + + // + // Caller has the responsibility to free the FragmentTable. + // + if (FragmentTable != NULL) { + FreePool (FragmentTable); + FragmentTable = NULL; + } + + return Status; +} + +/** + Receive one fragment decrypted from one TLS record. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in, out] Fragment The received Fragment. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS One fragment is received. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_ABORTED Something wrong decryption the message. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +HttpsReceive ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT NET_FRAGMENT *Fragment, + IN EFI_EVENT Timeout + ) +{ + EFI_STATUS Status; + NET_BUF *Pdu; + TLS_RECORD_HEADER RecordHeader; + UINT8 *BufferIn; + UINTN BufferInSize; + NET_FRAGMENT TempFragment; + UINT8 *BufferOut; + UINTN BufferOutSize; + NET_BUF *PacketOut; + UINT8 *DataOut; + UINT8 *GetSessionDataBuffer; + UINTN GetSessionDataBufferSize; + + Status = EFI_SUCCESS; + Pdu = NULL; + BufferIn = NULL; + BufferInSize = 0; + BufferOut = NULL; + BufferOutSize = 0; + PacketOut = NULL; + DataOut = NULL; + GetSessionDataBuffer = NULL; + GetSessionDataBufferSize = 0; + + // + // Receive only one TLS record + // + Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferInSize = Pdu->TotalSize; + BufferIn = AllocateZeroPool (BufferInSize); + if (BufferIn == NULL) { + Status = EFI_OUT_OF_RESOURCES; + NetbufFree (Pdu); + return Status; + } + + NetbufCopy (Pdu, 0, (UINT32) BufferInSize, BufferIn); + + NetbufFree (Pdu); + + // + // Handle Receive data. + // + RecordHeader = *(TLS_RECORD_HEADER *) BufferIn; + + if ((RecordHeader.ContentType == TlsContentTypeApplicationData) && + (RecordHeader.Version.Major == 0x03) && + (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) + ) { + // + // Decrypt Packet. + // + Status = TlsProcessMessage ( + HttpInstance, + BufferIn, + BufferInSize, + EfiTlsDecrypt, + &TempFragment + ); + + FreePool (BufferIn); + + if (EFI_ERROR (Status)) { + if (Status == EFI_ABORTED) { + // + // Something wrong decryption the message. + // BuildResponsePacket() will be called to generate Error Alert message and send it out. + // + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + } + if (EFI_ERROR (Status)) { + FreePool(BufferOut); + return Status; + } + + if (BufferOutSize != 0) { + PacketOut = NetbufAlloc ((UINT32)BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + NetbufFree (PacketOut); + } + + FreePool(BufferOut); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_ABORTED; + } + + return Status; + } + + // + // Parsing buffer. + // + ASSERT (((TLS_RECORD_HEADER *) (TempFragment.Bulk))->ContentType == TlsContentTypeApplicationData); + + BufferInSize = ((TLS_RECORD_HEADER *) (TempFragment.Bulk))->Length; + BufferIn = AllocateZeroPool (BufferInSize); + if (BufferIn == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + CopyMem (BufferIn, TempFragment.Bulk + TLS_RECORD_HEADER_LENGTH, BufferInSize); + + // + // Free the buffer in TempFragment. + // + FreePool (TempFragment.Bulk); + + } else if ((RecordHeader.ContentType == TlsContentTypeAlert) && + (RecordHeader.Version.Major == 0x03) && + (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) + ) { + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + FreePool (BufferIn); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + BufferIn, + BufferInSize, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + FreePool (BufferIn); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + BufferIn, + BufferInSize, + BufferOut, + &BufferOutSize + ); + } + + FreePool (BufferIn); + + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + + if (BufferOutSize != 0) { + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + NetbufFree (PacketOut); + } + + FreePool (BufferOut); + + // + // Get the session state. + // + GetSessionDataBufferSize = DEF_BUF_LEN; + GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); + if (GetSessionDataBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->GetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + GetSessionDataBuffer, + &GetSessionDataBufferSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (GetSessionDataBuffer); + GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); + if (GetSessionDataBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->GetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + GetSessionDataBuffer, + &GetSessionDataBufferSize + ); + } + if (EFI_ERROR (Status)) { + FreePool (GetSessionDataBuffer); + return Status; + } + + ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE)); + HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer; + + FreePool (GetSessionDataBuffer); + + if(HttpInstance->TlsSessionState == EfiTlsSessionError) { + DEBUG ((EFI_D_ERROR, "TLS Session State Error!\n")); + return EFI_ABORTED; + } + + BufferIn = NULL; + BufferInSize = 0; + } + + Fragment->Bulk = BufferIn; + Fragment->Len = (UINT32) BufferInSize; + + return Status; +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpsSupport.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpsSupport.h new file mode 100644 index 00000000..93564cf1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpDxe/HttpsSupport.h @@ -0,0 +1,265 @@ +/** @file + The header files of miscellaneous routines specific to Https for HttpDxe driver. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_HTTPS_SUPPORT_H__ +#define __EFI_HTTPS_SUPPORT_H__ + +#define HTTPS_DEFAULT_PORT 443 + +#define HTTPS_FLAG "https://" + +/** + Check whether the Url is from Https. + + @param[in] Url The pointer to a HTTP or HTTPS URL string. + + @retval TRUE The Url is from HTTPS. + @retval FALSE The Url is from HTTP. + +**/ +BOOLEAN +IsHttpsUrl ( + IN CHAR8 *Url + ); + +/** + Creates a Tls child handle, open EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[out] TlsSb Pointer to the TLS SERVICE_BINDING_PROTOCOL. + @param[out] TlsProto Pointer to the EFI_TLS_PROTOCOL instance. + @param[out] TlsConfiguration Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance. + + @return The child handle with opened EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL. + +**/ +EFI_HANDLE +EFIAPI +TlsCreateChild ( + IN EFI_HANDLE ImageHandle, + OUT EFI_SERVICE_BINDING_PROTOCOL **TlsSb, + OUT EFI_TLS_PROTOCOL **TlsProto, + OUT EFI_TLS_CONFIGURATION_PROTOCOL **TlsConfiguration + ); + +/** + Create event for the TLS receive and transmit tokens which are used to receive and + transmit TLS related messages. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events are created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCreateTxRxEvent ( + IN OUT HTTP_PROTOCOL *HttpInstance + ); + +/** + Close events in the TlsTxToken and TlsRxToken. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +EFIAPI +TlsCloseTxRxEvent ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Read the TlsCaCertificate variable and configure it. + + @param[in, out] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS TlsCaCertificate is configured. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_NOT_FOUND Fail to get "TlsCaCertificate" variable. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +TlsConfigCertificate ( + IN OUT HTTP_PROTOCOL *HttpInstance + ); + +/** + Configure TLS session data. + + @param[in, out] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS TLS session data is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsConfigureSession ( + IN OUT HTTP_PROTOCOL *HttpInstance + ); + +/** + Transmit the Packet by processing the associated HTTPS token. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Packet The packet to transmit. + + @retval EFI_SUCCESS The packet is transmitted. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCommonTransmit ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN NET_BUF *Packet + ); + +/** + Receive the Packet by processing the associated HTTPS token. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Packet The packet to transmit. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS The Packet is received. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_TIMEOUT The operation is time out. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCommonReceive ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN NET_BUF *Packet, + IN EFI_EVENT Timeout + ); + +/** + Receive one TLS PDU. An TLS PDU contains an TLS record header and its + corresponding record data. These two parts will be put into two blocks of buffers in the + net buffer. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[out] Pdu The received TLS PDU. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS An TLS PDU is received. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_PROTOCOL_ERROR An unexpected TLS packet was received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsReceiveOnePdu ( + IN OUT HTTP_PROTOCOL *HttpInstance, + OUT NET_BUF **Pdu, + IN EFI_EVENT Timeout + ); + +/** + Connect one TLS session by finishing the TLS handshake process. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS The TLS session is established. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_ABORTED TLS session state is incorrect. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsConnectSession ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_EVENT Timeout + ); + +/** + Close the TLS session and send out the close notification message. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TLS session is closed. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCloseSession ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Process one message according to the CryptMode. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Message Pointer to the message buffer needed to processed. + If ProcessMode is EfiTlsEncrypt, the message contain the TLS + header and plain text TLS APP payload. + If ProcessMode is EfiTlsDecrypt, the message contain the TLS + header and cipher text TLS APP payload. + @param[in] MessageSize Pointer to the message buffer size. + @param[in] ProcessMode Process mode. + @param[in, out] Fragment Only one Fragment returned after the Message is + processed successfully. + If ProcessMode is EfiTlsEncrypt, the fragment contain the TLS + header and cipher text TLS APP payload. + If ProcessMode is EfiTlsDecrypt, the fragment contain the TLS + header and plain text TLS APP payload. + + @retval EFI_SUCCESS Message is processed successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsProcessMessage ( + IN HTTP_PROTOCOL *HttpInstance, + IN UINT8 *Message, + IN UINTN MessageSize, + IN EFI_TLS_CRYPT_MODE ProcessMode, + IN OUT NET_FRAGMENT *Fragment + ); + +/** + Receive one fragment decrypted from one TLS record. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in, out] Fragment The received Fragment. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS One fragment is received. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_ABORTED Something wrong decryption the message. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +HttpsReceive ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT NET_FRAGMENT *Fragment, + IN EFI_EVENT Timeout + ); + +#endif + |