audk/NetworkPkg/DnsDxe/DnsImpl.c

2193 lines
64 KiB
C

/** @file
DnsDxe support functions implementation.
Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "DnsImpl.h"
/**
Remove TokenEntry from TokenMap.
@param[in] TokenMap All DNSv4 Token entrys.
@param[in] TokenEntry TokenEntry need to be removed.
@retval EFI_SUCCESS Remove TokenEntry from TokenMap sucessfully.
@retval EFI_NOT_FOUND TokenEntry is not found in TokenMap.
**/
EFI_STATUS
Dns4RemoveTokenEntry (
IN NET_MAP *TokenMap,
IN DNS4_TOKEN_ENTRY *TokenEntry
)
{
NET_MAP_ITEM *Item;
//
// Find the TokenEntry first.
//
Item = NetMapFindKey (TokenMap, (VOID *) TokenEntry);
if (Item != NULL) {
//
// Remove the TokenEntry if it's found in the map.
//
NetMapRemoveItem (TokenMap, Item, NULL);
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}
/**
Remove TokenEntry from TokenMap.
@param[in] TokenMap All DNSv6 Token entrys.
@param[in] TokenEntry TokenEntry need to be removed.
@retval EFI_SUCCESS Remove TokenEntry from TokenMap sucessfully.
@retval EFI_NOT_FOUND TokenEntry is not found in TokenMap.
**/
EFI_STATUS
Dns6RemoveTokenEntry (
IN NET_MAP *TokenMap,
IN DNS6_TOKEN_ENTRY *TokenEntry
)
{
NET_MAP_ITEM *Item;
//
// Find the TokenEntry first.
//
Item = NetMapFindKey (TokenMap, (VOID *) TokenEntry);
if (Item != NULL) {
//
// Remove the TokenEntry if it's found in the map.
//
NetMapRemoveItem (TokenMap, Item, NULL);
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}
/**
This function cancle the token specified by Arg in the Map.
@param[in] Map Pointer to the NET_MAP.
@param[in] Item Pointer to the NET_MAP_ITEM.
@param[in] Arg Pointer to the token to be cancelled. If NULL, all
the tokens in this Map will be cancelled.
This parameter is optional and may be NULL.
@retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token
is not the same as that in the Item, if Arg is not
NULL.
@retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is
cancelled.
**/
EFI_STATUS
EFIAPI
Dns4CancelTokens (
IN NET_MAP *Map,
IN NET_MAP_ITEM *Item,
IN VOID *Arg OPTIONAL
)
{
DNS4_TOKEN_ENTRY *TokenEntry;
NET_BUF *Packet;
UDP_IO *UdpIo;
if ((Arg != NULL) && (Item->Key != Arg)) {
return EFI_SUCCESS;
}
if (Item->Value != NULL) {
//
// If the TokenEntry is a transmit TokenEntry, the corresponding Packet is recorded in
// Item->Value.
//
Packet = (NET_BUF *) (Item->Value);
UdpIo = (UDP_IO *) (*((UINTN *) &Packet->ProtoData[0]));
UdpIoCancelSentDatagram (UdpIo, Packet);
}
//
// Remove TokenEntry from Dns4TxTokens.
//
TokenEntry = (DNS4_TOKEN_ENTRY *) Item->Key;
if (Dns4RemoveTokenEntry (Map, TokenEntry) == EFI_SUCCESS) {
TokenEntry->Token->Status = EFI_ABORTED;
gBS->SignalEvent (TokenEntry->Token->Event);
DispatchDpc ();
}
if (Arg != NULL) {
return EFI_ABORTED;
}
return EFI_SUCCESS;
}
/**
This function cancle the token specified by Arg in the Map.
@param[in] Map Pointer to the NET_MAP.
@param[in] Item Pointer to the NET_MAP_ITEM.
@param[in] Arg Pointer to the token to be cancelled. If NULL, all
the tokens in this Map will be cancelled.
This parameter is optional and may be NULL.
@retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token
is not the same as that in the Item, if Arg is not
NULL.
@retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is
cancelled.
**/
EFI_STATUS
EFIAPI
Dns6CancelTokens (
IN NET_MAP *Map,
IN NET_MAP_ITEM *Item,
IN VOID *Arg OPTIONAL
)
{
DNS6_TOKEN_ENTRY *TokenEntry;
NET_BUF *Packet;
UDP_IO *UdpIo;
if ((Arg != NULL) && (Item->Key != Arg)) {
return EFI_SUCCESS;
}
if (Item->Value != NULL) {
//
// If the TokenEntry is a transmit TokenEntry, the corresponding Packet is recorded in
// Item->Value.
//
Packet = (NET_BUF *) (Item->Value);
UdpIo = (UDP_IO *) (*((UINTN *) &Packet->ProtoData[0]));
UdpIoCancelSentDatagram (UdpIo, Packet);
}
//
// Remove TokenEntry from Dns6TxTokens.
//
TokenEntry = (DNS6_TOKEN_ENTRY *) Item->Key;
if (Dns6RemoveTokenEntry (Map, TokenEntry) == EFI_SUCCESS) {
TokenEntry->Token->Status = EFI_ABORTED;
gBS->SignalEvent (TokenEntry->Token->Event);
DispatchDpc ();
}
if (Arg != NULL) {
return EFI_ABORTED;
}
return EFI_SUCCESS;
}
/**
Get the TokenEntry from the TokensMap.
@param[in] TokensMap All DNSv4 Token entrys
@param[in] Token Pointer to the token to be get.
@param[out] TokenEntry Pointer to TokenEntry corresponding Token.
@retval EFI_SUCCESS Get the TokenEntry from the TokensMap sucessfully.
@retval EFI_NOT_FOUND TokenEntry is not found in TokenMap.
**/
EFI_STATUS
EFIAPI
GetDns4TokenEntry (
IN NET_MAP *TokensMap,
IN EFI_DNS4_COMPLETION_TOKEN *Token,
OUT DNS4_TOKEN_ENTRY **TokenEntry
)
{
LIST_ENTRY *Entry;
NET_MAP_ITEM *Item;
NET_LIST_FOR_EACH (Entry, &TokensMap->Used) {
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
*TokenEntry = (DNS4_TOKEN_ENTRY *) (Item->Key);
if ((*TokenEntry)->Token == Token) {
return EFI_SUCCESS;
}
}
*TokenEntry = NULL;
return EFI_NOT_FOUND;
}
/**
Get the TokenEntry from the TokensMap.
@param[in] TokensMap All DNSv6 Token entrys
@param[in] Token Pointer to the token to be get.
@param[out] TokenEntry Pointer to TokenEntry corresponding Token.
@retval EFI_SUCCESS Get the TokenEntry from the TokensMap sucessfully.
@retval EFI_NOT_FOUND TokenEntry is not found in TokenMap.
**/
EFI_STATUS
EFIAPI
GetDns6TokenEntry (
IN NET_MAP *TokensMap,
IN EFI_DNS6_COMPLETION_TOKEN *Token,
OUT DNS6_TOKEN_ENTRY **TokenEntry
)
{
LIST_ENTRY *Entry;
NET_MAP_ITEM *Item;
NET_LIST_FOR_EACH (Entry, &TokensMap->Used) {
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
*TokenEntry = (DNS6_TOKEN_ENTRY *) (Item->Key);
if ((*TokenEntry)->Token == Token) {
return EFI_SUCCESS;
}
}
*TokenEntry =NULL;
return EFI_NOT_FOUND;
}
/**
Cancel DNS4 tokens from the DNS4 instance.
@param[in] Instance Pointer to the DNS instance context data.
@param[in] Token Pointer to the token to be canceled. If NULL, all
tokens in this instance will be cancelled.
This parameter is optional and may be NULL.
@retval EFI_SUCCESS The Token is cancelled.
@retval EFI_NOT_FOUND The Token is not found.
**/
EFI_STATUS
Dns4InstanceCancelToken (
IN DNS_INSTANCE *Instance,
IN EFI_DNS4_COMPLETION_TOKEN *Token
)
{
EFI_STATUS Status;
DNS4_TOKEN_ENTRY *TokenEntry;
TokenEntry = NULL;
if(Token != NULL ) {
Status = GetDns4TokenEntry (&Instance->Dns4TxTokens, Token, &TokenEntry);
if (EFI_ERROR (Status)) {
return Status;
}
} else {
TokenEntry = NULL;
}
//
// Cancel this TokenEntry from the Dns4TxTokens map.
//
Status = NetMapIterate (&Instance->Dns4TxTokens, Dns4CancelTokens, TokenEntry);
if ((TokenEntry != NULL) && (Status == EFI_ABORTED)) {
//
// If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from
// the Dns4TxTokens and returns success.
//
if (NetMapIsEmpty (&Instance->Dns4TxTokens)) {
Instance->UdpIo->Protocol.Udp4->Cancel (Instance->UdpIo->Protocol.Udp4, &Instance->UdpIo->RecvRequest->Token.Udp4);
}
return EFI_SUCCESS;
}
ASSERT ((TokenEntry != NULL) || (0 == NetMapGetCount (&Instance->Dns4TxTokens)));
if (NetMapIsEmpty (&Instance->Dns4TxTokens)) {
Instance->UdpIo->Protocol.Udp4->Cancel (Instance->UdpIo->Protocol.Udp4, &Instance->UdpIo->RecvRequest->Token.Udp4);
}
return EFI_SUCCESS;
}
/**
Cancel DNS6 tokens from the DNS6 instance.
@param[in] Instance Pointer to the DNS instance context data.
@param[in] Token Pointer to the token to be canceled. If NULL, all
tokens in this instance will be cancelled.
This parameter is optional and may be NULL.
@retval EFI_SUCCESS The Token is cancelled.
@retval EFI_NOT_FOUND The Token is not found.
**/
EFI_STATUS
Dns6InstanceCancelToken (
IN DNS_INSTANCE *Instance,
IN EFI_DNS6_COMPLETION_TOKEN *Token
)
{
EFI_STATUS Status;
DNS6_TOKEN_ENTRY *TokenEntry;
TokenEntry = NULL;
if(Token != NULL ) {
Status = GetDns6TokenEntry (&Instance->Dns6TxTokens, Token, &TokenEntry);
if (EFI_ERROR (Status)) {
return Status;
}
} else {
TokenEntry = NULL;
}
//
// Cancel this TokenEntry from the Dns6TxTokens map.
//
Status = NetMapIterate (&Instance->Dns6TxTokens, Dns6CancelTokens, TokenEntry);
if ((TokenEntry != NULL) && (Status == EFI_ABORTED)) {
//
// If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from
// the Dns6TxTokens and returns success.
//
if (NetMapIsEmpty (&Instance->Dns6TxTokens)) {
Instance->UdpIo->Protocol.Udp6->Cancel (Instance->UdpIo->Protocol.Udp6, &Instance->UdpIo->RecvRequest->Token.Udp6);
}
return EFI_SUCCESS;
}
ASSERT ((TokenEntry != NULL) || (0 == NetMapGetCount (&Instance->Dns6TxTokens)));
if (NetMapIsEmpty (&Instance->Dns6TxTokens)) {
Instance->UdpIo->Protocol.Udp6->Cancel (Instance->UdpIo->Protocol.Udp6, &Instance->UdpIo->RecvRequest->Token.Udp6);
}
return EFI_SUCCESS;
}
/**
Free the resource related to the configure parameters.
@param Config The DNS configure data
**/
VOID
Dns4CleanConfigure (
IN OUT EFI_DNS4_CONFIG_DATA *Config
)
{
if (Config->DnsServerList != NULL) {
FreePool (Config->DnsServerList);
}
ZeroMem (Config, sizeof (EFI_DNS4_CONFIG_DATA));
}
/**
Free the resource related to the configure parameters.
@param Config The DNS configure data
**/
VOID
Dns6CleanConfigure (
IN OUT EFI_DNS6_CONFIG_DATA *Config
)
{
if (Config->DnsServerList != NULL) {
FreePool (Config->DnsServerList);
}
ZeroMem (Config, sizeof (EFI_DNS6_CONFIG_DATA));
}
/**
Allocate memory for configure parameter such as timeout value for Dst,
then copy the configure parameter from Src to Dst.
@param[out] Dst The destination DHCP configure data.
@param[in] Src The source DHCP configure data.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval EFI_SUCCESS The configure is copied.
**/
EFI_STATUS
Dns4CopyConfigure (
OUT EFI_DNS4_CONFIG_DATA *Dst,
IN EFI_DNS4_CONFIG_DATA *Src
)
{
UINTN Len;
UINT32 Index;
CopyMem (Dst, Src, sizeof (*Dst));
Dst->DnsServerList = NULL;
//
// Allocate a memory then copy DnsServerList to it
//
if (Src->DnsServerList != NULL) {
Len = Src->DnsServerListCount * sizeof (EFI_IPv4_ADDRESS);
Dst->DnsServerList = AllocatePool (Len);
if (Dst->DnsServerList == NULL) {
Dns4CleanConfigure (Dst);
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < Src->DnsServerListCount; Index++) {
CopyMem (&Dst->DnsServerList[Index], &Src->DnsServerList[Index], sizeof (EFI_IPv4_ADDRESS));
}
}
return EFI_SUCCESS;
}
/**
Allocate memory for configure parameter such as timeout value for Dst,
then copy the configure parameter from Src to Dst.
@param[out] Dst The destination DHCP configure data.
@param[in] Src The source DHCP configure data.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval EFI_SUCCESS The configure is copied.
**/
EFI_STATUS
Dns6CopyConfigure (
OUT EFI_DNS6_CONFIG_DATA *Dst,
IN EFI_DNS6_CONFIG_DATA *Src
)
{
UINTN Len;
UINT32 Index;
CopyMem (Dst, Src, sizeof (*Dst));
Dst->DnsServerList = NULL;
//
// Allocate a memory then copy DnsServerList to it
//
if (Src->DnsServerList != NULL) {
Len = Src->DnsServerCount * sizeof (EFI_IPv6_ADDRESS);
Dst->DnsServerList = AllocatePool (Len);
if (Dst->DnsServerList == NULL) {
Dns6CleanConfigure (Dst);
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < Src->DnsServerCount; Index++) {
CopyMem (&Dst->DnsServerList[Index], &Src->DnsServerList[Index], sizeof (EFI_IPv6_ADDRESS));
}
}
return EFI_SUCCESS;
}
/**
Callback of Dns packet. Does nothing.
@param Arg The context.
**/
VOID
EFIAPI
DnsDummyExtFree (
IN VOID *Arg
)
{
}
/**
Poll the UDP to get the IP4 default address, which may be retrieved
by DHCP.
The default time out value is 5 seconds. If IP has retrieved the default address,
the UDP is reconfigured.
@param Instance The DNS instance
@param UdpIo The UDP_IO to poll
@param UdpCfgData The UDP configure data to reconfigure the UDP_IO
@retval TRUE The default address is retrieved and UDP is reconfigured.
@retval FALSE Some error occured.
**/
BOOLEAN
Dns4GetMapping (
IN DNS_INSTANCE *Instance,
IN UDP_IO *UdpIo,
IN EFI_UDP4_CONFIG_DATA *UdpCfgData
)
{
DNS_SERVICE *Service;
EFI_IP4_MODE_DATA Ip4Mode;
EFI_UDP4_PROTOCOL *Udp;
EFI_STATUS Status;
ASSERT (Instance->Dns4CfgData.UseDefaultSetting);
Service = Instance->Service;
Udp = UdpIo->Protocol.Udp4;
Status = gBS->SetTimer (
Service->TimerToGetMap,
TimerRelative,
DNS_TIME_TO_GETMAP * TICKS_PER_SECOND
);
if (EFI_ERROR (Status)) {
return FALSE;
}
while (EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) {
Udp->Poll (Udp);
if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip4Mode, NULL, NULL)) &&
Ip4Mode.IsConfigured) {
Udp->Configure (Udp, NULL);
return (BOOLEAN) (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS);
}
}
return FALSE;
}
/**
Configure the opened Udp6 instance until the corresponding Ip6 instance
has been configured.
@param Instance The DNS instance
@param UdpIo The UDP_IO to poll
@param UdpCfgData The UDP configure data to reconfigure the UDP_IO
@retval TRUE Configure the Udp6 instance successfully.
@retval FALSE Some error occured.
**/
BOOLEAN
Dns6GetMapping (
IN DNS_INSTANCE *Instance,
IN UDP_IO *UdpIo,
IN EFI_UDP6_CONFIG_DATA *UdpCfgData
)
{
DNS_SERVICE *Service;
EFI_IP6_MODE_DATA Ip6Mode;
EFI_UDP6_PROTOCOL *Udp;
EFI_STATUS Status;
Service = Instance->Service;
Udp = UdpIo->Protocol.Udp6;
Status = gBS->SetTimer (
Service->TimerToGetMap,
TimerRelative,
DNS_TIME_TO_GETMAP * TICKS_PER_SECOND
);
if (EFI_ERROR (Status)) {
return FALSE;
}
while (EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) {
Udp->Poll (Udp);
if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip6Mode, NULL, NULL))) {
if (Ip6Mode.AddressList != NULL) {
FreePool (Ip6Mode.AddressList);
}
if (Ip6Mode.GroupTable != NULL) {
FreePool (Ip6Mode.GroupTable);
}
if (Ip6Mode.RouteTable != NULL) {
FreePool (Ip6Mode.RouteTable);
}
if (Ip6Mode.NeighborCache != NULL) {
FreePool (Ip6Mode.NeighborCache);
}
if (Ip6Mode.PrefixTable != NULL) {
FreePool (Ip6Mode.PrefixTable);
}
if (Ip6Mode.IcmpTypeList != NULL) {
FreePool (Ip6Mode.IcmpTypeList);
}
if (!Ip6Mode.IsStarted || Ip6Mode.IsConfigured) {
Udp->Configure (Udp, NULL);
if (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS) {
return TRUE;
}
}
}
}
return FALSE;
}
/**
Configure the UDP.
@param Instance The DNS session
@param UdpIo The UDP_IO instance
@retval EFI_SUCCESS The UDP is successfully configured for the
session.
**/
EFI_STATUS
Dns4ConfigUdp (
IN DNS_INSTANCE *Instance,
IN UDP_IO *UdpIo
)
{
EFI_DNS4_CONFIG_DATA *Config;
EFI_UDP4_CONFIG_DATA UdpConfig;
EFI_STATUS Status;
Config = &Instance->Dns4CfgData;
UdpConfig.AcceptBroadcast = FALSE;
UdpConfig.AcceptPromiscuous = FALSE;
UdpConfig.AcceptAnyPort = FALSE;
UdpConfig.AllowDuplicatePort = FALSE;
UdpConfig.TypeOfService = 0;
UdpConfig.TimeToLive = 128;
UdpConfig.DoNotFragment = FALSE;
UdpConfig.ReceiveTimeout = 0;
UdpConfig.TransmitTimeout = 0;
UdpConfig.UseDefaultAddress = Config->UseDefaultSetting;
UdpConfig.SubnetMask = Config->SubnetMask;
UdpConfig.StationPort = Config->LocalPort;
UdpConfig.RemotePort = DNS_SERVER_PORT;
CopyMem (&UdpConfig.StationAddress, &Config->StationIp, sizeof (EFI_IPv4_ADDRESS));
CopyMem (&UdpConfig.RemoteAddress, &Instance->SessionDnsServer.v4, sizeof (EFI_IPv4_ADDRESS));
Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfig);
if ((Status == EFI_NO_MAPPING) && Dns4GetMapping (Instance, UdpIo, &UdpConfig)) {
return EFI_SUCCESS;
}
return Status;
}
/**
Configure the UDP.
@param Instance The DNS session
@param UdpIo The UDP_IO instance
@retval EFI_SUCCESS The UDP is successfully configured for the
session.
**/
EFI_STATUS
Dns6ConfigUdp (
IN DNS_INSTANCE *Instance,
IN UDP_IO *UdpIo
)
{
EFI_DNS6_CONFIG_DATA *Config;
EFI_UDP6_CONFIG_DATA UdpConfig;
EFI_STATUS Status;
Config = &Instance->Dns6CfgData;
UdpConfig.AcceptPromiscuous = FALSE;
UdpConfig.AcceptAnyPort = FALSE;
UdpConfig.AllowDuplicatePort = FALSE;
UdpConfig.TrafficClass = 0;
UdpConfig.HopLimit = 128;
UdpConfig.ReceiveTimeout = 0;
UdpConfig.TransmitTimeout = 0;
UdpConfig.StationPort = Config->LocalPort;
UdpConfig.RemotePort = DNS_SERVER_PORT;
CopyMem (&UdpConfig.StationAddress, &Config->StationIp, sizeof (EFI_IPv6_ADDRESS));
CopyMem (&UdpConfig.RemoteAddress, &Instance->SessionDnsServer.v6, sizeof (EFI_IPv6_ADDRESS));
Status = UdpIo->Protocol.Udp6->Configure (UdpIo->Protocol.Udp6, &UdpConfig);
if ((Status == EFI_NO_MAPPING) && Dns6GetMapping (Instance, UdpIo, &UdpConfig)) {
return EFI_SUCCESS;
}
return Status;
}
/**
Update Dns4 cache to shared list of caches of all DNSv4 instances.
@param Dns4CacheList All Dns4 cache list.
@param DeleteFlag If FALSE, this function is to add one entry to the DNS Cache.
If TRUE, this function will delete matching DNS Cache entry.
@param Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter.
If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists.
@param DnsCacheEntry Entry Pointer to DNS Cache entry.
@retval EFI_SUCCESS Update Dns4 cache successfully.
@retval Others Failed to update Dns4 cache.
**/
EFI_STATUS
EFIAPI
UpdateDns4Cache (
IN LIST_ENTRY *Dns4CacheList,
IN BOOLEAN DeleteFlag,
IN BOOLEAN Override,
IN EFI_DNS4_CACHE_ENTRY DnsCacheEntry
)
{
DNS4_CACHE *NewDnsCache;
DNS4_CACHE *Item;
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
NewDnsCache = NULL;
Item = NULL;
//
// Search the database for the matching EFI_DNS_CACHE_ENTRY
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, Dns4CacheList) {
Item = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink);
if (StrCmp (DnsCacheEntry.HostName, Item->DnsCache.HostName) == 0 && \
CompareMem (DnsCacheEntry.IpAddress, Item->DnsCache.IpAddress, sizeof (EFI_IPv4_ADDRESS)) == 0) {
//
// This is the Dns cache entry
//
if (DeleteFlag) {
//
// Delete matching DNS Cache entry
//
RemoveEntryList (&Item->AllCacheLink);
FreePool (Item->DnsCache.HostName);
FreePool (Item->DnsCache.IpAddress);
FreePool (Item);
return EFI_SUCCESS;
} else if (Override) {
//
// Update this one
//
Item->DnsCache.Timeout = DnsCacheEntry.Timeout;
return EFI_SUCCESS;
}else {
return EFI_ACCESS_DENIED;
}
}
}
//
// Add new one
//
NewDnsCache = AllocatePool (sizeof (DNS4_CACHE));
if (NewDnsCache == NULL) {
return EFI_OUT_OF_RESOURCES;
}
InitializeListHead (&NewDnsCache->AllCacheLink);
NewDnsCache->DnsCache.HostName = AllocatePool (StrSize (DnsCacheEntry.HostName));
if (NewDnsCache->DnsCache.HostName == NULL) {
FreePool (NewDnsCache);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (NewDnsCache->DnsCache.HostName, DnsCacheEntry.HostName, StrSize (DnsCacheEntry.HostName));
NewDnsCache->DnsCache.IpAddress = AllocatePool (sizeof (EFI_IPv4_ADDRESS));
if (NewDnsCache->DnsCache.IpAddress == NULL) {
FreePool (NewDnsCache->DnsCache.HostName);
FreePool (NewDnsCache);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (NewDnsCache->DnsCache.IpAddress, DnsCacheEntry.IpAddress, sizeof (EFI_IPv4_ADDRESS));
NewDnsCache->DnsCache.Timeout = DnsCacheEntry.Timeout;
InsertTailList (Dns4CacheList, &NewDnsCache->AllCacheLink);
return EFI_SUCCESS;
}
/**
Update Dns6 cache to shared list of caches of all DNSv6 instances.
@param Dns6CacheList All Dns6 cache list.
@param DeleteFlag If FALSE, this function is to add one entry to the DNS Cache.
If TRUE, this function will delete matching DNS Cache entry.
@param Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter.
If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists.
@param DnsCacheEntry Entry Pointer to DNS Cache entry.
@retval EFI_SUCCESS Update Dns6 cache successfully.
@retval Others Failed to update Dns6 cache.
**/
EFI_STATUS
EFIAPI
UpdateDns6Cache (
IN LIST_ENTRY *Dns6CacheList,
IN BOOLEAN DeleteFlag,
IN BOOLEAN Override,
IN EFI_DNS6_CACHE_ENTRY DnsCacheEntry
)
{
DNS6_CACHE *NewDnsCache;
DNS6_CACHE *Item;
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
NewDnsCache = NULL;
Item = NULL;
//
// Search the database for the matching EFI_DNS_CACHE_ENTRY
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, Dns6CacheList) {
Item = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink);
if (StrCmp (DnsCacheEntry.HostName, Item->DnsCache.HostName) == 0 && \
CompareMem (DnsCacheEntry.IpAddress, Item->DnsCache.IpAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) {
//
// This is the Dns cache entry
//
if (DeleteFlag) {
//
// Delete matching DNS Cache entry
//
RemoveEntryList (&Item->AllCacheLink);
FreePool (Item->DnsCache.HostName);
FreePool (Item->DnsCache.IpAddress);
FreePool (Item);
return EFI_SUCCESS;
} else if (Override) {
//
// Update this one
//
Item->DnsCache.Timeout = DnsCacheEntry.Timeout;
return EFI_SUCCESS;
}else {
return EFI_ACCESS_DENIED;
}
}
}
//
// Add new one
//
NewDnsCache = AllocatePool (sizeof (DNS6_CACHE));
if (NewDnsCache == NULL) {
return EFI_OUT_OF_RESOURCES;
}
InitializeListHead (&NewDnsCache->AllCacheLink);
NewDnsCache->DnsCache.HostName = AllocatePool (StrSize (DnsCacheEntry.HostName));
if (NewDnsCache->DnsCache.HostName == NULL) {
FreePool (NewDnsCache);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (NewDnsCache->DnsCache.HostName, DnsCacheEntry.HostName, StrSize (DnsCacheEntry.HostName));
NewDnsCache->DnsCache.IpAddress = AllocatePool (sizeof (EFI_IPv6_ADDRESS));
if (NewDnsCache->DnsCache.IpAddress == NULL) {
FreePool (NewDnsCache->DnsCache.HostName);
FreePool (NewDnsCache);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (NewDnsCache->DnsCache.IpAddress, DnsCacheEntry.IpAddress, sizeof (EFI_IPv6_ADDRESS));
NewDnsCache->DnsCache.Timeout = DnsCacheEntry.Timeout;
InsertTailList (Dns6CacheList, &NewDnsCache->AllCacheLink);
return EFI_SUCCESS;
}
/**
Add Dns4 ServerIp to common list of addresses of all configured DNSv4 server.
@param Dns4ServerList Common list of addresses of all configured DNSv4 server.
@param ServerIp DNS server Ip.
@retval EFI_SUCCESS Add Dns4 ServerIp to common list successfully.
@retval Others Failed to add Dns4 ServerIp to common list.
**/
EFI_STATUS
EFIAPI
AddDns4ServerIp (
IN LIST_ENTRY *Dns4ServerList,
IN EFI_IPv4_ADDRESS ServerIp
)
{
DNS4_SERVER_IP *NewServerIp;
DNS4_SERVER_IP *Item;
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
NewServerIp = NULL;
Item = NULL;
//
// Search the database for the matching ServerIp
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, Dns4ServerList) {
Item = NET_LIST_USER_STRUCT (Entry, DNS4_SERVER_IP, AllServerLink);
if (CompareMem (&Item->Dns4ServerIp, &ServerIp, sizeof (EFI_IPv4_ADDRESS)) == 0) {
//
// Already done.
//
return EFI_SUCCESS;
}
}
//
// Add new one
//
NewServerIp = AllocatePool (sizeof (DNS4_SERVER_IP));
if (NewServerIp == NULL) {
return EFI_OUT_OF_RESOURCES;
}
InitializeListHead (&NewServerIp->AllServerLink);
CopyMem (&NewServerIp->Dns4ServerIp, &ServerIp, sizeof (EFI_IPv4_ADDRESS));
InsertTailList (Dns4ServerList, &NewServerIp->AllServerLink);
return EFI_SUCCESS;
}
/**
Add Dns6 ServerIp to common list of addresses of all configured DNSv6 server.
@param Dns6ServerList Common list of addresses of all configured DNSv6 server.
@param ServerIp DNS server Ip.
@retval EFI_SUCCESS Add Dns6 ServerIp to common list successfully.
@retval Others Failed to add Dns6 ServerIp to common list.
**/
EFI_STATUS
EFIAPI
AddDns6ServerIp (
IN LIST_ENTRY *Dns6ServerList,
IN EFI_IPv6_ADDRESS ServerIp
)
{
DNS6_SERVER_IP *NewServerIp;
DNS6_SERVER_IP *Item;
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
NewServerIp = NULL;
Item = NULL;
//
// Search the database for the matching ServerIp
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, Dns6ServerList) {
Item = NET_LIST_USER_STRUCT (Entry, DNS6_SERVER_IP, AllServerLink);
if (CompareMem (&Item->Dns6ServerIp, &ServerIp, sizeof (EFI_IPv6_ADDRESS)) == 0) {
//
// Already done.
//
return EFI_SUCCESS;
}
}
//
// Add new one
//
NewServerIp = AllocatePool (sizeof (DNS6_SERVER_IP));
if (NewServerIp == NULL) {
return EFI_OUT_OF_RESOURCES;
}
InitializeListHead (&NewServerIp->AllServerLink);
CopyMem (&NewServerIp->Dns6ServerIp, &ServerIp, sizeof (EFI_IPv6_ADDRESS));
InsertTailList (Dns6ServerList, &NewServerIp->AllServerLink);
return EFI_SUCCESS;
}
/**
Find out whether the response is valid or invalid.
@param TokensMap All DNS transmittal Tokens entry.
@param Identification Identification for queried packet.
@param Type Type for queried packet.
@param Class Class for queried packet.
@param Item Return corresponding Token entry.
@retval TRUE The response is valid.
@retval FALSE The response is invalid.
**/
BOOLEAN
IsValidDnsResponse (
IN NET_MAP *TokensMap,
IN UINT16 Identification,
IN UINT16 Type,
IN UINT16 Class,
OUT NET_MAP_ITEM **Item
)
{
LIST_ENTRY *Entry;
NET_BUF *Packet;
UINT8 *TxString;
DNS_HEADER *DnsHeader;
CHAR8 *QueryName;
DNS_QUERY_SECTION *QuerySection;
NET_LIST_FOR_EACH (Entry, &TokensMap->Used) {
*Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
Packet = (NET_BUF *) ((*Item)->Value);
if (Packet == NULL){
continue;
} else {
TxString = NetbufGetByte (Packet, 0, NULL);
ASSERT (TxString != NULL);
DnsHeader = (DNS_HEADER *) TxString;
QueryName = (CHAR8 *) (TxString + sizeof (*DnsHeader));
QuerySection = (DNS_QUERY_SECTION *) (QueryName + AsciiStrLen (QueryName) + 1);
if (NTOHS (DnsHeader->Identification) == Identification &&
NTOHS (QuerySection->Type) == Type &&
NTOHS (QuerySection->Class) == Class) {
return TRUE;
}
}
}
*Item = NULL;
return FALSE;
}
/**
Parse Dns Response.
@param Instance The DNS instance
@param RxString Received buffer.
@param Completed Flag to indicate that Dns response is valid.
@retval EFI_SUCCESS Parse Dns Response successfully.
@retval Others Failed to parse Dns Response.
**/
EFI_STATUS
ParseDnsResponse (
IN OUT DNS_INSTANCE *Instance,
IN UINT8 *RxString,
OUT BOOLEAN *Completed
)
{
DNS_HEADER *DnsHeader;
CHAR8 *QueryName;
DNS_QUERY_SECTION *QuerySection;
CHAR8 *AnswerName;
DNS_ANSWER_SECTION *AnswerSection;
UINT8 *AnswerData;
NET_MAP_ITEM *Item;
DNS4_TOKEN_ENTRY *Dns4TokenEntry;
DNS6_TOKEN_ENTRY *Dns6TokenEntry;
UINT32 IpCount;
UINT32 RRCount;
UINT32 AnswerSectionNum;
UINT32 CNameTtl;
EFI_IPv4_ADDRESS *HostAddr4;
EFI_IPv6_ADDRESS *HostAddr6;
EFI_DNS4_CACHE_ENTRY *Dns4CacheEntry;
EFI_DNS6_CACHE_ENTRY *Dns6CacheEntry;
DNS_RESOURCE_RECORD *Dns4RR;
DNS6_RESOURCE_RECORD *Dns6RR;
EFI_STATUS Status;
EFI_TPL OldTpl;
Item = NULL;
Dns4TokenEntry = NULL;
Dns6TokenEntry = NULL;
IpCount = 0;
RRCount = 0;
AnswerSectionNum = 0;
CNameTtl = 0;
HostAddr4 = NULL;
HostAddr6 = NULL;
Dns4CacheEntry = NULL;
Dns6CacheEntry = NULL;
Dns4RR = NULL;
Dns6RR = NULL;
*Completed = TRUE;
Status = EFI_SUCCESS;
//
// Get header
//
DnsHeader = (DNS_HEADER *) RxString;
DnsHeader->Identification = NTOHS (DnsHeader->Identification);
DnsHeader->Flags.Uint16 = NTOHS (DnsHeader->Flags.Uint16);
DnsHeader->QuestionsNum = NTOHS (DnsHeader->QuestionsNum);
DnsHeader->AnswersNum = NTOHS (DnsHeader->AnswersNum);
DnsHeader->AuthorityNum = NTOHS (DnsHeader->AuthorityNum);
DnsHeader->AditionalNum = NTOHS (DnsHeader->AditionalNum);
//
// Get Query name
//
QueryName = (CHAR8 *) (RxString + sizeof (*DnsHeader));
//
// Get query section
//
QuerySection = (DNS_QUERY_SECTION *) (QueryName + AsciiStrLen (QueryName) + 1);
QuerySection->Type = NTOHS (QuerySection->Type);
QuerySection->Class = NTOHS (QuerySection->Class);
//
// Get Answer name
//
AnswerName = (CHAR8 *) QuerySection + sizeof (*QuerySection);
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
//
// Check DnsResponse Validity, if so, also get a valid NET_MAP_ITEM.
//
if (Instance->Service->IpVersion == IP_VERSION_4) {
if (!IsValidDnsResponse (
&Instance->Dns4TxTokens,
DnsHeader->Identification,
QuerySection->Type,
QuerySection->Class,
&Item
)) {
*Completed = FALSE;
Status = EFI_ABORTED;
goto ON_EXIT;
}
ASSERT (Item != NULL);
Dns4TokenEntry = (DNS4_TOKEN_ENTRY *) (Item->Key);
} else {
if (!IsValidDnsResponse (
&Instance->Dns6TxTokens,
DnsHeader->Identification,
QuerySection->Type,
QuerySection->Class,
&Item
)) {
*Completed = FALSE;
Status = EFI_ABORTED;
goto ON_EXIT;
}
ASSERT (Item != NULL);
Dns6TokenEntry = (DNS6_TOKEN_ENTRY *) (Item->Key);
}
//
// Continue Check Some Errors.
//
if (DnsHeader->Flags.Bits.RCode != DNS_FLAGS_RCODE_NO_ERROR || DnsHeader->AnswersNum < 1 || \
DnsHeader->Flags.Bits.QR != DNS_FLAGS_QR_RESPONSE) {
//
// The domain name referenced in the query does not exist.
//
if (DnsHeader->Flags.Bits.RCode == DNS_FLAGS_RCODE_NAME_ERROR) {
Status = EFI_NOT_FOUND;
} else {
Status = EFI_DEVICE_ERROR;
}
goto ON_COMPLETE;
}
//
// Do some buffer allocations.
//
if (Instance->Service->IpVersion == IP_VERSION_4) {
ASSERT (Dns4TokenEntry != NULL);
if (Dns4TokenEntry->GeneralLookUp) {
//
// It's the GeneralLookUp querying.
//
Dns4TokenEntry->Token->RspData.GLookupData = AllocateZeroPool (sizeof (DNS_RESOURCE_RECORD));
if (Dns4TokenEntry->Token->RspData.GLookupData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
Dns4TokenEntry->Token->RspData.GLookupData->RRList = AllocateZeroPool (DnsHeader->AnswersNum * sizeof (DNS_RESOURCE_RECORD));
if (Dns4TokenEntry->Token->RspData.GLookupData->RRList == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
} else {
//
// It's not the GeneralLookUp querying. Check the Query type.
//
if (QuerySection->Type == DNS_TYPE_A) {
Dns4TokenEntry->Token->RspData.H2AData = AllocateZeroPool (sizeof (DNS_HOST_TO_ADDR_DATA));
if (Dns4TokenEntry->Token->RspData.H2AData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
Dns4TokenEntry->Token->RspData.H2AData->IpList = AllocateZeroPool (DnsHeader->AnswersNum * sizeof (EFI_IPv4_ADDRESS));
if (Dns4TokenEntry->Token->RspData.H2AData->IpList == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
} else {
Status = EFI_UNSUPPORTED;
goto ON_EXIT;
}
}
} else {
ASSERT (Dns6TokenEntry != NULL);
if (Dns6TokenEntry->GeneralLookUp) {
//
// It's the GeneralLookUp querying.
//
Dns6TokenEntry->Token->RspData.GLookupData = AllocateZeroPool (sizeof (DNS_RESOURCE_RECORD));
if (Dns6TokenEntry->Token->RspData.GLookupData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
Dns6TokenEntry->Token->RspData.GLookupData->RRList = AllocateZeroPool (DnsHeader->AnswersNum * sizeof (DNS_RESOURCE_RECORD));
if (Dns6TokenEntry->Token->RspData.GLookupData->RRList == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
} else {
//
// It's not the GeneralLookUp querying. Check the Query type.
//
if (QuerySection->Type == DNS_TYPE_AAAA) {
Dns6TokenEntry->Token->RspData.H2AData = AllocateZeroPool (sizeof (DNS6_HOST_TO_ADDR_DATA));
if (Dns6TokenEntry->Token->RspData.H2AData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
Dns6TokenEntry->Token->RspData.H2AData->IpList = AllocateZeroPool (DnsHeader->AnswersNum * sizeof (EFI_IPv6_ADDRESS));
if (Dns6TokenEntry->Token->RspData.H2AData->IpList == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
} else {
Status = EFI_UNSUPPORTED;
goto ON_EXIT;
}
}
}
Status = EFI_NOT_FOUND;
//
// Processing AnswerSection.
//
while (AnswerSectionNum < DnsHeader->AnswersNum) {
//
// Answer name should be PTR, else EFI_UNSUPPORTED returned.
//
if ((*(UINT8 *) AnswerName & 0xC0) != 0xC0) {
Status = EFI_UNSUPPORTED;
goto ON_EXIT;
}
//
// Get Answer section.
//
AnswerSection = (DNS_ANSWER_SECTION *) (AnswerName + sizeof (UINT16));
AnswerSection->Type = NTOHS (AnswerSection->Type);
AnswerSection->Class = NTOHS (AnswerSection->Class);
AnswerSection->Ttl = NTOHL (AnswerSection->Ttl);
AnswerSection->DataLength = NTOHS (AnswerSection->DataLength);
//
// Check whether it's the GeneralLookUp querying.
//
if (Instance->Service->IpVersion == IP_VERSION_4 && Dns4TokenEntry->GeneralLookUp) {
Dns4RR = Dns4TokenEntry->Token->RspData.GLookupData->RRList;
AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection);
//
// Fill the ResourceRecord.
//
Dns4RR[RRCount].QName = AllocateZeroPool (AsciiStrLen (QueryName) + 1);
if (Dns4RR[RRCount].QName == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
CopyMem (Dns4RR[RRCount].QName, QueryName, AsciiStrLen (QueryName));
Dns4RR[RRCount].QType = AnswerSection->Type;
Dns4RR[RRCount].QClass = AnswerSection->Class;
Dns4RR[RRCount].TTL = AnswerSection->Ttl;
Dns4RR[RRCount].DataLength = AnswerSection->DataLength;
Dns4RR[RRCount].RData = AllocateZeroPool (Dns4RR[RRCount].DataLength);
if (Dns4RR[RRCount].RData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
CopyMem (Dns4RR[RRCount].RData, AnswerData, Dns4RR[RRCount].DataLength);
RRCount ++;
Status = EFI_SUCCESS;
} else if (Instance->Service->IpVersion == IP_VERSION_6 && Dns6TokenEntry->GeneralLookUp) {
Dns6RR = Dns6TokenEntry->Token->RspData.GLookupData->RRList;
AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection);
//
// Fill the ResourceRecord.
//
Dns6RR[RRCount].QName = AllocateZeroPool (AsciiStrLen (QueryName) + 1);
if (Dns6RR[RRCount].QName == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
CopyMem (Dns6RR[RRCount].QName, QueryName, AsciiStrLen (QueryName));
Dns6RR[RRCount].QType = AnswerSection->Type;
Dns6RR[RRCount].QClass = AnswerSection->Class;
Dns6RR[RRCount].TTL = AnswerSection->Ttl;
Dns6RR[RRCount].DataLength = AnswerSection->DataLength;
Dns6RR[RRCount].RData = AllocateZeroPool (Dns6RR[RRCount].DataLength);
if (Dns6RR[RRCount].RData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
CopyMem (Dns6RR[RRCount].RData, AnswerData, Dns6RR[RRCount].DataLength);
RRCount ++;
Status = EFI_SUCCESS;
} else {
//
// It's not the GeneralLookUp querying.
// Check the Query type, parse the response packet.
//
switch (AnswerSection->Type) {
case DNS_TYPE_A:
//
// This is address entry, get Data.
//
ASSERT (Dns4TokenEntry != NULL);
if (AnswerSection->DataLength != 4) {
Status = EFI_ABORTED;
goto ON_EXIT;
}
HostAddr4 = Dns4TokenEntry->Token->RspData.H2AData->IpList;
AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection);
CopyMem (&HostAddr4[IpCount], AnswerData, sizeof (EFI_IPv4_ADDRESS));
//
// Allocate new CacheEntry pool to update DNS cache dynamically.
//
Dns4CacheEntry = AllocateZeroPool (sizeof (EFI_DNS4_CACHE_ENTRY));
if (Dns4CacheEntry == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
Dns4CacheEntry->HostName = AllocateZeroPool (2 * (StrLen(Dns4TokenEntry->QueryHostName) + 1));
if (Dns4CacheEntry->HostName == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
CopyMem (Dns4CacheEntry->HostName, Dns4TokenEntry->QueryHostName, 2 * (StrLen(Dns4TokenEntry->QueryHostName) + 1));
Dns4CacheEntry->IpAddress = AllocateZeroPool (sizeof (EFI_IPv4_ADDRESS));
if (Dns4CacheEntry->IpAddress == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
CopyMem (Dns4CacheEntry->IpAddress, AnswerData, sizeof (EFI_IPv4_ADDRESS));
if (CNameTtl != 0 && AnswerSection->Ttl != 0) {
Dns4CacheEntry->Timeout = MIN (CNameTtl, AnswerSection->Ttl);
} else {
Dns4CacheEntry->Timeout = MAX (CNameTtl, AnswerSection->Ttl);
}
UpdateDns4Cache (&mDriverData->Dns4CacheList, FALSE, TRUE, *Dns4CacheEntry);
//
// Free allocated CacheEntry pool.
//
FreePool (Dns4CacheEntry->HostName);
Dns4CacheEntry->HostName = NULL;
FreePool (Dns4CacheEntry->IpAddress);
Dns4CacheEntry->IpAddress = NULL;
FreePool (Dns4CacheEntry);
Dns4CacheEntry = NULL;
IpCount ++;
Status = EFI_SUCCESS;
break;
case DNS_TYPE_AAAA:
//
// This is address entry, get Data.
//
ASSERT (Dns6TokenEntry != NULL);
if (AnswerSection->DataLength != 16) {
Status = EFI_ABORTED;
goto ON_EXIT;
}
HostAddr6 = Dns6TokenEntry->Token->RspData.H2AData->IpList;
AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection);
CopyMem (&HostAddr6[IpCount], AnswerData, sizeof (EFI_IPv6_ADDRESS));
//
// Allocate new CacheEntry pool to update DNS cache dynamically.
//
Dns6CacheEntry = AllocateZeroPool (sizeof (EFI_DNS6_CACHE_ENTRY));
if (Dns6CacheEntry == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
Dns6CacheEntry->HostName = AllocateZeroPool (2 * (StrLen(Dns6TokenEntry->QueryHostName) + 1));
if (Dns6CacheEntry->HostName == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
CopyMem (Dns6CacheEntry->HostName, Dns6TokenEntry->QueryHostName, 2 * (StrLen(Dns6TokenEntry->QueryHostName) + 1));
Dns6CacheEntry->IpAddress = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS));
if (Dns6CacheEntry->IpAddress == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
CopyMem (Dns6CacheEntry->IpAddress, AnswerData, sizeof (EFI_IPv6_ADDRESS));
if (CNameTtl != 0 && AnswerSection->Ttl != 0) {
Dns6CacheEntry->Timeout = MIN (CNameTtl, AnswerSection->Ttl);
} else {
Dns6CacheEntry->Timeout = MAX (CNameTtl, AnswerSection->Ttl);
}
UpdateDns6Cache (&mDriverData->Dns6CacheList, FALSE, TRUE, *Dns6CacheEntry);
//
// Free allocated CacheEntry pool.
//
FreePool (Dns6CacheEntry->HostName);
Dns6CacheEntry->HostName = NULL;
FreePool (Dns6CacheEntry->IpAddress);
Dns6CacheEntry->IpAddress = NULL;
FreePool (Dns6CacheEntry);
Dns6CacheEntry = NULL;
IpCount ++;
Status = EFI_SUCCESS;
break;
case DNS_TYPE_CNAME:
//
// According RFC 1034 - 3.6.2, if the query name is an alias, the name server will include the CNAME
// record in the response and restart the query at the domain name specified in the data field of the
// CNAME record. So, just record the TTL value of the CNAME, then skip to parse the next record.
//
CNameTtl = AnswerSection->Ttl;
break;
default:
Status = EFI_UNSUPPORTED;
goto ON_EXIT;
}
}
//
// Find next one
//
AnswerName = (CHAR8 *) AnswerSection + sizeof (*AnswerSection) + AnswerSection->DataLength;
AnswerSectionNum ++;
}
if (Instance->Service->IpVersion == IP_VERSION_4) {
ASSERT (Dns4TokenEntry != NULL);
if (Dns4TokenEntry->GeneralLookUp) {
Dns4TokenEntry->Token->RspData.GLookupData->RRCount = RRCount;
} else {
if (QuerySection->Type == DNS_TYPE_A) {
Dns4TokenEntry->Token->RspData.H2AData->IpCount = IpCount;
} else {
Status = EFI_UNSUPPORTED;
goto ON_EXIT;
}
}
} else {
ASSERT (Dns6TokenEntry != NULL);
if (Dns6TokenEntry->GeneralLookUp) {
Dns6TokenEntry->Token->RspData.GLookupData->RRCount = RRCount;
} else {
if (QuerySection->Type == DNS_TYPE_AAAA) {
Dns6TokenEntry->Token->RspData.H2AData->IpCount = IpCount;
} else {
Status = EFI_UNSUPPORTED;
goto ON_EXIT;
}
}
}
ON_COMPLETE:
//
// Parsing is complete, free the sending packet and signal Event here.
//
if (Item != NULL && Item->Value != NULL) {
NetbufFree ((NET_BUF *) (Item->Value));
}
if (Instance->Service->IpVersion == IP_VERSION_4) {
ASSERT (Dns4TokenEntry != NULL);
Dns4RemoveTokenEntry (&Instance->Dns4TxTokens, Dns4TokenEntry);
Dns4TokenEntry->Token->Status = Status;
if (Dns4TokenEntry->Token->Event != NULL) {
gBS->SignalEvent (Dns4TokenEntry->Token->Event);
DispatchDpc ();
}
} else {
ASSERT (Dns6TokenEntry != NULL);
Dns6RemoveTokenEntry (&Instance->Dns6TxTokens, Dns6TokenEntry);
Dns6TokenEntry->Token->Status = Status;
if (Dns6TokenEntry->Token->Event != NULL) {
gBS->SignalEvent (Dns6TokenEntry->Token->Event);
DispatchDpc ();
}
}
ON_EXIT:
//
// Free the allocated buffer if error happen.
//
if (EFI_ERROR (Status)) {
if (Dns4TokenEntry != NULL) {
if (Dns4TokenEntry->GeneralLookUp) {
if (Dns4TokenEntry->Token->RspData.GLookupData != NULL) {
if (Dns4TokenEntry->Token->RspData.GLookupData->RRList != NULL) {
while (RRCount != 0) {
RRCount --;
if (Dns4TokenEntry->Token->RspData.GLookupData->RRList[RRCount].QName != NULL) {
FreePool (Dns4TokenEntry->Token->RspData.GLookupData->RRList[RRCount].QName);
}
if (Dns4TokenEntry->Token->RspData.GLookupData->RRList[RRCount].RData != NULL) {
FreePool (Dns4TokenEntry->Token->RspData.GLookupData->RRList[RRCount].RData);
}
}
FreePool (Dns4TokenEntry->Token->RspData.GLookupData->RRList);
}
FreePool (Dns4TokenEntry->Token->RspData.GLookupData);
}
} else {
if (QuerySection->Type == DNS_TYPE_A && Dns4TokenEntry->Token->RspData.H2AData != NULL) {
if (Dns4TokenEntry->Token->RspData.H2AData->IpList != NULL) {
FreePool (Dns4TokenEntry->Token->RspData.H2AData->IpList);
}
FreePool (Dns4TokenEntry->Token->RspData.H2AData);
}
}
}
if (Dns6TokenEntry != NULL) {
if (Dns6TokenEntry->GeneralLookUp) {
if (Dns6TokenEntry->Token->RspData.GLookupData != NULL) {
if (Dns6TokenEntry->Token->RspData.GLookupData->RRList != NULL) {
while (RRCount != 0) {
RRCount --;
if (Dns6TokenEntry->Token->RspData.GLookupData->RRList[RRCount].QName != NULL) {
FreePool (Dns6TokenEntry->Token->RspData.GLookupData->RRList[RRCount].QName);
}
if (Dns6TokenEntry->Token->RspData.GLookupData->RRList[RRCount].RData != NULL) {
FreePool (Dns6TokenEntry->Token->RspData.GLookupData->RRList[RRCount].RData);
}
}
FreePool (Dns6TokenEntry->Token->RspData.GLookupData->RRList);
}
FreePool (Dns6TokenEntry->Token->RspData.GLookupData);
}
} else {
if (QuerySection->Type == DNS_TYPE_AAAA && Dns6TokenEntry->Token->RspData.H2AData != NULL) {
if (Dns6TokenEntry->Token->RspData.H2AData->IpList != NULL) {
FreePool (Dns6TokenEntry->Token->RspData.H2AData->IpList);
}
FreePool (Dns6TokenEntry->Token->RspData.H2AData);
}
}
}
if (Dns4CacheEntry != NULL) {
if (Dns4CacheEntry->HostName != NULL) {
FreePool (Dns4CacheEntry->HostName);
}
if (Dns4CacheEntry->IpAddress != NULL) {
FreePool (Dns4CacheEntry->IpAddress);
}
FreePool (Dns4CacheEntry);
}
if (Dns6CacheEntry != NULL) {
if (Dns6CacheEntry->HostName != NULL) {
FreePool (Dns6CacheEntry->HostName);
}
if (Dns6CacheEntry->IpAddress != NULL) {
FreePool (Dns6CacheEntry->IpAddress);
}
FreePool (Dns6CacheEntry);
}
}
gBS->RestoreTPL (OldTpl);
return Status;
}
/**
Parse response packet.
@param Packet The packets received.
@param EndPoint The local/remote UDP access point
@param IoStatus The status of the UDP receive
@param Context The opaque parameter to the function.
**/
VOID
EFIAPI
DnsOnPacketReceived (
NET_BUF *Packet,
UDP_END_POINT *EndPoint,
EFI_STATUS IoStatus,
VOID *Context
)
{
DNS_INSTANCE *Instance;
UINT8 *RcvString;
BOOLEAN Completed;
Instance = (DNS_INSTANCE *) Context;
NET_CHECK_SIGNATURE (Instance, DNS_INSTANCE_SIGNATURE);
RcvString = NULL;
Completed = FALSE;
if (EFI_ERROR (IoStatus)) {
goto ON_EXIT;
}
ASSERT (Packet != NULL);
if (Packet->TotalSize <= sizeof (DNS_HEADER)) {
goto ON_EXIT;
}
RcvString = NetbufGetByte (Packet, 0, NULL);
ASSERT (RcvString != NULL);
//
// Parse Dns Response
//
ParseDnsResponse (Instance, RcvString, &Completed);
ON_EXIT:
if (Packet != NULL) {
NetbufFree (Packet);
}
if (!Completed) {
UdpIoRecvDatagram (Instance->UdpIo, DnsOnPacketReceived, Instance, 0);
}
}
/**
Release the net buffer when packet is sent.
@param Packet The packets received.
@param EndPoint The local/remote UDP access point
@param IoStatus The status of the UDP receive
@param Context The opaque parameter to the function.
**/
VOID
EFIAPI
DnsOnPacketSent (
NET_BUF *Packet,
UDP_END_POINT *EndPoint,
EFI_STATUS IoStatus,
VOID *Context
)
{
DNS_INSTANCE *Instance;
LIST_ENTRY *Entry;
NET_MAP_ITEM *Item;
DNS4_TOKEN_ENTRY *Dns4TokenEntry;
DNS6_TOKEN_ENTRY *Dns6TokenEntry;
Dns4TokenEntry = NULL;
Dns6TokenEntry = NULL;
Instance = (DNS_INSTANCE *) Context;
NET_CHECK_SIGNATURE (Instance, DNS_INSTANCE_SIGNATURE);
if (Instance->Service->IpVersion == IP_VERSION_4) {
NET_LIST_FOR_EACH (Entry, &Instance->Dns4TxTokens.Used) {
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
if (Packet == (NET_BUF *)(Item->Value)) {
Dns4TokenEntry = ((DNS4_TOKEN_ENTRY *)Item->Key);
Dns4TokenEntry->PacketToLive = Dns4TokenEntry->Token->RetryInterval;
break;
}
}
} else {
NET_LIST_FOR_EACH (Entry, &Instance->Dns6TxTokens.Used) {
Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);
if (Packet == (NET_BUF *)(Item->Value)) {
Dns6TokenEntry = ((DNS6_TOKEN_ENTRY *)Item->Key);
Dns6TokenEntry->PacketToLive = Dns6TokenEntry->Token->RetryInterval;
break;
}
}
}
NetbufFree (Packet);
}
/**
Query request information.
@param Instance The DNS instance
@param Packet The packet for querying request information.
@retval EFI_SUCCESS Query request information successfully.
@retval Others Failed to query request information.
**/
EFI_STATUS
DoDnsQuery (
IN DNS_INSTANCE *Instance,
IN NET_BUF *Packet
)
{
EFI_STATUS Status;
//
// Ready to receive the DNS response.
//
if (Instance->UdpIo->RecvRequest == NULL) {
Status = UdpIoRecvDatagram (Instance->UdpIo, DnsOnPacketReceived, Instance, 0);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Transmit the DNS packet.
//
NET_GET_REF (Packet);
Status = UdpIoSendDatagram (Instance->UdpIo, Packet, NULL, NULL, DnsOnPacketSent, Instance);
return Status;
}
/**
Construct the Packet according query section.
@param Instance The DNS instance
@param QueryName Queried Name
@param Type Queried Type
@param Class Queried Class
@param Packet The packet for query
@retval EFI_SUCCESS The packet is constructed.
@retval Others Failed to construct the Packet.
**/
EFI_STATUS
ConstructDNSQuery (
IN DNS_INSTANCE *Instance,
IN CHAR8 *QueryName,
IN UINT16 Type,
IN UINT16 Class,
OUT NET_BUF **Packet
)
{
NET_FRAGMENT Frag;
DNS_HEADER *DnsHeader;
DNS_QUERY_SECTION *DnsQuery;
//
// Messages carried by UDP are restricted to 512 bytes (not counting the IP
// or UDP headers).
//
Frag.Bulk = AllocatePool (DNS_MAX_MESSAGE_SIZE * sizeof (UINT8));
if (Frag.Bulk == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Fill header
//
DnsHeader = (DNS_HEADER *) Frag.Bulk;
DnsHeader->Identification = (UINT16)NET_RANDOM (NetRandomInitSeed());
DnsHeader->Flags.Uint16 = 0x0000;
DnsHeader->Flags.Bits.RD = 1;
DnsHeader->Flags.Bits.OpCode = DNS_FLAGS_OPCODE_STANDARD;
DnsHeader->Flags.Bits.QR = DNS_FLAGS_QR_QUERY;
DnsHeader->QuestionsNum = 1;
DnsHeader->AnswersNum = 0;
DnsHeader->AuthorityNum = 0;
DnsHeader->AditionalNum = 0;
DnsHeader->Identification = HTONS (DnsHeader->Identification);
DnsHeader->Flags.Uint16 = HTONS (DnsHeader->Flags.Uint16);
DnsHeader->QuestionsNum = HTONS (DnsHeader->QuestionsNum);
DnsHeader->AnswersNum = HTONS (DnsHeader->AnswersNum);
DnsHeader->AuthorityNum = HTONS (DnsHeader->AuthorityNum);
DnsHeader->AditionalNum = HTONS (DnsHeader->AditionalNum);
Frag.Len = sizeof (*DnsHeader);
//
// Fill Query name
//
CopyMem (Frag.Bulk + Frag.Len, QueryName, AsciiStrLen (QueryName));
Frag.Len = (UINT32) (Frag.Len + AsciiStrLen (QueryName));
*(Frag.Bulk + Frag.Len) = 0;
Frag.Len ++;
//
// Rest query section
//
DnsQuery = (DNS_QUERY_SECTION *) (Frag.Bulk + Frag.Len);
DnsQuery->Type = HTONS (Type);
DnsQuery->Class = HTONS (Class);
Frag.Len += sizeof (*DnsQuery);
//
// Wrap the Frag in a net buffer.
//
*Packet = NetbufFromExt (&Frag, 1, 0, 0, DnsDummyExtFree, NULL);
if (*Packet == NULL) {
FreePool (Frag.Bulk);
return EFI_OUT_OF_RESOURCES;
}
//
// Store the UdpIo in ProtoData.
//
*((UINTN *) &((*Packet)->ProtoData[0])) = (UINTN) (Instance->UdpIo);
return EFI_SUCCESS;
}
/**
Retransmit the packet.
@param Instance The DNS instance
@param Packet Retransmit the packet
@retval EFI_SUCCESS The packet is retransmitted.
@retval Others Failed to retransmit.
**/
EFI_STATUS
DnsRetransmit (
IN DNS_INSTANCE *Instance,
IN NET_BUF *Packet
)
{
EFI_STATUS Status;
UINT8 *Buffer;
ASSERT (Packet != NULL);
//
// Set the requests to the listening port, other packets to the connected port
//
Buffer = NetbufGetByte (Packet, 0, NULL);
ASSERT (Buffer != NULL);
NET_GET_REF (Packet);
Status = UdpIoSendDatagram (
Instance->UdpIo,
Packet,
NULL,
NULL,
DnsOnPacketSent,
Instance
);
if (EFI_ERROR (Status)) {
NET_PUT_REF (Packet);
}
return Status;
}
/**
The timer ticking function for the DNS services.
@param Event The ticking event
@param Context The DNS service instance
**/
VOID
EFIAPI
DnsOnTimerRetransmit (
IN EFI_EVENT Event,
IN VOID *Context
)
{
DNS_SERVICE *Service;
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
DNS_INSTANCE *Instance;
LIST_ENTRY *EntryNetMap;
NET_MAP_ITEM *ItemNetMap;
DNS4_TOKEN_ENTRY *Dns4TokenEntry;
DNS6_TOKEN_ENTRY *Dns6TokenEntry;
Dns4TokenEntry = NULL;
Dns6TokenEntry = NULL;
Service = (DNS_SERVICE *) Context;
if (Service->IpVersion == IP_VERSION_4) {
//
// Iterate through all the children of the DNS service instance. Time
// out the packet. If maximum retries reached, clean the Token up.
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Dns4ChildrenList) {
Instance = NET_LIST_USER_STRUCT (Entry, DNS_INSTANCE, Link);
EntryNetMap = Instance->Dns4TxTokens.Used.ForwardLink;
while (EntryNetMap != &Instance->Dns4TxTokens.Used) {
ItemNetMap = NET_LIST_USER_STRUCT (EntryNetMap, NET_MAP_ITEM, Link);
Dns4TokenEntry = (DNS4_TOKEN_ENTRY *)(ItemNetMap->Key);
if (Dns4TokenEntry->PacketToLive == 0 || (--Dns4TokenEntry->PacketToLive > 0)) {
EntryNetMap = EntryNetMap->ForwardLink;
continue;
}
//
// Retransmit the packet if haven't reach the maxmium retry count,
// otherwise exit the transfer.
//
if (++Dns4TokenEntry->RetryCounting <= Dns4TokenEntry->Token->RetryCount) {
DnsRetransmit (Instance, (NET_BUF *)ItemNetMap->Value);
EntryNetMap = EntryNetMap->ForwardLink;
} else {
//
// Maximum retries reached, clean the Token up.
//
Dns4RemoveTokenEntry (&Instance->Dns4TxTokens, Dns4TokenEntry);
Dns4TokenEntry->Token->Status = EFI_TIMEOUT;
gBS->SignalEvent (Dns4TokenEntry->Token->Event);
DispatchDpc ();
//
// Free the sending packet.
//
if (ItemNetMap->Value != NULL) {
NetbufFree ((NET_BUF *)(ItemNetMap->Value));
}
EntryNetMap = Instance->Dns4TxTokens.Used.ForwardLink;
}
}
}
}else {
//
// Iterate through all the children of the DNS service instance. Time
// out the packet. If maximum retries reached, clean the Token up.
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Dns6ChildrenList) {
Instance = NET_LIST_USER_STRUCT (Entry, DNS_INSTANCE, Link);
EntryNetMap = Instance->Dns6TxTokens.Used.ForwardLink;
while (EntryNetMap != &Instance->Dns6TxTokens.Used) {
ItemNetMap = NET_LIST_USER_STRUCT (EntryNetMap, NET_MAP_ITEM, Link);
Dns6TokenEntry = (DNS6_TOKEN_ENTRY *) (ItemNetMap->Key);
if (Dns6TokenEntry->PacketToLive == 0 || (--Dns6TokenEntry->PacketToLive > 0)) {
EntryNetMap = EntryNetMap->ForwardLink;
continue;
}
//
// Retransmit the packet if haven't reach the maxmium retry count,
// otherwise exit the transfer.
//
if (++Dns6TokenEntry->RetryCounting <= Dns6TokenEntry->Token->RetryCount) {
DnsRetransmit (Instance, (NET_BUF *) ItemNetMap->Value);
EntryNetMap = EntryNetMap->ForwardLink;
} else {
//
// Maximum retries reached, clean the Token up.
//
Dns6RemoveTokenEntry (&Instance->Dns6TxTokens, Dns6TokenEntry);
Dns6TokenEntry->Token->Status = EFI_TIMEOUT;
gBS->SignalEvent (Dns6TokenEntry->Token->Event);
DispatchDpc ();
//
// Free the sending packet.
//
if (ItemNetMap->Value != NULL) {
NetbufFree ((NET_BUF *) (ItemNetMap->Value));
}
EntryNetMap = Instance->Dns6TxTokens.Used.ForwardLink;
}
}
}
}
}
/**
The timer ticking function for the DNS driver.
@param Event The ticking event
@param Context NULL
**/
VOID
EFIAPI
DnsOnTimerUpdate (
IN EFI_EVENT Event,
IN VOID *Context
)
{
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
DNS4_CACHE *Item4;
DNS6_CACHE *Item6;
Item4 = NULL;
Item6 = NULL;
//
// Iterate through all the DNS4 cache list.
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns4CacheList) {
Item4 = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink);
Item4->DnsCache.Timeout--;
}
Entry = mDriverData->Dns4CacheList.ForwardLink;
while (Entry != &mDriverData->Dns4CacheList) {
Item4 = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink);
if (Item4->DnsCache.Timeout == 0) {
RemoveEntryList (&Item4->AllCacheLink);
FreePool (Item4->DnsCache.HostName);
FreePool (Item4->DnsCache.IpAddress);
FreePool (Item4);
Entry = mDriverData->Dns4CacheList.ForwardLink;
} else {
Entry = Entry->ForwardLink;
}
}
//
// Iterate through all the DNS6 cache list.
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns6CacheList) {
Item6 = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink);
Item6->DnsCache.Timeout--;
}
Entry = mDriverData->Dns6CacheList.ForwardLink;
while (Entry != &mDriverData->Dns6CacheList) {
Item6 = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink);
if (Item6->DnsCache.Timeout == 0) {
RemoveEntryList (&Item6->AllCacheLink);
FreePool (Item6->DnsCache.HostName);
FreePool (Item6->DnsCache.IpAddress);
FreePool (Item6);
Entry = mDriverData->Dns6CacheList.ForwardLink;
} else {
Entry = Entry->ForwardLink;
}
}
}