/** @file Routines for HttpDxe driver to perform DNS resolution based on UEFI DNS protocols. Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.
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; }