/** @file
DnsDxe support functions implementation.

Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#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 successfully.
  @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 successfully.
  @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 cancel 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 cancel 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 successfully.
  @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 successfully.
  @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 occurred.

**/
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 occurred.

**/
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  Length                Received buffer length.
  @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,
  IN     UINT32        Length,
  OUT BOOLEAN          *Completed
  )
{
  DNS_HEADER  *DnsHeader;

  CHAR8              *QueryName;
  UINT32             QueryNameLen;
  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;
  UINT32      RemainingLength;

  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;
  RemainingLength = Length;

  //
  // Check whether the remaining packet length is available or not.
  //
  if (RemainingLength <= sizeof (DNS_HEADER)) {
    *Completed = FALSE;
    return EFI_ABORTED;
  } else {
    RemainingLength -= sizeof (DNS_HEADER);
  }

  //
  // 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);

  //
  // There is always one QuestionsNum in DNS message. The capability to handle more
  // than one requires to redesign the message format. Currently, it's not supported.
  //
  if (DnsHeader->QuestionsNum > 1) {
    *Completed = FALSE;
    return EFI_UNSUPPORTED;
  }

  //
  // Get Query name
  //
  QueryName = (CHAR8 *)(RxString + sizeof (*DnsHeader));

  QueryNameLen = (UINT32)AsciiStrLen (QueryName) + 1;

  //
  // Check whether the remaining packet length is available or not.
  //
  if (RemainingLength <= QueryNameLen + sizeof (DNS_QUERY_SECTION)) {
    *Completed = FALSE;
    return EFI_ABORTED;
  } else {
    RemainingLength -= (QueryNameLen + sizeof (DNS_QUERY_SECTION));
  }

  //
  // Get query section
  //
  QuerySection        = (DNS_QUERY_SECTION *)(QueryName + QueryNameLen);
  QuerySection->Type  = NTOHS (QuerySection->Type);
  QuerySection->Class = NTOHS (QuerySection->Class);

  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;

  //
  // Get Answer name
  //
  AnswerName = (CHAR8 *)QuerySection + sizeof (*QuerySection);

  //
  // Processing AnswerSection.
  //
  while (AnswerSectionNum < DnsHeader->AnswersNum) {
    //
    // Check whether the remaining packet length is available or not.
    //
    if (RemainingLength <= sizeof (UINT16) + sizeof (DNS_ANSWER_SECTION)) {
      *Completed = FALSE;
      Status     = EFI_ABORTED;
      goto ON_EXIT;
    } else {
      RemainingLength -= (sizeof (UINT16) + sizeof (DNS_ANSWER_SECTION));
    }

    //
    // 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 the remaining packet length is available or not.
    //
    if (RemainingLength < AnswerSection->DataLength) {
      *Completed = FALSE;
      Status     = EFI_ABORTED;
      goto ON_EXIT;
    } else {
      RemainingLength -= 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);
          Dns4TokenEntry->Token->RspData.H2AData = NULL;
        }
      }
    }

    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);
          Dns6TokenEntry->Token->RspData.H2AData = NULL;
        }
      }
    }

    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;
  UINT32  Len;

  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);

  Len = Packet->TotalSize;

  RcvString = NetbufGetByte (Packet, 0, NULL);
  ASSERT (RcvString != NULL);

  //
  // Parse Dns Response
  //
  ParseDnsResponse (Instance, RcvString, Len, &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 maximum 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 maximum 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;
    }
  }
}
