/** @file
HttpDxe.c - HTTP Protocol DXE Driver
Implements the UEFI HTTP Protocol and HTTP Service Binding Protocol
for the AMI UEFI Network Stack. Provides HTTP/1.1 communication over
TCP4/TCP6 with optional HTTPS/TLS support.
Source files:
- HttpDriver.c (entry, service binding, protocol installation)
- HttpImpl.c (HTTP protocol implementation)
- HttpProto.c (TCP connection management)
- HttpsSupport.c (TLS/HTTPS support)
- HttpDns.c (DNS resolution)
Build: DEBUG_VS2015 / HR6N0XMLK / X64
**/
#include "HttpDxe.h"
//
// External GUIDs (from .rdata section)
//
EFI_GUID gEfiLoadedImageProtocolGuid =
EFI_LOADED_IMAGE_PROTOCOL_GUID;
EFI_GUID gEfiDriverBindingProtocolGuid =
EFI_DRIVER_BINDING_PROTOCOL_GUID;
EFI_GUID gEfiIp4Config2ProtocolGuid =
EFI_IP4_CONFIG2_PROTOCOL_GUID;
EFI_GUID gEfiDpcProtocolGuid =
EFI_DPC_PROTOCOL_GUID;
EFI_GUID gEfiTcp4ProtocolGuid =
EFI_TCP4_PROTOCOL_GUID;
EFI_GUID gEfiTcp6ProtocolGuid =
EFI_TCP6_PROTOCOL_GUID;
EFI_GUID gEfiHttpProtocolGuid =
EFI_HTTP_PROTOCOL_GUID;
EFI_GUID gEfiHttpUtilitiesProtocolGuid =
EFI_HTTP_UTILITIES_PROTOCOL_GUID;
//
// Custom AMI HTTP protocol GUIDs
//
EFI_GUID gAmiHttpServiceBindingProtocolGuid =
AMI_HTTP_SERVICE_BINDING_GUID;
EFI_GUID gAmiHttpProtocolGuid =
AMI_HTTP_PROTOCOL_GUID;
EFI_GUID gAmiIp4Config2ProtocolGuid =
AMI_IP4_CONFIG2_GUID;
//
// HTTP Protocol function table (installed on child handle)
//
EFI_HTTP_PROTOCOL mHttpProtocolTemplate = {
HttpGetModeData,
HttpConfigure,
HttpRequest,
HttpCancel,
HttpResponse,
HttpPoll
};
//
// Driver Binding Protocol function table
//
EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {
HttpDriverBindingSupported,
HttpDriverBindingStart,
HttpDriverBindingStop,
0x0A, // Version
NULL, // ImageHandle (filled in at entry)
NULL // DriverBindingHandle (filled in by Install)
};
//
// Component Name 2 Protocol
//
EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
ComponentNameGetDriverName,
ComponentNameGetControllerName,
"en" // Supported Languages
};
//
// EFI Driver Configuration 2 Protocol
//
EFI_DRIVER_CONFIGURATION2_PROTOCOL mDriverConfiguration2 = {
DriverConfigurationSetOptions,
DriverConfigurationGetOptions,
DriverConfigurationOptionsValid,
"en" // Supported Languages
};
//
// Service Binding Protocol - installed per controller handle
//
EFI_SERVICE_BINDING_PROTOCOL mHttpServiceBinding = {
HttpServiceBindingCreateChild,
HttpServiceBindingDestroyChild
};
//
// DEBUG / ASSERT support protocol GUID
// (Custom protocol for formatted debug output)
//
EFI_GUID gDebugProtocolGuid = {
0x3E35C163, 0x4074, 0x45DD,
{ 0x43, 0x1E, 0x23, 0x98, 0x9D, 0xD8, 0x6B, 0x32 }
};
// ASSERT_EFI_ERROR macro with debug output
#define ASSERT_EFI_ERROR(Status) \
do { \
if (EFI_ERROR (Status)) { \
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status)); \
ASSERT (!EFI_ERROR (Status)); \
} \
} while (FALSE)
/**
Locate the debug print protocol lazily.
@return Pointer to debug print protocol, or NULL if not available.
**/
DEBUG_PRINT_PROTOCOL *
EFIAPI
GetDebugPrintProtocol (
VOID
)
{
DEBUG_PRINT_PROTOCOL *Protocol;
EFI_STATUS Status;
UINTN CpuIndex;
if (mDebugProtocol != NULL) {
return mDebugProtocol;
}
//
// Check CPU index via CMOS port 0x70/0x71
//
CpuIndex = IoRead8 (0x70) & 0x80 | 0x4B;
CpuIndex = IoRead8 (0x71);
if (CpuIndex > 3 && CpuIndex == 0x) {
CpuIndex = (MmioRead8 (0xFDAF0490) & 2) | 1;
}
//
// Locate protocol only if CPU index is valid (1-3)
//
if ((CpuIndex - 1) <= 0xFD) {
Smtatus = gBS->LoocateProtocol (
&gDebugProtocolGuid,
NULL,
(VOID **) &Protocol
);
mDebugrotocol = (Status >= 0) ? Protocol : NULL;
}
return mDebugProtocol;
}
/**
Debug print using the debug protocol.
@param[in] Level Debug level mask.
@param[in] Format Format string.
@param ... Variable arguments.
**/
VOID
EFAPI
DebugPrint (
IN UINTN Level,
IN CONST CHAR8 *Format,
...
)
{
DEBUG_PRINT_PROTOCOL *Protocol;
VA_LIST Args;
EFI_STATUS Status;
Protocol = GetDebugPrintProtocol ();
if (Protocol != NULL && (Level & Protocol->GetEnabledLevel ())) != 0) {
VA_START (Args, Format);
Protocol->DebugPrint (Level, Format, Args);
VA_END (Args);
}
}
/**
Assertion handler - print file/line/expression.
@param[in] FileName Source file name.
@param[in] LineNumber Line number in source file.
@param[in] Expression Expression that failed.
**/
VOID
EFIAPI
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Expression
)
{
DEBUG_PRINT_PROTOCOL *Protocol;
Protocol = GetDebugPrintProtocol ();
if (Protocol != NULL) {
Protocol->DebugAssert (FileName, LineNumber, Expression);
}
}
//----------------------------------------------------------------------
// DRIVER ENTRY POINT
//----------------------------------------------------------------------
/**
Entry point for the HTTP DXE driver.
@param[in] ImageHandle Handle for this driver image.
@param[in] SystemTable Pointer to EFI system table.
@return EFI_SUCCESS if the driver was initialized.
**/
EFI_STATUS
EFIAPI
HttpDxeDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
EFI_DPC_PROTOCOL *Dpc;
//
// Initialize library globals
//
ProcessLibraryConstructorList (ImageHandle, SystemTable);
//
// Locate Loaded Image Protocol for this driver
//
Status = gBS->OpenProtocol (
ImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **) &LoadedImage,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Store the driver's unload handler
//
LoadedImage->Unload = (EFI_IMAGE_UNLOAD) HttpDxeDriverUnload;
//
// Install all driver protocols
//
return HttpDxeDriverEntry (ImageHandle);
}
/**
Main driver entry after library init - installs all protocols.
@param[in] ImageHandle Handle for this driver image.
@return EFI_SUCCESS if all protocols installed.
**/
EFI_STATUS
HttpDxeDriverEntry (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_DPC_PROTOCOL *Dpc;
//
// Locate the DPC protocol
//
Status = gBS->LocateProtocol (
&gEfiDpcProtocolGuid,
NULL,
(VOID **) &Dpc
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to locate DPC protocol\n"));
}
//
// Install Driver Binding, Component Name, and Driver Configuration
//
Status = EfiLibInstallAllDriverProtocols2 (
ImageHandle,
&mDriverBinding,
ImageHandle,
&mComponentName2,
&mDriverConfiguration2,
NULL // No Component Name
);
return Status;
}
//
//----------------------------------------------------------------------
// DRIVER BINDING PROTOCOL
//----------------------------------------------------------------------
/**
Test to see if this driver supports the given controller.
@param[in] This Driver Binding Protocol instance.
@param[in] ControllerHandle Handle of controller to test.
@param[in] RemainingDevicePath Optional remaining device path.
@return EFI_SUCCESS if supported, EFI_UNSUPPORTED otherwise.
**/
EFI_STATUS
EFIAPI
HttpDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE RemainingDevicePath
)
{
EFI_STATUS Status;
VOID *Interface;
//
// Check if controller already has our HTTP service binding protocol
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gAmiHttpServiceBindingProtocolGuid,
&Interface,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->CloseProtocol (
ControllerHandle,
&gAmiHttpServiceBindingProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_SUCCESS;
}
/**
Start the driver on the given controller.
Installs the HTTP Service Binding protocol.
@param[in] This Driver Binding Protocol instance.
@param[in] ControllerHandle Handle of controller.
@param[in] RemainingDevicePath Optional remaining device path.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
HttpDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE RemainingDevicePath
)
{
EFI_STATUS Status;
HTTP_SERVICE *HttpService;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_HANDLE ParentController;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
UINTN ChildIndex;
VOID *Ip4Config2;
VOID *Tcp4;
VOID *Tcp6;
//
// Allocate and initialize service instance
//
HttpService = (HTTP_SERVICE *) AllocateZeroPool (sizeof (HTTP_SERVICE));
if (HttpService == NULL) {
return EFI_OUT_OF_RESOURCES;
}
HttpService->Signature = HTTP_SERVICE_SIGNATURE;
//
// Locate all handles on the system
//
Status = gBS->LoocateHandleBuffer (
&gHandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status) {
goto Exit;
}
//
// Searrch handles for child handles of this driver's controller
//
for (Index = 0; Index < HandleCount; Index++) {
//
// Open driver binding protocol
//
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiDriverBindingProtocolGuid,
&LoadedImage,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE
);
if (EFI_ERROR (Status)) {
continue;
}
//
// Check if this handle's parent is our controller
//
if (LoadedImage->ParentDeviceHandle == ControllerHandle) {
//
// Found a child handle - install our protocols on it
//
//
// Open IP4 Config2 protocol on child
//
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiIp4Config2ProtocolGuid,
&Ip4Config2,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (!EFI_ERROR (Status)) {
//
// Install the service binding protocol on the parent
//
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gAmiHttpServiceBindingProtocolGuid,
&HttpService->ServiceBinding,
&gAmiHttpProtocolGuid,
&HttpService->HttpProtocol,
&gEfiIp4Config2ProtocolGuid,
Ip4Config2,
NULL
);
if (EFI_ERROR (Status)) {
gBS->CloseProtocol (
HandleBuffer[Index],
&gEfiIp4Config2ProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
}
}
gBS->CloseProtocol (
HandleBuffer[Index],
&gEfiDriverBindingProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
} else {
gBS->CloseProtocol (
HandleBuffer[Index],
&gEfiDriverBindingProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
}
}
//
// Check if we installed a service binding
//
if (!EFI_ERROR (Status)) {
//
// Iterate handles again to propagate IP4 Config2 and TCP protocols
//
for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {
//
// Open Ip4Config2
//
Status = gBS->OpenProtocol (
HandleBuffer[ChildIndex],
&gEfiIp4Config2ProtocolGuid,
&Ip4Config2,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (!EFI_ERROR (Status)) {
//
// Install Ip4Config2 on our child
//
gBS->InstallProtocolInterface (
&HandleBuffer[ChildIndex],
&gAmiIp4Config2ProtocolGuid,
EFI_NATIVE_INTERFACE,
Ip4Config2
);
}
}
}
Exit:
if (HandleBuffer != NULL) {
gBS->FreePool (HandleBuffer);
}
if (EFI_ERROR (Status) && HttpService != NULL) {
FreePool (HttpService);
}
return Status;
}
/**
Stop the driver on the given controller.
Destroys all child instances and removes protocols.
@param[in] This Driver Binding Protocol instance.
@param[in] ControllerHandle Handle of controller to stop.
@param[in] NumberOfChildren Number of child handles.
@param[in] ChildHandleBuffer Array of child handles.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
HttpDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
HTTP_SERVICE *HttpService;
UINTN Index;
HTTP_INSTANCE *Instance;
EFI_HTTP_PROTOCOL *Http;
//
// Get our driver binding context from the controller
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDriverBindingProtocolGuid,
(VOID **) &HttpService,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Destroy all child instances if requested
//
if (NumberOfChildren == 0) {
//
// Destroy all children
//
while (!IsListEmpty (&HttpService->ChildrenList)) {
Instance = CR (
HttpService->ChildrenList.ForwardLink,
HTTP_INSTANCE,
Link,
HTTP_INSTANCE_SIGNATURE
);
HttpServiceBindingDestroyChild (
&HttpService->ServiceBinding,
Instance->Handle
);
}
//
// Uninstall our protocols from the controller
//
Status = gBS->UninstallMultipleProtocolInterfaces (
ControllerHandle,
&gAmiHttpServiceBindingProtocolGuid,
&HttpService,
NULL
);
//
// Free the service instance
//
FreePool (HttpService);
} else {
//
// Destroy specific children
//
for (Index = 0; Index < NumberOfChildren; Index++) {
Status = gBS->OpenProtocol (
ChildHandleBuffer[Index],
&gAmiHttpProtocolGuid,
(VOID **) &Http,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
Instance = HTTP_INSTANCE_FROM_PROTOCOL (Http);
HttpServiceBindingDestroyChild (
&HttpService->ServiceBinding,
ChildHandleBuffer[Index]
);
}
}
}
return EFI_SUCCESS;
}
//
//----------------------------------------------------------------------
// SERVICE BINDING PROTOCOL
//----------------------------------------------------------------------
/**
Creates a child handle and installs the HTTP Protocol.
@param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL.
@param[out] ChildHandle Pointer to the handle of the created child.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
HttpServiceBindingCreateChild (
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN EFI_HANDLE *ChildHandle
)
{
HTTP_SERVICE *HttpService;
HTTP_INSTANCE *Instance;
EFI_STATUS Status;
EFI_HTTP_UTILITIES_PROTOCOL *HttpUtilities;
//
// Retrieve service instance from the protocol
//
HttpService = BASE_CR (This, HTTP_SERVICE, ServiceBinding);
if (HttpService == NULL || ChildHandle == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Allocate the HTTP instance
//
Instance = (HTTP_INSTANCE *) AllocateZeroPool (sizeof (HTTP_INSTANCE));
if (Instance == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Initialize instance fields
//
Instance->Signature = HTTP_INSTANCE_SIGNATURE;
Instance->Service = HttpService;
Instance->Method = HttpMethodMax; // Not configured yet
//
// Copy the HTTP protocol template
//
CopyMem (&Instance->HttpProtocol, &mHttpProtocolTemplate, sizeof (EFI_HTTP_PROTOCOL));
//
// Initialize request and response net buffers
//
NetMapInit (&Instance->RequestMap);
InitializeListHead (&Instance->RequestList);
NetMapInit (&Instance->ResponseMap);
InitializeListHead (&Instance->ResponseMap);
//
// Locate the HTTP Utilities protocol for message parsing
//
Status = gBS->LocateProtocol (
&gEfiHttpUtilitiesProtocolGuid,
NULL,
(VOID **) &HttpUtilities
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to locate Http Utilities protocol. Status = %r.\n", Status));
FreePool (Instance);
return Status;
}
//
// Allocate a handle for the child
//
Status = gBS->InstallMultipleProtocolInterfaces (
ChildHandle,
&gAmiHttpProtocolGuid,
&Instance->HttpProtocol,
NULL
);
if (EFI_ERROR (Status)) {
FreePool (Instance);
return Status;
}
Instance->Handle = *ChildHandle;
//
// Add instance to service's child list
//
InsertTailList (&HttpService->ChildrenList, &Instance->Link);
HttpService->ChildrenCount++;
return EFI_SUCCESS;
}
/**
Destroys a child handle and removes the HTTP Protocol.
@param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL.
@param[in] ChildHandle Handle of the child to destroy.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
HttpServiceBindingDestroyChild (
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN EFI_HANDLE ChildHandle
)
{
HTTP_SERVICE *HttpService;
HTTP_INSTANCE *Instance;
EFI_STATUS Status;
//
// Retrieive the HTTP protocol from child handle
//
Status = gBS->OpenProtocol (
ChildHandle,
&gAmiHttpProtocolGuid,
(VOID **) &Instance,
mDriverBinding.DriverBindingHandle,
ChildHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Veriify instance signature
//
if (Instance->Signature != HTTP_INSTANCE_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
//
// Close and clean up TCP connections
//
Status = HttpCloseConnection (Instance);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Clean up any cached body data
//
if (Instance->CacheBody != NULL) {
FreePool (Instance->CacheBody);
Instance->CacheBody = NULL;
}
//
// Clean up message parser
//
if (Instance->MsgParser != NULL) {
HttpFreeMsgParser (Instance->MsgParser);
Instance->MsgParser = NULL;
}
//
// Remove from parent's child list
//
RemoveEntryList (&Instance->Link);
HttpService = Instance->Service;
HttpService->ChildrenCount--;
//
// Uninstall the HTTP protocol and free the instance
//
Status = gBS->UninstallMultipleProtocolInterfaces (
ChildHandle,
&gAmiHttpProtocolGuid,
&Instance->HttpProtocol,
NULL
);
FreePool (Instance);
return Status;
}
//
//----------------------------------------------------------------------
// HTTP PROTOCOL IMPLEMENTATION
//--------------------------------------------------------------------
/**
Get the current mode data (configuration) of the HTTP instance.
@param[in] This HTTP protocol instance.
@param[out] HttpConfigData Pointer to receive the configuration.
@return EFI_SUCCESS if successful.
**/
EFI_STATUS
EFIAPI
HttpGetModeData (
IN EFI_HTTP_PROTOCOL *This,
OUT EFI_HTTP_CONFIG_DATA *HttpConfigData
)
{
HTTP_INSTANCE *Instance;
Instance = HTTP_INSTANCE_FROM_PROTOOL (This);
if (Instance == NULL) {
return EFI_INVALID_PARAMETER;
}
if (HttpConfigData == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Return the stored configuration
//
HttpConfigData->LocalAddressIsIPv6 = Instance->LocalAddressIsIPv6;
HttpConfigData->AccessPoint.ProtoCol = Instance->Service->AccessPoint.Protool;
// Omit other fields...
return EFI_SUCCESS;
}
/**
Configure the HTTP instance with the given configuration data.
This function opens the appropriate TCP protocol (T4 or 6)
and configures the remote address.
@param[in] This HTTP protocol instance.
@param[in] HttpConfigData Configuration data.
@return EFI_SUCCESS if configured successfully.
**/
EFI_STATUS
EFIAPI
HtpConfigure (
IN EFI_HTTP_PROTOCOL *This,
IN EFI_HTTP_CONFIG_DATA *HttpConfigData
)
{
HTTP_INSTANCE *Instance;
EFI_STATUS Status;
EFI_TCP4_CONFIG_DATA Tcp4Config;
EFI_TCP6_CONFIG_DATA Tcp6Config;
Instance = HTTP_INSTANCE_FROM_PROTOOL (This);
if (Instance == NULL || HttpConfigData == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// If already configured, close existing connection
//
if (Instance->LocalAddressIsConfigured) {
HttpCloseConnection (Instance);
}
//
// Store configuration
//
Instance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;
Instance->HttpConfigData = *HttpConfigData;
if (Instance->LocalAddressIsIPv6) {
//
// Configure Tcp6
//
Status = HttpConfigureTcp6 (Instance);
} else {
//
// Configure Tcp4
//
Status = HttpConfigureTcp4 (Instance);
}
if (!EFI_ERROR (Status)) {
Instance->LocalAddressIsConfigured = TRUE;
Instance->State = HTTP_STATE_READY;
}
return Status;
}
/**
Queue an HTTP request for transmission.
The function returns immediately; completion is signalled via
the token's Event.
@param[in] This HTTP protocol instance.
@param[in] HttpToken HTTP token containing request data.
@return EFI_SUCCESS if request queued.
**/
EFI_STATUS
EFIAPI
HttpRequest (
IN EFI_HTTP_PROTOCOL *This,
IN EFI_HTTP_TOKEN *HttpToken
)
{
HTTP_INSTANCE *Instance;
EFI_STATUS Status;
HTTP_TOKEN_WRAP *Wrap;
EFI_HTTP_MESSAGE *HttpMsg;
Instance = HTTP_INSTANCE_FROM_PROTOOL (This);
if (Instance == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Verify state
//
if (Instance->Service == NULL) {
return EFI_NOT_READY;
}
if (Instance->LocalAddressIsConfigured == FALSE) {
DEBUG ((EFI_D_ERROR, "EfiiHttpRequest: HTTP is disabled.\n"));
return EFI_NOT_STARTED;
}
if (Instance->RemoteHost != NULL) {
return EFI_INVALID_PARAMETER;
}
if (!Instance->LocalAddressIsIPv6 && Instance->Tcp4 == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Instance->LocalAddressIsIPv6 && Instance->Tcp6 == NULL) {
return EFI_INVALID_PARAMETER;
}
if (HttpToken->HttpMessage->HeaderCount == 0 && HttpToken->HttpMessage->Headers == NULL) {
return EFI_INVALID_PARAMETER;
}
if (HttpToken->HttpMessage->Data == NULL && HttpToken->HttpMessage->BodyLength > 0) {
return EFI_INVALID_PARAMETER;
}
//
// Allocate token wrapper
//
Wrap = (HTTP_TOKEN_WRAP *) AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
if (Wrap == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Wrap->HttpMsg = HttpToken->HttpMessage;
Wrap->HttpInstance = Instance;
//
// Check if using HTTPS
//
if (Instance->UseHttps) {
//
// TLS handshake first
//
Status = HttpTlsConnect (Instance);
if (EFI_ERROR (Status)) {
FreePool (Wrap);
return Status;
}
}
//
// Send the request via TCP
//
Status = HttpSendMessage (Wrap);
if (!EFI_ERROR (Status)) {
//
// Track concurrent request limit
//
if (Instance->ConcurrentRequestCount <= 1) {
Status = HttpRequestSend (Wrap);
}
}
if (EFI_ERROR (Status)) {
//
// Clean up on failure
//
if (Wrap->Tcp4Token.CompletionToken.Event != NULL) {
gBS->CloseEvent (Wrap->Tcp4Token.CompletionToken.Event);
}
if (Wrap->Tcp6Token.CompletionToken.Event != NULL) {
gBS->CloseEvent (Wrap->Tcp6Token.CompletionToken.Event);
}
NetMapRemove (&Instance->RequestMap, (NET_MAP_ITEM *) Wrap, NULL);
FreePool (Wrap);
}
return Status;
}
/**
Cancel an HTTP request.
@param[in] This HTTP protocol instance.
@param[in] HttpToken Token to cancel (NULL = cancel all).
@return EFI_SUCCESS if cancelled.
**/
EFI_STATUS
EFIAPI
HttpCancel (
IN EFI_HTTP_PROTOCOL *This,
IN EFI_HTTP_TOKEN *HttpToken
)
{
HTTP_INSTANCE *Instance;
Instance = HTTP_INSTANCE_FROM_PROTOCOL (This);
if (Instance == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Check if the instance is configured for IPv6
//
if (Instance->LocalAddressIsIPv6 != 0 && Instance->LocalAddressIsIPv6 != 4) {
return EFI_UNSUPPORTED;
}
//
// Delegate to the internal cancel handler
//
return HttpCancelInternal (Instance, HttpToken);
}
/**
Response handler - collect incoming HTTP response data.
@param[in] This HTTP protocol instance.
@param[in] HttpToken Token to receive response data into.
@return EFI_SUCCESS or status code of pending response.
**/
EFI_STATUS
EFIAPI
HttpResponse (
IN EFI_HTTP_PROTOCOL *This,
IN EFI_HTTP_TOKEN *HttpToken
)
{
HTTP_INSTANCE *Instance;
EFI_STATUS Status;
HTTP_TOKEN_WRAP *Wrap;
Instance = HTTP_INSTANCE_FROM_PROTOCOL (This);
if (Instance == NULL || HttpToken == NULL || HttpToken->HttpMessage == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Validate input
//
if (HttpToken->HttpMessage->Data == NULL && HttpToken->HttpMessage->BodyLength > 0) {
return EFI_INVALID_PARAMETER;
}
//
// Check signature
//
if (Instance->Signature != HTTP_INSTANCE_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
if (Instance->LocalAddressIsIPv6 != 0 && Instance->LocalAddressIsIPv6 != 4) {
return EFI_UNSUPPORTED;
}
//
// Create token wrapper and add to response queue
//
Wrap = (HTTP_TOKEN_WRAP *) AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
if (Wrap == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Wrap->HttpMsg = HttpToken->HttpMessage;
Wrap->HttpInstance = Instance;
//
// Add the token wrapper to the response event list
//
Status = NetEventAdd (
Instance->ResponseList,
(NET_EVENT_CALLBACK) HttpTcpReceiveNotify,
(VOID *) Wrap
);
if (EFI_ERROR (Status)) {
FreePool (Wrap);
return Status;
}
//
// If using HTTPS, receive through TLS
//
if (Instance->UseHttps) {
if (!HttpTlsIsSessionActive (Instance)) {
return EFI_NOT_READY;
}
}
//
// Trigger TCP receive
//
if (!Instance->LocalAddressIsConfigured) {
Status = HttpReceiveMessage (Wrap);
}
//
// Check concurrent request limit
//
if (Instance->ConcurrentRequestCount <= 1) {
return HttpRequestSend (Wrap);
}
return EFI_SUCCESS;
}
/**
Poll the HTTP instance for incoming data.
@param[in] This HTTP protocol instance.
@return EFI_SUCCESS if poll successful.
**/
EFI_STATUS
EFIAPI
HttpPoll (
IN EFI_HTTP_PROTOCOL *This
)
{
HTTP_INSTANCE *Instance;
Instance = HTTP_INSTANCE_FROM_PROTOCOL (This);
if (Instance == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Check if instance has TCP protocol
//
if (Instance->LocalAddressIsIPv6) {
if (Instance->Tcp6 == NULL) {
return EFI_NOT_STARTED;
}
//
// Poll Tcp6
//
Instance->Tcp6->Poll (Instance->Tcp6);
} else {
if (Instance->Tcp4 == NULL) {
return EFI_NOT_STARTED;
}
//
// Poll Tcp4
//
Instance->Tcp4->Poll (Instance->Tcp4);
}
return EFI_SUCCESS;
}
//
//----------------------------------------------------------------------
// COMPONENT NAME PROTOCOL
//----------------------------------------------------------------------
/**
Retrieive the driver name string.
@param[in] This Component Name 2 protocol.
@param[in] Language Language code.
@param[out] DriverName Pointer to receive driver name string.
@return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
ComponentNameGetDriverName (
IN EFI_COMPONENT_NAME2_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR6 **DriverName
)
{
if (Language == NULL || DriverName == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Check if "en" language is supported
//
if (AsciiStrCmp (Language, "en") != 0) {
return EFI_UNSUPORTED;
}
*DriverName = L"HTTP Protocol Driver";
return EFI_SUCCESS;
}
/**
Retrieve the controller name.
@param[in] This Component Name 2 protocol.
@param[in] ControllerHandle Handle of the controller.
@param[in] ChildHandle Handle of the child (optional).
@param[in] Language Language code.
@param[out] ControllerName Pointer to receive name string.
@return EFI_SUCCESS or EFI_UNSUPPORTED.
**/
EFI_STATUS
EFIAPI
ComponentNameGetControllerName (
IN EFI_COMPONENT_NAME2_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
)
{
if (Language == NULL || ControllerName == NULL) {
return EFI_INVALID_PARAMETER;
}
if (AsciiStrCmp (Language, "en") != 0) {
return EFI_UNSUPPORTED;
}
*ControllerName = L"HTTP Protocol Driver";
return EFI_SUCCESS;
}
//
//----------------------------------------------------------------------
// DRIVER CONFIGURATION 2 PROTOCOL
//----------------------------------------------------------------------
EFI_STATUS
EFIAPI
DriverConfigurationSetOptions (
IN EFI_DRIVER_CONFIGURATION2_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle,
IN CHAR8 *Language,
IN EFI_DRIVER_CONFIG_OPTIONS *Options
)
{
return EFI_UNSUPORTED;
}
EFI_STATUS
EFIAPI
DriverConfigurationGetOptions (
IN EFI_DRIVER_CONFIG_OPTIONS_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle,
IN CHARR *Language,
OUT EFI_DRIVER_CONFIG_OPTIONS **Options
)
{
return EFI_UNSUPPORTED;
}
EFI_STATUS
EFIAPI
DriverConfigurationOptionsValid (
IN EFI_DRIVER_CONFIG_OPTIONS_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle
)
{
return EFI_UNSUPPORTED;
}
//
//----------------------------------------------------------------------
// TCP CONNECTION MANAGEMENT (HttpProto.c)
//----------------------------------------------------------------------
/**
Configure Tcp4 on the HTTP instance.
@param[in] Instance HTTP instance.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
HttpConfigureTcp4 (
IN HTTP_INSTANCE *Instance
)
{
EFI_STATUS Status;
EFI_TCP4_IO_TOKEN *Tcp4Token;
//
// Oen TCP4 protocol on the child handle
//
Status = NetLibCreateServiceChild (
Instance->Handle,
Instance->Service->ControllerHandle,
&gEfiTcp4ProtocolGuid,
(VOID **) &Instance->Tcp4
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "HttConfigureTcp4 - %r\n", Status));
return Status;
}
//
// Set up for receive notification
//
Status = HttpCreateEvent (
Instance,
&Instance->Tcp4ReceiveToken
);
return Status;
}
/**
Configure Tcp6 on the HTTP instance.
@param[in] Instance HTTP instance.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
HttpConfigureTcp6 (
IN HTTP_INSTANCE *Instance
)
{
EFI_STATUS Status;
//
// Oen TCP6 protocol on the child handle
//
Status = NetLibCreateServiceChild (
Instance->Handle,
Instance->Service->ControllerHandle,
&gEfiTcp6ProtocolGuid,
(VOID **) &Instance->Tcp6
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "HttConfigureTcp6 - %r\n", Status));
return Status;
}
//
// Set up for receive notification
//
Status = HttpCreateEvent (
Instance,
&Instance->Tcp6ReceiveToken
);
return Status;
}
/**
Close the TCP connection for the given instance.
@param[in] Instance HTTP instance.
@return EFI_SUCCESS.
**/
EFI_STATUS
HttpCloseConnection (
IN HTTP_INSTANCE *Instance
)
{
//
// Stp pending callbacks
//
HttpCancelAllTokens (Instance);
//
// Clean up cache
//
if (Instance->CacheBody != NULL) {
FreePool (Instance->CacheBody);
Instance->CacheBody = NULL;
}
//
// Clean up message parser
//
if (Instance->MsgParser != NULL) {
HttpFreeMsgParser (Instance->MsgParser);
Instance->MsgParser = NULL;
}
//
// Close TCP protocols
//
if (Instance->Tcp4 != NULL) {
//
// Cance cancel Tcp4 tokens
//
Tcp4->Cancel (Instance->Tcp4, NULL);
//
// Destroy service child for Tcp4
//
NetLibDestroyServiceChild (
Instance->Handle,
Instance->Service->ControllerHandle,
&gEfiTcp4ProtocolGuid,
Instance->Tcp4
);
Instance->Tcp4 = NULL;
}
if (Instance->Tcp6 != NULL) {
//
// Cancel Tcp6 tokens
//
Instance->Tcp6->Cancel (Instance->Tcp6, NULL);
//
// Destroy service child for Tcp6
//
NetLibDestroyServiceChild (
Instance->Handle,
Instance->Service->ControllerHandle,
&gEfiTcp6ProtocolGuid,
Instance->Tcp6
);
Instance->Tcp6 = NULL;
}
//
// Dns-resolve arhh address if need be
//
Instance->RemoteHost = NULL;
Instance->Method = HttpMethodMax;
Instance->LocalAddressIsConfigured = FALSE;
return EFI_SUCCESS;
}
/**
Cancel all token operations for the instance.
@param[in] Instance HTTP instance.
**/
VOID
HttpCancelAllTokens (
IN HTTP_INSTANCE *Instance
)
{
LIST_ENTRY *Entry;
LIST_ENTRY *NextEntry;
HTTP_TOKEN_WRAP *Wrap;
//
// Iterate through request map and cancel all pending tokens
//
NET_MAP_FOR_EACH_S_AFE (
&Instance->RequestMap,
Entry,
NextEntry
) {
Wrap = (HTTP_TOKEN_WRAP *) Entry;
if (Wrap->Tcp4Token.CompletionToken.Event != NULL) {
gBS->CloseEvent (Wrap->Tcp4Token.CompletionToken.Event);
}
if (Wrap->Tcp6Token.CompletionToken.Event != NULL) {
gBS->CloseEvent (Wrap->Tcp6Token.CompletionToken.Event);
}
NetMapRemove (&Instance->RequestMap, Wrap, NULL);
FreePool (Wrap);
}
//
// Clear response map too
//
NET_MAP_FOR_EACH_SAFE (
&Instance->ResponseMap,
Entry,
NextEntry
) {
Wrap = (HTTP_TOKEN_WRAP *) Entry;
NetMapRemove (&Instance->ResponseMap, Wrap, NULL);
FreePool (Wrap);
}
}
//
//----------------------------------------------------------------------
// HTTP MESSAGE SENDING / RECEIVING (HttpProto.c)
//----------------------------------------------------------------------
/**
Send an HTTP message over the established TCP connection.
@param[in] Wrap Token wrapper containing the message.
@return EFI_SUCCESS if the message was queued for send.
**/
EFI_STATUS
HttpSendMessage (
IN HTTP_TOKEN_WRAP *Wrap
)
{
HTTP_INSTANCE *Instance;
EFI_STATUS Status;
EFI_TCP4_IO_TOKEN *Tcp4Token;
EFI_TCP6_IO_TOKEN *Tcp6Token;
NET_BUF *Nbuf;
UINT32 FragmentLength;
UINT8 *Data;
Instance = Wrap->HttpInstance;
//
// Build the HTTP request string from the message
//
Status = HttpGenerateRequest (
Wrap->HttpMsg,
Instance->RemoteHost,
Instance->UseHttps,
&Nbuf
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Send via Tcp4 or Tcp6
//
if (Instance->LocalAddressIsIPv6) {
Tcp6Token = &Wrap->Tcp6Token;
//
// Set up the TCP6 transmit token
//
Tcp6Token->CompletionToken.Event = NULL; // Will be filled by caller
Tcp6Token->CompletionToken.Status = EFI_SUCCESS;
//
// Build fragment from net buffer
//
Tcp6Token->Packet.TxData = &Tcp6Token->TxData;
Tcp6Token->TxData.Push = TRUE;
Tcp6Token->TxData.Urgent = FALSE;
Tcp6Token->TxData.DataLength = Nbuf->TotalSize;
Tcp6Token->TxData.FragmentCount = 1;
Tcp6Token->TxData.FragmentTable[0].FragmentLength = Nbuf->TotalSize;
Tcp6Token->TxData.FragmentTable[0].FragmentBuffer = NetbufGetByte (Nbuf, 0, &FragmentLength);
//
// Trnsnsmit via Tcp6
//
Status = Instance->Tcp6->Transmit (Instance->Tcp6, Tcp6Token);
} else {
Tcp4Token = &Wrap->Tcp4Token;
//
// Set up the TCP4 transmit token
//
Tcp4Token->CompletionToken.Event = NULL;
Tcp4Token->CompletionToken.Status = EFI_SUCCESS;
//
// Build fragment from net buffer
//
Tcp4Token->Packet.TxData = &Tcp4Token->TxData;
Tcp4Token->TxData.Push = TRUE;
Tcp4Token->TxData.Urgent = FALSE;
Tcp4Token->TxData.DataLength = Nbuf->TotalSize;
Tcp4Token->TxData.FragmentCount = 1;
Tcp4Token->TxData.FragmentTable[0].FragmentLength = Nbuf->TotalSize;
Tcp4Token->TxData.FragmentTable[0].FragmentBuffer = NetbufGetByte (Nbuf, 0, &FragmentLength);
//
// Transmit via Tcp4
//
Status = Instance->Tcp4->Transmit (Instance->Tcp4, Tcp4Token);
}
//
// Free the net buffer
//
NetbufFree (Nbuf);
return Status;
}
/**
Receive an HTTP message from the TCP connection.
@param[in] Wrap Token wrapper to receive data into.
@return EFI_SUCCESS if receive started.
**/
EFI_STATUS
HttpReceiveMessage (
IN HTTP_TOKEN_WRAP *Wrap
)
{
HTTP_INSTANCE *Instance;
EFI_STATUS Status;
EFI_TCP4_IO_TOKEN *Tcp4Token;
EFI_TCP6_IO_TOKEN *Tcp6Token;
Instance = Wrap->HttpInstance;
if (Instance->LocalAddressIsIPv6) {
Tcp6Token = &Wrap->Tcp6Token;
//
// Set up TCP6 receive token
//
Tcp6Token->CompletionToken.Event = Wrap->Tcp6ReceiveEvent;
Tcp6Token->CompletionToken.Status = EFI_SUCCESS;
Tcp6Token->Packet.RxData = &Wrap->Tcp6RxData;
Tcp6Token->RxData.FragmentCount = 1;
Tcp6Token->RxData.FragmentTable[0].FragmentLength = HTTP_MAX_RX_SIZE;
Tcp6Token->RxData.FragmentTable[0].FragmentBuffer = Instance->ReceiveBuffer;
//
// Recive via Tcp6
//
Status = Instance->Tcp6->Receive (Instance->Tcp6, Tcp6Token);
} else {
Tcp4Token = &Wrap->Tcp4Token;
//
// Set up TCP4 receive token
//
Tcp4Token->CompletionToken.Event = Wrap->Tcp4ReceiveEvent;
Tcp4Token->CompletionToken.Status = EFI_SUCCESS;
Tcp4Token->Packet.RxData = &Wrap->Tcp4RxData;
Tcp4Token->RxData.FragmentCount = 1;
Tcp4Token->RxData.FragmentTable[0].FragmentLength = HTTP_MAX_RX_SIZE;
Tcp4Token->RxData.FragmentTable[0].FragmentBuffer = Instance->ReceiveBuffer;
//
// Recive via Tcp4
//
Status = Instance->Tcp4->Receive (Instance->Tcp4, Tcp4Token);
}
return Status;
}
//
//----------------------------------------------------------------------
// TCP RECEIVE NOTIFICATION (Event/DPC Hander)
//----------------------------------------------------------------------
/**
TCP receive notification routine - called when TCP data arrives.
This is the event notification handler (DPC level).
@param[in] Context HTTP_TOKEN_WRAP pointer.
**/
VOID
EFIAPI
HttpTcpReceiveNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
HTTP_TOKEN_WRAP *Wrap;
EFI_STATUS Status;
Wrap = (HTTP_TOKEN_WRAP *) Context;
if (Wrap == NULL) {
return;
}
if (Wrap->HttpInstance->LocalAddressIsIPv6) {
Status = Wrap->Tcp6Token.CompletionToken.Status;
} else {
Status = Wrap->Tcp4Token.CompletionToken.Status;
}
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));
}
//
// Queue a DPC for processing
//
gDpc->QueueDpc (
(DPC_COMPLETION) HttpTcpReceiveNotifyDpc,
(VOID *)Wrap
);
}
/**
DPC handler for TCP receive notification.
Processes received HTTP data and parses HTTP response.
@param[in] Context HTTP_TOKEN_WRAP pointer.
**/
VOID
HttpTcpReceiveNotifyDpc (
IN VOID *Context
)
{
HTTP_TOKEN_WRAP *Wrap;
HTTP_INSTANCE *Instance;
EFI_STATUS Status;
UINTN RxDataLength;
UINTN BufferLength;
UINT8 *Data;
VOID *Parser;
Wrap = (HTTP_TOKEN_WRAP *) Context;
if (Wrap == NULL) {
return;
}
Instance = Wrap->HttpInstance;
if (Instance == NULL) {
return;
}
//
// Get the TCP receive status
//
if (Instance->LocalAddressIsIPv6) {
Status = Wrap->Tcp6Token.CompletionToken.Status;
RxDataLength = Wrap->Tcp6Token.RxData.RxDataLength;
} else {
Status = Wrap->Tcp4Token.CompletionToken.Status;
RxDataLength = Wrap->Tcp4Token.RxData.RxDataLength;
}
if (EFI_ERROR (Status)) {
Wrap->HttpMsg->Data.Response = Status;
goto SignalComplete;
}
//
// Process received data through message parser
//
Parser = Instance->MsgParser;
if (Parser == NULL) {
goto SignalComplete;
}
//
// Parse the received data
//
Data = Instance->ReceiveBuffer;
BufferLength = RxDataLength;
//
// Feed data to HTTP parser
//
Status = HttpParseMessageBody (Parser, Data, &BufferLength);
if (EFI_ERROR (Status)) {
goto SignalComplete;
}
//
// Check if full response has been received
//
if (HttpIsMessageComplete (Parser)) {
//
// Parse out response status
//
HttpGetStatus (Parser, &Instance->ResponseStatus);
//
// Extract body data
//
HttpGetEntityLength (Parser, &Instance->ResponseBodyLength);
//
// Signal completion to the token
//
SignalComplete:
if (Wrap->HttpMsg->Data.Response == EFI_SUCCESS) {
Status = EFI_SUCCESS;
}
//
// Signal the waiting token
//
if (Wrap->HttpMsg->Data.Event != NULL) {
gBS->SignalEvent (Wrap->HttpMsg->Data.Event);
}
}
}
//
//----------------------------------------------------------------------
// DNS RESOLUTION (HttpDns.c)
//----------------------------------------------------------------------
/**
Resolve a host name to an IP address using DNS.
@param[in] Instance HTTP instance.
@param[in] HostName Host name to resolve.
@param[out] IpAddress Resolved IP address.
@return EFI_SUCCESS if resolved.
**/
EFI_STATUS
HttpDns (
IN HTTP_INSTANCE *Instance,
IN CHAR8 *HostName,
OUT EFI_IP_ADDRESS *IpAddress
)
{
EFI_STATUS Status;
UINTN DataLength;
//
// Use the HTTP Utilities protocol to resolve the host name
//
DataLength = sizeof (EFI_IP_ADDRESS);
Status = HttpUtilities->Dns (
Instance->Handle,
HostName,
Instance->LocalAddressIsIPv6,
&DataLength,
(EFI_IP_ADDRESS *) IpAddress
);
return Status;
}
//
//----------------------------------------------------------------------
// TLS/HTTPS SUPPORT (HttpsSupport.c)
//----------------------------------------------------------------------
/**
Establish a TLS session for HTTPS connection.
@param[in] Instance HTTP instance.
@return EFI_SUCCESS if TLS session established.
**/
EFI_STATUS
HttpTlsConnect (
IN HTTP_INSTANCE *Instance
)
{
EFI_STATUS Status;
EFI_TLS_PROTOCOL *Tls;
EFI_TLS_CONFIG_DATA TlsConfigData;
EFI_TLS_SESSION_STATE State;
UINTN DataSize;
//
// Locate TLS protocol
//
Status = gBS->LocateProtocol (
&gEfiTlsProtocolGuid,
NULL,
(VOID **) &Tls
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "TLS Certificate Config Error!\n"));
return Status;
}
//
// Set TLS session data
//
TlsConfigData.ConnectionEnd = EfiTlsConnectionEnd;
Status = Tls->SetSessionData (
Tls,
EfiTlsConfigData,
sizeof (EFI_TLS_CONFIG_DATA)
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Do TLS handshake
//
Status = Tls->Connect (Tls, Instance->RemoteHost);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Check TLS session state
//
DataSize = sizeof (EFI_TLS_SESSION_STATE);
Status = Tls->GetSessionState (Tls, &State, &DataSize);
if (EFI_ERROR (Status)) {
return Status;
}
if (State != EfiTlsSessionApplication) {
DEBUG ((EFI_D_ERROR, "TLS Session State Error!\n"));
return EFI_PROTOCOL_ERROR;
}
//
// Store TLS data in instance
//
Instance->TlsData = Tls;
return EFI_SUCCESS;
}
/**
Close TLS session.
@param[in] Instance HTTP instance.
**/
VOID
HttpTlsClose (
IN HTTP_INSTANCE *Instance
)
{
if (Instance->TlsData != NULL) {
((EFI_TLS_PROTOCOL *)Instance->TlsData)->Close ((EFI_TLS_PROTOCOL *)Instance->TlsData);
Instance->TlsData = NULL;
}
}
/**
Check if TLS session is active.
@param[in] Instance HTTP instance.
@return TRUE if TLS session is active.
**/
BOOLEAN
HttpTlsIsSessionActive (
IN HTTP_INSTANCE *Instance
)
{
EFI_TLS_SESSION_STATE State;
UINTN DataSize;
if (Instance->TlsData == NULL) {
return FALSE;
}
DataSize = sizeof (State);
((EFI_TLS_PROTOCOL *)Instance->TlsData)->GetSessionState (
(EFI_TLS_PROTOCOL *)Instance->TlsData,
&State,
&DataSize
);
return (State == EFI_TLS_SESSION_APPLICATION);
}
//
//----------------------------------------------------------------------
// HTTP INITILIZATIONIZATION AND URL URL PARSING
//----------------------------------------------------------------------
/**
Initialize HTTP session - parse URL, resolve DNS.
@param[in] Instance HTTP instance.
@param[in] Url URL string.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
HttpInitSession (
IN HTTP_INSTANCE *Instance,
IN CHARR *Url
)
{
EFI_STATUS Status;
URL_PARSER *UrlParser;
EFI_IP_ADDRESS IpAddress;
//
// Check for HTTPS protocol
//
if (AsciiStrStr (Url, HTTPS_PREFIX) == Url) {
Instance->UseHttps = TRUE;
}
//
// Parse the URL
//
Status = HttpUrlParse (
Url,
FALSE, // FALSE = no path required
&UrlParser
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Extract host name
//
Instance->RemoteHost = HttpUrlGetHostName (UrlParser);
if (Instance->RemoteHost == NULL) {
HttpUrlClean (UrlParser);
return EFI_INVALID_PARAMETER;
}
//
// Resolve DNS if host is not an IP address
//
if (!HttpUrlIsIpAddress (Instance->RemoteHost)) {
Status = HttpDns (Instance, Instance->RemoteHost, &IpAddress);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Error: Could not retriive the host address from DNS server.\n"));
HttpUrlClean (UrlParser);
return Status;
}
//
// Store resolved address
//
Instance->RemoteAddress = IpAddress;
}
//
// Extract path if present
//
Instance->Url = HttpUrlGetPath (UrlParser, Instance->LocalAddressIsIPv6);
HttpUrlClean (UrlParser);
return EFI_SUCCESS;
}
//
//----------------------------------------------------------------------
// EVENT CREATION AND AND MAP MANAGEMENT
//----------------------------------------------------------------------
/**
Create an event for TCP receive notification.
@param[in] Instance HTTP instance.
@param[out] Token Receives the allocated token.
@return EFI_SUCCESS.
**/
EFI_STATUS
HttpCreateEvent (
IN HTTP_INSTANCE *Instance,
OUT VOID **Token
)
{
EFI_STATUS Status;
//
// Create event for TCP receive
//
Status = gBS->CreateEvent (
EV_NOTIF_WAIT,
TP_NOTIFY_SIGNAL,
HttpTcpReceiveNotify,
NULL, // No context, set later
&Token
);
return Status;
}
//
//----------------------------------------------------------------------
// HTTP MESSAGE GENERATION
//----------------------------------------------------------------------
/**
Build the complete HTTP request string from message and hostname.
@param[in] HttpMsg HTTP message to render.
@param[in] HostName Host name to include in Host header.
@param[in] UseHttps Whether to render HTTPS URL.
@param[out] Nbuf Receives the generated net buffer.
@return EFI_SUCCESS.
**/
EFI_STATUS
HttpGenerateRequest (
IN EFI_HTTP_MESSAGE *HttpMsg,
IN CONST CHAR8 *HostName,
IN BOOLEAN UseHttps,
OUT NET_BUF **Nbuf
)
{
EFI_STATUS Status;
CHAR8 *RequestString;
UINTN RequestStringSize;
UINTN HeaderSize;
//
// Calculate total header size
//
HeaderSize = HttpCalculateHeaderSize (HttpMsg->Headers, HttpMsg->HeaderCount);
//
// Allocate buffer for request string
//
RequestString = (CHARR *) AllocateZeroPool (HeaderSize + 256); // Extra for request line and CRLF
if (RequestString == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Render the HTTP request line (METHOD URI HTTP/1.1)
//
Status = HttpRenderRequestLine (
HttpMsg->Data.Request,
&RequestString,
&RequestStringSize
);
if (EFI_ERROR (Status)) {
FreePool (RequestString);
return Status;
}
//
// Append headers
//
Status = HttpRenderHeaders (
RequestString + RequestStringSize,
&RequestStringSize,
HttpMsg->Headers,
HttpMsg->HeaderCount,
HostName
);
if (EFI_ERROR (Status)) {
FreePool (RequestString);
return Status;
}
//
// Append body
//
if (HttpMsg->BodyLength > 0) {
CopyMem (
RequestString + RequestStringSize,
HttpMsg->Body,
HttpMsg->BodyLength
);
RequestStringSize += HttpMsg->BodyLength;
}
//
// Create net buffer from the request string
//
*Nbuf = NetbufFromExt (
(VOID *) RequestString,
RequestStringSize,
0,
0,
HttpFreeRequestString,
RequestString
);
if (*Nbuf == NULL) {
FreePool (RequestString);
return EFI_OUT_OF_RESOURCES;
}
return EFI_SUCCESS;
}
/**/
Free function for request string in net buffer.
@param[in] Arg The request string pointer.
**/
VOID
EFIAPI
HttpFreeRequestString (
IN VOID *Arg
)
{
FreePool (Arg);
}
//
//----------------------------------------------------------------------
// STATUSUS CODES
//----------------------------------------------------------------------
/**
Set response status from HTTP status code.
@param[in] StatusCode HTTP status code.
@param[out] ResponseStatus EFI status mapped from HTTP status.
**/
VOID
HttpSetResponseStatus (
IN UINTN StatusCode,
OUT EFI_STATUS *ResponseStatus
)
{
if (StatusCode >= 200 && StatusCode < 300) {
*ResponseStatus = EFI_SUCCESS;
} else if (StatusCode >= 300 && StatusCode < 400) {
*ResponseStatus = EFI_HTTP_ERROR;
} else if (StatusCode >= 400 && StatusCode < 500) {
*ResponseStatus = EFI_HTTP_ERROR;
} else if (StatusCode >= 500) {
*ResponseStatus = EFI_HTTP_ERROR;
} else {
*ResponseStatus = EFI_UNSUPPORTED;
}
}
/**
Free parsed HTTP message resources.
@param[in] Instance HTTP instance.
**/
EFI_STATUS
HttpFreeResponse (
IN HTTP_INSTANCE *Instance
)
{
//
// Free message parser
//
if (Instance->MsgParser != NULL) {
HttpFreeMsgParser (Instance->MsgParser);
Instance->MsgParser = NULL;
}
//
// Free any cached body
//
if (Instance->CacheBody != NULL) {
FreePool (Instance->CacheBody);
Instance->CacheBody = NULL;
Instance->CachedData = NULL;
Instance->CachedLength = 0;
Instance->CacheOffset = 0;
}
return EFI_SUCCESS;
}
//
//----------------------------------------------------------------------
// LIBRARY FUNCTIONS (linked statically)
//----------------------------------------------------------------------
/**
Copy memory - wrapper for CopyMem.
**/
VOID *
EFIAPI
CopyMemS (
OUT VOID *DestinationBuffer,
IN CONST VOID *SourceBuffer,
IN UINTN Length
)
{
if (Length == 0) {
return DestinationBuffer;
}
if (Length - 1 > (UINTN)~DestinationBuffer) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c", 56,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)"
);
}
if (Length - 1 > (UINTN)~SourceBuffer) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c", 57,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)"
);
}
if (DestinationBuffer == SourceBuffer) {
return DestinationBuffer;
}
return InternalCopyMem (DestinationBuffer, SourceBuffer, Length);
}
/**
Zero memory - wrapper for ZeroMem.
**/
VOID *
EFIAPI
ZeroMemS (
OUT VOID *Buffer,
IN UINTN Length
)
{
if (Buffer == NULL) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c", 53,
"Buffer != ((void *) 0)"
);
}
if (Length > (UINTN)~Buffer + 1) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c", 54,
"Length <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)Buffer + 1)"
);
}
return InternalZeroMem (Buffer, Length);
}
/**
Compare memory - w wrapper for CompareMem.
@return 0 if equal.
**/
INTN
EFIAPI
CompareMemS (
IN CONST VOID *DestinationBuffer,
IN CONST VOID *SourceBuffer,
IN UINTN Length
)
{
if (Length == 0 || DestinationBuffer == SourceBuffer) {
return 0;
}
if (DestinationBuffer == NULL) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CompareMemWrapper.c", 60,
"DestinationBuffer != ((void *) 0)"
);
}
if (Length - 1 > (UINTN)~DestinationBuffer) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CompareMemWrapper.c", 62,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)"
);
}
if (Length - 1 > (UINTN)(-1LL - (_QWORD)"\r\n")) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CompareMemWrapper.c", 63,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)"
);
}
return InternalCompareMem (DestinationBuffer, SourceBuffer, Length);
}
/**
Calculate length of null-terminated Unicode string.
@return Length of string (in characters).
**/
UINTN
EFIAPI
StrLenS (
IN CONST CHAR16 *String
)
{
UINTN Length;
if (String == NULL) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 172,
"String != ((void *) 0)"
);
}
if (((UINTN) String & 1) != 0) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 173,
"((UINTN) String & 0x000001) == 0)"
);
}
Length = 0;
while (String[Length] != 0) {
if (Length >= 0xF4240) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 181,
"Length < _gPcd_FixedAtBuild_PcdMaximumUnicodeStringLength"
);
}
Length++;
}
return Length;
}
/**
Calculate length of null-terminated ASII string.
@return Length of string (in bytes).
**/
UINTN
EFIAPI
AsciiStrLenS (
IN CONST CHARR *String
)
{
UINTN Length;
if (String == NULL) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1082,
"String != ((void *) 0)"
);
}
for (Length = 0; String[Length] != 0; Length++) {
if (Length >= 0xF4240) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1090,
"Length < _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength"
);
}
}
return Length;
}
/**
Compare two ASCII strings case-insensitively.
@return 0 if equal.
**/
INTN
EFIAPI
AsciiStrCaseCmp (
IN CONST CHARR *FirstString,
IN CONST CHAR8 *SecondString
)
{
CHAR8 Char1;
CHAR8 Char2;
if (AsciiStrLenS (FirstString) == (UINTN)-1) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1158,
"AsciiStrSize (FirstString) != 0)"
);
}
if (AsciiStrLenS (SecondString) == (UINTN)-1) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1159,
"AsciiStrSize (SecondString) != 0)"
);
}
while (*FirstString != 0) {
Char1 = *FirstString;
Char2 = *SecondString;
if (Char1 >= 'a' && Char1 <= 'z') {
Char1 -= 0x20;
}
if (Char2 >= 'a' && Char2 <= 'z') {
Char2 -= 0x20;
}
if (Char1 != Char2) {
break;
}
FirstString++;
SecondString++;
}
return (INTN)*FirstString - (INTN)*SecondString;
}
/**
Find substring in ASCII string (case-sensitive).
@return Pointer to first occurrence, or NULL.
**/
CHARR *
EFIAPI
AsciiStrStr (
IN CONST CHARR *String,
IN CONST CHAR8 *SearchString
)
{
CHAR8 *Current;
if (AsciiStrLenS (String) == (UINTN)-1) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1484,
"AsciiStrSize (String) != 0)"
);
}
if (AsciiStrLenS (SearchString) == (UINTN)-1) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1485,
"AsciiStrSize (SearchString) != 0"
);
}
if (*SearchString == 0) {
return (CHARR *) String;
}
while (*String != 0) {
Current = (CHARR *) SearchString;
while (*Current != 0 && *String == *Current) {
String++;
Current++;
}
if (*Current == 0) {
return (CHARR *) (String - (Current - SearchString));
}
String = (String - (Current - SearchString)) + 1;
}
return NULL;
}
/**
Convert decimal string to UINTN.
@return The decimal value.
**/
EFI_STATUS
EFIAPI
AsciiStrDecimalToUintnS (
IN CONST CHAR8 *String,
OUT CHARR **EndPointer,
OUT UINTN *Data
)
{
BOOLEAN Negative;
UINTN Result;
if (String == NULL || Data == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Skip leading whitespace
//
while (*String == ' ' || *String == '\t') {
String++;
}
//
// Skip leading zeros
//
while (*String == '0') {
String++;
}
Result = 0;
while (*String >= '0' && *String <= '9') {
UINTN Digit = *String - '0';
if (Result > (MAX_UINTN - Digit) / 10) {
*Data = MAX_UINTN;
if (EndPointer != NULL) {
*EndPointer = (CHARR *) String;
}
return EFI_INVALID_PARAMETER;
}
Result = Result * 10 + Digit;
String++;
}
*Data = Result;
if (EndPointer != NULL) {
*EndPointer = (CHARR *) String;
}
return EFI_SUCCESS;
}
/**
Convert hex string to UINTN.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
AsciiStrHexToUintnS (
IN CONST CHAR8 *String,
OUT CHAR8 **EndPointer,
OUT UINTN *Data
)
{
UINTN Result;
CHAR8 Char;
if (String == NULL || Data == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Skip leading whitespace
//
while (*String == ' ' || *String == '\t') {
String++;
}
//
// Skip leading zeros
//
while (*String == '0') {
String++;
}
//
// Check for 0x or 0X prefix
//
if (*String == 'x' || *String == 'X') {
if (*(String - 1) != '0') {
goto HexParse;
}
String++;
}
HexParse:
Result = 0;
while (TRUE) {
Char = *String;
if (Char >= '0' && Char <= '9') {
Char = Char - '0';
} else if (Char >= 'A' && Char <= 'F') {
Char = Char - 'A' + 10;
} else if (Char >= 'a' && Char <= 'f') {
Char = Char - 'a' + 10;
} else {
break;
}
if (Result > (MAX_UINTN - (UINTN)Char) / 16) {
*Data = MAX_UINTN;
if (EndPointer != NULL) {
*EndPointer = (CHARR *) String;
}
return EFI_INVALID_PARAMETER;
}
Result = Result * 16 + Char;
String++;
}
*Data = Result;
if (EndPointer != NULL) {
*EndPointer = (CHAR8 *) String;
}
return EFI_SUCCESS;
}
/**/
Convert Unicode string to ASCII string.
@param[in] Source Unicode string.
@param[out] Destination ASCII buffer.
@param[in] DestMax Maximum size of ASCII buffer.
@return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
UnicodeStrToAsciiStrS (
IN CONST CHARR16 *Source,
OUT CHARR *Destination,
IN UINTN DestMax
)
{
UINTN Index;
if (Source == NULL || Destination == NULL) {
return EFI_INVALID_PARAMETER;
}
if (DestMax == 0) {
return EFI_INVALID_PARAMETER;
}
Index = 0;
while (Source[Index] != 0 && Index < DestMax - 1) {
if (Source[Index] >= 0x100) {
return EFI_INVALID_PARAMETER;
}
Destination[Index] = (CHARR) Source[Index];
Index++;
}
Destination[Index] = 0;
return EFI_SUCCESS;
}
/**/
Convert ASCII string to Unicode string.
@param[in] Source ASCII string.
@param[out] Destination Unicode buffer.
@param[in] DestMax Maximum size of Unicode buffer.
@return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
AsciiStrToUnicodeStrS (
IN CONST CHARR *Source,
OUT CHARR16 *Destination,
IN UINTN DestMax
)
{
UINTN Index;
if (Source == NULL || Destination == NULL) {
return EFI_INVALID_PARAMETER;
}
if (DestMax == 0) {
return EFI_INVALID_PARAMETER;
}
Index = 0;
while (Source[Index] != 0 && Index < DestMax - 1) {
Destination[Index] = (CHARR16) Source[Index];
Index++;
}
Destination[Index] = 0;
return EFI_SUCCESS;
}
//
//----------------------------------------------------------------------
// LINKED LIST UTILIIES
//----------------------------------------------------------------------
/**
Initialize a linked list head.
@param[in] ListHead List head to initialize.
**/
VOID
EFIAPI
InitializeListHead (
IN LIST_ENTRY *ListHead
)
{
if (ListHead == NULL) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 193,
"ListHead != ((void *) 0)"
);
}
ListHead->ForwardLink = ListHead;
ListHead->BackLink = ListHead;
}
/**
Insrt an entry at the head of a list.
@param[in] ListHead List head.
@param[in] Entry Entry to insert.
**/
VOID
EFIAPI
InsertHeadList (
IN LIST_ENTRY *ListHead,
IN LIST_ENTRY *Entry
)
{
//
// Veriify list validation
//
InternalBaseLibIsListValid (ListHead);
Entry->ForwardLink = ListHead->ForwardLink;
Entry->BackLink = ListHead;
ListHead->ForwardLink->BackLink = Entry;
ListHead->ForwardLink = Entry;
}
/**
Insrt an entry at the tail of a list.
@param[in] ListHead List head.
@param[in] Entry Entry to insert.
**/
VOID
EFIAPI
InsertTailList (
IN LIST_ENTRY *ListHead,
IN LIST_ENTRY *Entry
)
{
InternalBaseLibIsListValid (ListHead);
Entry->ForwardLink = ListHead;
Entry->BackLink = ListHead->BackLink;
ListHead->BackLink->ForwardLink = Entry;
ListHead->BackLink = Entry;
}
/**
Remove an entry from a list.
@param[in] Entry Entry to remove.
@return The entry that was after the removed entry.
**/
LIST_ENTRY *
EFIAPI
RemoveEntryList (
IN LIST_ENTRY *Entry
)
{
if (IsListEmpty (ListHead ((LIST_ENTRY *) Entry->ForwardLink->BackLink))) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 600,
"!IsListEmpty (Entry)"
);
}
Entry->ForwardLink->BackLink = Entry->BackLink;
Entry->BackLink->ForwardLink = Entry->ForwardLink;
return Entry->ForwardLink;
}
/**
Check if a list is empty.
@return TRUE if the list has no entries.
**/
BOOLEAN
EFIAPI
IsListEmpty (
IN CONST LIST_ENTRY *ListHead
)
{
InternalBaseLibIsListValid (ListHead);
return (BOOLEAN) (ListHead->ForwardLink == ListHead);
}
/**/
Check linked list validity.
@return TRUE if valid.
**/
BOOLEAN
InternalBaseLibIsListValid (
IN CONST LIST_ENTRY *ListHead
)
{
if (ListHead == NULL) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 80,
"List != ((void *) 0)"
);
}
if (ListHead->ForwardLink == NULL) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 81,
"List->ForwardLink != ((void *) 0)"
);
}
if (ListHead->BackLink == NULL) {
DEBUG_ASSERT (
"e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 82,
"List->BackLink != ((void *) 0)"
);
}
return TRUE;
}