Newer
Older
AMI-Aptio-BIOS-Reversed / AmiNetworkPkg / UefiNetworkStack / Common / DnsDxe / DnsDxe / DnsDxe.c
@Ajax Dong Ajax Dong 2 days ago 51 KB Recovering names (cleanups)
/* DnsDxe.c -- DNS protocol DXE driver
 *
 * Source: AmiNetworkPkg/UefiNetworkStack/Common/DnsDxe/
 *   DnsDriver.c   -  _ModuleEntryPoint, driver binding, service binding
 *   DnsProtocol.c -  EFI_DNS4_PROTOCOL and EFI_DNS6_PROTOCOL methods
 *   DnsImpl.c     -  Packet parsing, DNS query building, response processing
 *
 * Reconstructed from DnsDxe.efi (Index 0144, SHA256 61d65b3b4799...)
 */

#include "DnsDxe.h"

/* ========================================================================
 * Global variable definitions
 * ======================================================================== */
EFI_HANDLE            ImageHandle     = NULL;
EFI_SYSTEM_TABLE      *SystemTable    = NULL;
EFI_BOOT_SERVICES     *BootServices   = NULL;
EFI_RUNTIME_SERVICES  *RuntimeServices = NULL;

/* DPC event handle -- used to queue deferred DNS response processing.
 * Located via gBS->LocateProtocol(&gEfiDpcProtocolGuid, 0, &DpcEvent). */
VOID  *DpcEvent   = NULL;

/* DNS Timer event handle (periodic, 1-second interval) */
EFI_EVENT           DnsTimer        = NULL;

/* DNS cache + retry management state.
 * qword_BF90 points to a DNS_CACHE_MANAGER structure:
 *   0x00: TimerEvent (EFI_EVENT)
 *   0x08: CacheList  (LIST_ENTRY head for IPv4 cache entries)
 *   0x18: ...
 *   0x28: ServerList (LIST_ENTRY head for IPv4 server info)
 *   0x38: ...
 *   0x40: CacheList6 (LIST_ENTRY head for IPv6 cache entries)
 *   0x50: ...
 *   0x58: ServerList6 (LIST_ENTRY head for IPv6 server info)
 *   0x68: ...
 */
UINTN  DnsCacheManager  = 0;

/* Debug output protocol handle */
VOID  *DebugOutProtocol = NULL;

/* HOB list pointer for system table retrieval */
VOID  *HobList          = NULL;

/* The driver binding protocol handles */
EFI_HANDLE  Dns4DriverBindingHandle = NULL;
EFI_HANDLE  Dns6DriverBindingHandle = NULL;

/* Known RR type mapping table -- used during DNS response validation
 * to verify that received record type values are recognized. */
UINT32  DnsKnownRrTypes[33] = { 0 };

/* ========================================================================
 * Forward declarations for internally-used functions
 * (mapped from sub_XXXX to meaningful names)
 * ======================================================================== */

/* --- Utility / memory wrappers --- */
static EFI_STATUS  EFIAPI DxeAllocatePool(UINTN Size, VOID **Buffer);
static VOID        *AllocateZeroPool(UINTN Size);
static VOID        *AllocateCopyPool(VOID *Dst, VOID *Src, UINTN Size);
static VOID        ZeroMem(VOID *Buffer, UINTN Size);
static EFI_STATUS  FreePoolAligned(VOID *Buffer);

/* --- Linked-list helpers --- */
static VOID        RemoveEntryList(LIST_ENTRY *Entry);
static BOOLEAN     IsListEmpty(CONST LIST_ENTRY *Entry);
static VOID        InsertHeadList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry);
static VOID        InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry);

/* --- ASSERT / debug output --- */
static VOID        DebugAssert(CONST CHAR8 *FileName, UINTN LineNumber,
                               CONST CHAR8 *Desc);
static VOID        DebugPrint(UINTN ErrorLevel, CONST CHAR8 *Format, ...);

/* --- NET_MAP operations --- */
static EFI_STATUS  NetMapInsert(NET_MAP *Map, INTN Key, INTN Value, VOID *Ptr);
static EFI_STATUS  NetMapIterate(NET_MAP *Map, INTN (*Func)(NET_MAP_ITEM *, VOID *), VOID *Context);
static INTN        NetMapGetCount(NET_MAP *Map);
static VOID        NetMapCleanup(NET_MAP *Map);

/* --- NET_BUF operations --- */
static NET_BUF     *NetbufFromExt(VOID *Data, UINTN Size);
static EFI_STATUS  NetbufFree(NET_BUF *Nbuf);

/* --- DNS packet processing --- */
static EFI_STATUS  DnsBuildPacket(DNS_INSTANCE *Instance,
                                  CHAR16 *QueryName,
                                  UINT16 QueryType, UINT16 QueryClass,
                                  NET_BUF **Packet);
static EFI_STATUS  DnsParseResponse(DNS_INSTANCE *Instance,
                                    NET_BUF *Packet,
                                    DNS4_TOKEN_ENTRY *Token4 OPTIONAL,
                                    DNS6_TOKEN_ENTRY *Token6 OPTIONAL);
static EFI_STATUS  DnsTransmitPacket(DNS_INSTANCE *Instance, NET_BUF *Packet);

/* --- Cache management --- */
static EFI_STATUS  DnsCacheInsert(DNS_INSTANCE *Instance, UINTN IpVersion,
                                  CHAR16 *HostName, VOID *Address,
                                  UINT32 TtlValue);
static EFI_STATUS  DnsCacheRemove(DNS_INSTANCE *Instance, UINTN IpVersion,
                                  CHAR16 *Hostname);

/* --- Timer callback --- */
static VOID        EFIAPI DnsPeriodicTimerNotify(EFI_EVENT Event, VOID *Context);
static VOID        EFIAPI DnsCacheTimerNotify(EFI_EVENT Event, VOID *Context);
static VOID        EFIAPI DnsOnUdpReceive(VOID *Context, VOID *Packet);
static VOID        EFIAPI DnsTransmitRetransmit(DNS_INSTANCE *Instance,
                                                NET_BUF *Packet);

/* ========================================================================
 * Module Entry Point
 * Called by UEFI core when the driver is loaded.
 * ======================================================================== */
EFI_STATUS
EFIAPI
ModuleEntryPoint(
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_STATUS  Status;
  UINTN       DpcHandle;

  /* Save global handles */
  ::ImageHandle  = ImageHandle;
  ::SystemTable  = (UINTN)SystemTable;
  BootServices   = (UINTN)SystemTable->BootServices;
  RuntimeServices = (UINTN)SystemTable->RuntimeServices;

  /* Initialize HOB list for system table access */
  DxeGetHobList();

  /* Locate DPC protocol handle */
  Status = BootServices->LocateProtocol(
             &gEfiDpcProtocolGuid, 0, (VOID **)&DpcEvent);
  if (EFI_ERROR(Status)) {
    DEBUG((EFI_D_ERROR, "ASSERT! (Status = %r)\n", Status));
    ASSERT(!EFI_ERROR(Status));
  }

  /* Install driver binding protocols and service bindings */
  Status = DnsDriverEntry(ImageHandle);
  if (EFI_ERROR(Status)) {
    return Status;
  }

  /* Install protocol interfaces on ImageHandle */
  Status = BootServices->InstallMultipleProtocolInterfaces(
             &Dns4DriverBindingHandle,
             &gEfiDns4ServiceBindingProtocolGuid,
             &mDns4ServiceBinding,
             &gEfiComponentName2ProtocolGuid,
             &mDnsComponentName,
             NULL);
  if (EFI_ERROR(Status)) {
    BootServices->UninstallMultipleProtocolInterfaces(
                   ImageHandle,
                   &gEfiDns4ServiceBindingProtocolGuid,
                   &mDns4ServiceBinding,
                   &gEfiComponentName2ProtocolGuid,
                   &mDnsComponentName,
                   NULL);
  }

  return Status;
}

/* ========================================================================
 * Driver Binding protocol callbacks
 * ======================================================================== */

/* DNS_DRIVER_BINDING_PROTOCOL Support/Start/Stop */
EFI_STATUS
EFIAPI
Dns4DriverBindingSupported(
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS  Status;
  VOID        *Interface;

  Status = BootServices->OpenProtocol(
             ControllerHandle,
             &gEfiUdp4ServiceBindingProtocolGuid,
             &Interface,
             This->DriverBindingHandle,
             ControllerHandle,
             EFI_OPEN_PROTOCOL_BY_DRIVER);
  if (!EFI_ERROR(Status)) {
    BootServices->CloseProtocol(
                   ControllerHandle,
                   &gEfiUdp4ServiceBindingProtocolGuid,
                   This->DriverBindingHandle,
                   ControllerHandle);
    return EFI_SUCCESS;
  }

  Status = BootServices->OpenProtocol(
             ControllerHandle,
             &gEfiUdp4ProtocolGuid,
             &Interface,
             This->DriverBindingHandle,
             ControllerHandle,
             EFI_OPEN_PROTOCOL_BY_DRIVER);
  if (!EFI_ERROR(Status)) {
    BootServices->CloseProtocol(
                   ControllerHandle,
                   &gEfiUdp4ProtocolGuid,
                   This->DriverBindingHandle,
                   ControllerHandle);
    return EFI_SUCCESS;
  }

  return EFI_UNSUPPORTED;
}

EFI_STATUS
EFIAPI
Dns4DriverBindingStart(
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  return DnsCreateService(ControllerHandle, 4);
}

EFI_STATUS
EFIAPI
Dns4DriverBindingStop(
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer
  )
{
  return DnsDestroyService(ControllerHandle, 4);
}

EFI_STATUS
EFIAPI
Dns6DriverBindingSupported(
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS  Status;
  VOID        *Interface;

  Status = BootServices->OpenProtocol(
             ControllerHandle,
             &gEfiUdp6ServiceBindingProtocolGuid,
             &Interface,
             This->DriverBindingHandle,
             ControllerHandle,
             EFI_OPEN_PROTOCOL_BY_DRIVER);
  if (!EFI_ERROR(Status)) {
    BootServices->CloseProtocol(
                   ControllerHandle,
                   &gEfiUdp6ServiceBindingProtocolGuid,
                   This->DriverBindingHandle,
                   ControllerHandle);
    return EFI_SUCCESS;
  }

  Status = BootServices->OpenProtocol(
             ControllerHandle,
             &gEfiUdp6ProtocolGuid,
             &Interface,
             This->DriverBindingHandle,
             ControllerHandle,
             EFI_OPEN_PROTOCOL_BY_DRIVER);
  if (!EFI_ERROR(Status)) {
    BootServices->CloseProtocol(
                   ControllerHandle,
                   &gEfiUdp6ProtocolGuid,
                   This->DriverBindingHandle,
                   ControllerHandle);
    return EFI_SUCCESS;
  }

  return EFI_UNSUPPORTED;
}

EFI_STATUS
EFIAPI
Dns6DriverBindingStart(
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  return DnsCreateService(ControllerHandle, 6);
}

EFI_STATUS
EFIAPI
Dns6DriverBindingStop(
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                  *ChildHandleBuffer
  )
{
  return DnsDestroyService(ControllerHandle, 6);
}

/* ========================================================================
 * DnsCreateService -- Create DNS service binding for given IP version.
 *
 * Allocates DNS_SERVICE_BINDING_PRIVATE structure, opens UDP protocol,
 * and creates the cache-expiry timer.
 * ======================================================================== */
EFI_STATUS
DnsCreateService(
  IN EFI_HANDLE   ControllerHandle,
  IN UINT8        IpVersion       /* 4 or 6 */
  )
{
  EFI_STATUS  Status;
  DNS_SERVICE_BINDING  *Service;
  EFI_GUID    *UdpServiceGuid;
  EFI_GUID    *DnsSbGuid;
  EFI_HANDLE  UdpHandle;
  UINTN       UdpVersion;
  UDP_IO      *UdpIo;

  *ServiceBinding = 0;
  Service = AllocateZeroPool(sizeof(DNS_SERVICE_BINDING));
  if (Service == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Service->Signature = DNS_SB_SIGNATURE;

  if (IpVersion == 4) {
    UdpServiceGuid = &gEfiUdp4ServiceBindingProtocolGuid;
    UdpVersion     = 4;
  } else {
    UdpServiceGuid = &gEfiUdp6ServiceBindingProtocolGuid;
    UdpVersion     = 6;
  }

  /* Open UDP service binding on controller */
  Status = BootServices->OpenProtocol(
             ControllerHandle,
             UdpServiceGuid,
             (VOID **)&UdpHandle,
             ControllerHandle,
             ControllerHandle,
             EFI_OPEN_PROTOCOL_BY_DRIVER);
  if (EFI_ERROR(Status)) {
    goto Error;
  }

  /* Create UDP IO */
  UdpIo = UdpIoCreateIo(
             ControllerHandle,
             Service->DriverBindingHandle,
             UdpVersion,
             0);
  if (UdpIo == NULL) {
    goto Error;
  }

  Service->UdpIo = UdpIo;

  /* Create a periodic timer event for cache expiry */
  Status = BootServices->CreateEvent(
             EVT_TIMER | EVT_NOTIFY_SIGNAL,
             TPL_CALLBACK,
             DnsCacheTimerNotify,
             Service,
             &Service->TimerEvent);
  if (EFI_ERROR(Status)) {
    UdpIoDestroyIo(UdpIo);
    goto Error;
  }

  /* Set 1-second periodic timer */
  Status = BootServices->SetTimer(
             Service->TimerEvent,
             TimerPeriodic,
             10000000);
  if (EFI_ERROR(Status)) {
    BootServices->CloseEvent(Service->TimerEvent);
    UdpIoDestroyIo(UdpIo);
    goto Error;
  }

  Service->IpVersion     = IpVersion;
  Service->RefCount      = 0;
  Service->ControllerHandle = ControllerHandle;

  *ServiceBinding = Service;
  return EFI_SUCCESS;

Error:
  FreePoolAligned(Service);
  return EFI_OUT_OF_RESOURCES;
}

/* ========================================================================
 * DnsDestroyService -- Tear down DNS service binding.
 * ======================================================================== */
EFI_STATUS
DnsDestroyService(
  IN EFI_HANDLE  ControllerHandle,
  IN UINT8       IpVersion
  )
{
  DNS_SERVICE_BINDING  *Service;

  /* Locate the service instance from controller handle */
  Service = DnsLookupService(ControllerHandle, IpVersion);
  if (Service == NULL) {
    return EFI_NOT_FOUND;
  }

  /* Clean up any pending transactions (IPv4 and/or IPv6) */
  if (!NetMapIsEmpty(&Service->Dns4TxTokens)) {
    DnsCleanupTokenMap(&Service->Dns4TxTokens,
                       DNS4_TOKEN_ENTRY_FROM_MAP_ITEM,
                       EFI_ABORTED);
  }

  if (!NetMapIsEmpty(&Service->Dns6TxTokens)) {
    DnsCleanupTokenMap(&Service->Dns6TxTokens,
                       DNS6_TOKEN_ENTRY_FROM_MAP_ITEM,
                       EFI_ABORTED);
  }

  /* Close timer event */
  if (Service->DnsTimerEvent != NULL) {
    BootServices->CloseEvent(Service->DnsTimerEvent);
  }

  /* Destroy UDP IO */
  if (Service->UdpIo != NULL) {
    UdpIoDestroyIo(Service->UdpIo);
  }

  /* Free the service structure itself */
  FreePoolAligned(Service);

  return EFI_SUCCESS;
}

/* ========================================================================
 * DnsDriverEntry -- Top-level driver entry called from entry point.
 * Installs the driver binding protocol on ImageHandle for both IPv4 and IPv6.
 * ======================================================================== */
EFI_STATUS
DnsDriverEntry(
  IN EFI_HANDLE  ImageHandle
  )
{
  EFI_STATUS  Status;

  /* Install DNSv4 driver binding */
  Status = EfiLibInstallDriverBindingComponentName2(
             ImageHandle,
             SystemTable,
             &mDns4DriverBinding,
             ImageHandle);
  if (EFI_ERROR(Status)) {
    return Status;
  }

  /* Install DNSv6 driver binding */
  Status = EfiLibInstallDriverBindingComponentName2(
             ImageHandle,
             SystemTable,
             &mDns6DriverBinding,
             NULL);
  if (EFI_ERROR(Status)) {
    /* On failure, uninstall the DNSv4 binding + component names */
    BootServices->UninstallMultipleProtocolInterfaces(
                   ImageHandle,
                   &gEfiDns4ServiceBindingProtocolGuid,
                   &mDns4DriverBinding,
                   &gEfiComponentName2ProtocolGuid,
                   &mDnsComponentName,
                   &gEfiComponentNameProtocolGuid,
                   &mDnsComponentName,
                   NULL);
    return Status;
  }

  /* Allocate cache manager state for DnsCacheManager (qword_BF90) */
  DnsCacheManager = (UINTN)AllocateZeroPool(sizeof(DNS_CACHE_MANAGER));
  if (DnsCacheManager == 0) {
    return EFI_OUT_OF_RESOURCES;
  }

  /* Create a DPC-related timer for deferred response processing */
  Status = BootServices->CreateEvent(
             EVT_NOTIFY_SIGNAL,
             TPL_NOTIFY,
             DnsPeriodicTimerNotify,
             NULL,
             (EFI_EVENT *)DnsCacheManager);
  if (EFI_ERROR(Status)) {
    goto Error;
  }

  Status = BootServices->SetTimer(
             *(EFI_EVENT *)DnsCacheManager,
             TimerPeriodic,
             10000000);
  if (EFI_ERROR(Status)) {
    goto Error;
  }

  /* Initialize cache lists */
  InitializeListHead((LIST_ENTRY *)(DnsCacheManager + 8));
  InitializeListHead((LIST_ENTRY *)(DnsCacheManager + 24));
  InitializeListHead((LIST_ENTRY *)(DnsCacheManager + 40));
  InitializeListHead((LIST_ENTRY *)(DnsCacheManager + 56));

  return EFI_SUCCESS;

Error:
  if (*(EFI_EVENT *)DnsCacheManager != NULL) {
    BootServices->CloseEvent(*(EFI_EVENT *)DnsCacheManager);
  }
  if (DnsCacheManager) {
    FreePoolAligned((VOID *)DnsCacheManager);
    DnsCacheManager = 0;
  }
  return Status;
}

/* ========================================================================
 * DNSv4 Protocol: GetHostByName
 * ======================================================================== */
EFI_STATUS
EFIAPI
Dns4GetHostByName(
  IN  EFI_DNS4_PROTOCOL          *This,
  IN  CHAR16                     *Hostname,
  OUT EFI_DNS4_COMPLETION_TOKEN  *Token
  )
{
  DNS_INSTANCE      *Instance;
  EFI_STATUS        Status;
  UINTN             ServerCount;
  UINT32            *ServerList;
  LIST_ENTRY        *Entry;
  DNS_CACHE_ENTRY   *CacheEntry;
  UINTN             Index;

  if (This == NULL || Hostname == NULL || Token == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = DNS_INSTANCE_FROM_DNS4_PROTOCOL(This);
  if (Instance == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  /* Check if driver is started */
  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  /* If already configured, return cached entry */
  if (Instance->Dns4CfgData.UseDefaultSetting) {
    /* Initialize the token response */
    ZeroMem((VOID *)&Token->RspData, sizeof(Token->RspData));
    Token->Status = EFI_NOT_READY;

    if (Instance->Dns4RetryCount > 0) {
      /* Attempt to look up from cache first */
      ServerList = NULL;
      ServerCount = Instance->Dns4CacheMap.Count;

      /* Fill in server list */
      Token->RspData.General.ResponseBufferSize = ServerCount * sizeof(EFI_IPv4_ADDRESS);

      /* Find the entry: iterate cache map */
      Entry = Instance->Dns4CacheMap.List.ForwardLink;
      Index = 0;
      return EFI_SUCCESS;
    }
  }

  return EFI_DEVICE_ERROR;
}

/* ========================================================================
 * DNSv4 Protocol: GetHostByAddr
 * ======================================================================== */
EFI_STATUS
EFIAPI
Dns4GetHostByAddr(
  IN  EFI_DNS4_PROTOCOL          *This,
  IN  EFI_IPv4_ADDRESS           *IpAddress,
  OUT EFI_DNS4_COMPLETION_TOKEN  *Token
  )
{
  DNS_INSTANCE      *Instance;
  UINTN             Ret;
  UINTN             TokenData;
  UINT32            ReverseIp;
  UINT32            IpCheck;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  if (Token != NULL) {
    if (Token->Event != NULL && Token->Status != NULL) {
      /* Valid: have both */
    } else if (Token->Event == NULL && Token->Status == NULL) {
      /* Neither event nor status -- that is valid too */
    } else {
      return EFI_INVALID_PARAMETER;
    }
    if (Token->RspData.General.QueryBuffer[1] != 17) {
      return EFI_UNSUPPORTED;
    }
  }

  Instance = DNS_INSTANCE_FROM_DNS4_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  /* Build PTR query: reverse IP + ".in-addr.arpa" */
  if (Token != NULL) {
    /* Byte-swap and reverse the IP address */
    ReverseIp = ((UINT32)Token->RspData.General.QueryBuffer[19] << 24) |
                ((UINT32)Token->RspData.General.QueryBuffer[20] << 16) |
                ((UINT32)Token->RspData.General.QueryBuffer[21] << 8)  |
                ((UINT32)Token->RspData.General.QueryBuffer[22]);

    /* Validate IP against known PTR types */
    if (Token->RspData.General.ResponseBuffer[0] == 0) {
      /* Check each byte */
      UINTN i;
      for (i = 0; i <= 32; i++) {
        if (ReverseIp == DnsKnownRrTypes[i])
          break;
      }
      if (ReverseIp == 0)
        goto Submit;

      /* Check if non-zero bits mask matches */
      if (i == 31 || ...) {
Submit:
        /* Submit the PTR query as host-by-name with PTR type */
        Ret = DnsSubmitPacket4(Instance, Token);
        if (!EFI_ERROR(Ret)) {
          /* Trigger packet transmission */
          Status = DnsSendQuery(Instance, TokenData, ...);
          if (!EFI_ERROR(Status)) {
            return EFI_SUCCESS;
          }
          /* Cancel if tx failed */
          DnsCancelPacket4(Instance, Token);
        }
      }
    }
  }

  return Ret;
}

/* ========================================================================
 * DNSv4 Protocol: Poll
 * ========================================================================
 * Checks for received network data and processes it.
 */
EFI_STATUS
EFIAPI
Dns4Poll(
  IN EFI_DNS4_PROTOCOL  *This
  )
{
  DNS_INSTANCE  *Instance;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = DNS_INSTANCE_FROM_DNS4_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }
  if (Instance->Mode == 2) {
    return EFI_ACCESS_DENIED;
  }

  /* Call UdpIo's Poll function to drain the receive queue */
  return UdpIoPoll(Instance->UdpIo);
}

/* ========================================================================
 * DNSv4 Protocol: Cancel
 * ========================================================================
 * Cancels a pending DNS query token.
 */
EFI_STATUS
EFIAPI
Dns4Cancel(
  IN  EFI_DNS4_PROTOCOL         *This,
  IN  EFI_DNS4_COMPLETION_TOKEN *Token
  )
{
  DNS_INSTANCE  *Instance;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = DNS_INSTANCE_FROM_DNS4_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  /* Remove from TxTokens map and signal completion */
  return DnsCancelToken(&Instance->Dns4TxTokens,
                        DNS4_TOKEN_ENTRY_FROM_MAP_ITEM,
                        Token);
}

/* ========================================================================
 * DNSv4 Protocol: GetConfigData
 * ========================================================================
 * Returns current DNS configuration data (server list, cache names).
 */
EFI_STATUS
EFIAPI
Dns4GetConfigData(
  IN  EFI_DNS4_PROTOCOL     *This,
  OUT EFI_DNS4_CONFIG_DATA  *ConfigData
  )
{
  DNS_INSTANCE  *Instance;

  if (This == NULL || ConfigData == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = DNS_INSTANCE_FROM_DNS4_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  /* Fill config data from Instance */
  CopyMem(ConfigData, &Instance->Dns4CfgData, sizeof(EFI_DNS4_CONFIG_DATA));
  ConfigData->StationIp   = Instance->Dns4CfgData.StationIp;
  ConfigData->LocalPort   = Instance->Dns4CfgData.LocalPort;
  ConfigData->RetryCount  = Instance->Dns4CfgData.RetryCount;
  ConfigData->RetryInterval = Instance->Dns4CfgData.RetryInterval;

  return EFI_SUCCESS;
}

/* ========================================================================
 * DNSv4 Protocol: SetConfigData
 * ========================================================================
 * Sets DNS configuration and creates UDP IO for the new config.
 */
EFI_STATUS
EFIAPI
Dns4SetConfigData(
  IN  EFI_DNS4_PROTOCOL     *This,
  IN  EFI_DNS4_CONFIG_DATA  *ConfigData
  )
{
  return DnsSetConfigData(This, ConfigData, 4);
}

/* ========================================================================
 * DNSv4 Protocol: GetServerList
 * ========================================================================
 * Returns the list of DNS server addresses.
 */
EFI_STATUS
EFIAPI
Dns4GetServerList(
  IN  EFI_DNS4_PROTOCOL  *This,
  IN OUT UINTN           *ServerCount,
  OUT EFI_IPv4_ADDRESS   *ServerList
  )
{
  DNS_INSTANCE  *Instance;
  LIST_ENTRY    *Entry;
  UINTN         Count;
  UINT32        *Addrs;

  if (This == NULL || ServerCount == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = DNS_INSTANCE_FROM_DNS4_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  if (Instance->Mode == 0) {
    Instance = (VOID *)((UINT8 *)Instance - 24);
  }

  if (Instance->Dns4ChildrenList.ForwardLink == NULL) {
    return EFI_NOT_READY;
  }

  /* Count servers */
  Count = 0;
  for (Entry = Instance->Dns4CacheMap.List.ForwardLink;
       Entry != &Instance->Dns4CacheMap.List;
       Entry = Entry->ForwardLink) {
    Count++;
  }

  *ServerCount = Count;

  /* Allocate and fill server addresses */
  Addrs = AllocateZeroPool(sizeof(UINT32) * Count);
  if (Addrs == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Entry = Instance->Dns4CacheMap.List.ForwardLink;
  Count = 0;
  while (Entry != &Instance->Dns4CacheMap.List &&
         Count < *ServerCount) {
    Addrs[Count] = ((DNS_CACHE_ENTRY *)Entry)->TtlValue;
    Count++;
    Entry = Entry->ForwardLink;
  }

  *ServerList = (EFI_IPv4_ADDRESS *)Addrs;
  return EFI_SUCCESS;
}

/* ========================================================================
 * DNSv4 Protocol: GetHostName / SetHostName stubs
 * ========================================================================
 */
EFI_STATUS
EFIAPI
Dns4GetHostName(
  IN  EFI_DNS4_PROTOCOL  *This,
  OUT CHAR16             *Hostname,
  IN OUT UINTN           *HostnameSize
  )
{
  return DnsGetHostName(This, Hostname, HostnameSize, 4);
}

EFI_STATUS
EFIAPI
Dns4SetHostName(
  IN  EFI_DNS4_PROTOCOL  *This,
  IN  CHAR16             *Hostname
  )
{
  return DnsSetHostName(This, Hostname, 4);
}

/* ========================================================================
 * DNSv6 Protocol: GetHostByName
 * ========================================================================
 */
EFI_STATUS
EFIAPI
Dns6GetHostByName(
  IN  EFI_DNS6_PROTOCOL          *This,
  IN  CHAR16                     *Hostname,
  OUT EFI_DNS6_COMPLETION_TOKEN  *Token
  )
{
  DNS_INSTANCE      *Instance;
  EFI_STATUS        Status;
  UINTN             ServerCount;
  EFI_IPv6_ADDRESS  *ServerList;
  LIST_ENTRY        *Entry;
  UINTN             Index;

  if (This == NULL || Hostname == NULL || Token == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = DNS_INSTANCE_FROM_DNS6_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  if (!Instance->Dns6CfgData.UseDefaultSetting) {
    /* Use cache server list */
    ServerCount = Instance->Dns6CacheMap.Count;

    /* Allocate reply data */
    Token->RspData.General.ResponseBuffer =
      AllocateZeroPool(16 * ServerCount);
  }

  return EFI_SUCCESS;
}

/* ========================================================================
 * DNSv6 Protocol: GetHostByAddr
 * ========================================================================
 */
EFI_STATUS
EFIAPI
Dns6GetHostByAddr(
  IN  EFI_DNS6_PROTOCOL          *This,
  IN  EFI_IPv6_ADDRESS           *IpAddress,
  OUT EFI_DNS6_COMPLETION_TOKEN  *Token
  )
{
  DNS_INSTANCE      *Instance;
  UINTN             Ret;
  UINT8             Index;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (Token != NULL) {
    if (Token->RspData.General.ResponseBufferSize != 0) {
      if (Token->RspData.General.ResponseBuffer == NULL) {
        /* Both zero = valid for no-buffer mode */
        goto CheckProtocol;
      }
    }
    CheckProtocol:
    if (Token->RspData.General.QueryBuffer[1] != 17) {
      return EFI_UNSUPPORTED;
    }
  }

  Instance = DNS_INSTANCE_FROM_DNS6_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  /* Validate IPv6 address fields from the token */
  if (Token != NULL) {
    /* Check that the address is non-zero */
    Index = 0;
    while (Index < 16 && Token->RspData.General.QueryBuffer[2 + Index] == 0) {
      Index++;
    }

    if (Index >= 15) {
      return EFI_INVALID_PARAMETER;
    }

    /* Submit PTR query for IPv6 reverse lookup */
    /* Reuse DnsSubmitPacket4/6 infrastructure */
    Token->Status = EFI_NOT_READY;
    Ret = DnsBuildQuery6(Instance, IpAddress, Token);
    if (!EFI_ERROR(Ret)) {
      DnsSendQuery6(Instance, Token);
    }
  } else {
    /* Called with no token - just clean up */
    DnsCancelAll6(Instance);
    Ret = EFI_SUCCESS;
  }

  return Ret;
}

/* ========================================================================
 * DNSv6 Protocol: Poll
 * ========================================================================
 */
EFI_STATUS
EFIAPI
Dns6Poll(
  IN EFI_DNS6_PROTOCOL  *This
  )
{
  /* Same as Dns4Poll but for IPv6 */
  UINT8  Version;

  if (DnsCheckMode(This, 6) <= 0) {
    return EFI_NOT_STARTED;
  }

  /* Drain the receive queue -- poll the UDPv6 IO */
  {
    DNS_INSTANCE *Instance = DNS_INSTANCE_FROM_DNS6_PROTOCOL(This);
    if (Instance->UdpIo != NULL) {
      return UdpIoPoll(Instance->UdpIo);
    }
  }

  return EFI_DEVICE_ERROR;
}

/* ========================================================================
 * DNSv6 Protocol: Cancel
 * ========================================================================
 */
EFI_STATUS
EFIAPI
Dns6Cancel(
  IN  EFI_DNS6_PROTOCOL          *This,
  IN  EFI_DNS6_COMPLETION_TOKEN  *Token
  )
{
  DNS_INSTANCE  *Instance;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = DNS_INSTANCE_FROM_DNS6_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  return Dns6CancelToken(Instance, Token);
}

/* ========================================================================
 * DNSv6 Protocol: GetConfigData
 * ========================================================================
 */
EFI_STATUS
EFIAPI
Dns6GetConfigData(
  IN  EFI_DNS6_PROTOCOL     *This,
  OUT EFI_DNS6_CONFIG_DATA  *ConfigData
  )
{
  DNS_INSTANCE  *Instance;

  if (This == NULL || ConfigData == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = DNS_INSTANCE_FROM_DNS6_PROTOCOL(This);

  if (Instance->Mode == 0) {
    return EFI_NOT_STARTED;
  }

  CopyMem(ConfigData, &Instance->Dns6CfgData, sizeof(EFI_DNS6_CONFIG_DATA));

  return EFI_SUCCESS;
}

/* ========================================================================
 * DNSv6 Protocol: SetConfigData
 * ========================================================================
 */
EFI_STATUS
EFIAPI
Dns6SetConfigData(
  IN  EFI_DNS6_PROTOCOL     *This,
  IN  EFI_DNS6_CONFIG_DATA  *ConfigData
  )
{
  return DnsSetConfigData(This, ConfigData, 6);
}

/* ========================================================================
 * DNSv6 Protocol: GetHostName / SetHostName
 * ========================================================================
 */
EFI_STATUS
EFIAPI
Dns6GetHostName(
  IN  EFI_DNS6_PROTOCOL  *This,
  OUT CHAR16             *Hostname,
  IN OUT UINTN           *HostnameSize
  )
{
  /* TODO: return the configured DNSv6 hostname */
  return EFI_UNSUPPORTED;
}

EFI_STATUS
EFIAPI
Dns6SetHostName(
  IN  EFI_DNS6_PROTOCOL  *This,
  IN  CHAR16             *Hostname
  )
{
  /* TODO: set the DNSv6 hostname */
  return EFI_UNSUPPORTED;
}

/* ========================================================================
 * DNS query building
 * ========================================================================
 * Constructs a DNS query packet on the wire for the given query name,
 * query type (A/AAAA/PTR), and query class (IN).
 */
EFI_STATUS
DnsBuildQuery(
  IN  DNS_INSTANCE  *Instance,
  IN  CHAR8         *QueryName,
  IN  UINT16        QueryType,
  IN  UINT16        QueryClass,
  OUT NET_BUF       **Packet
  )
{
  DNS_HEADER    *DnsHeader;
  UINT8         *PacketData;
  UINT8         *NamePtr;
  UINTN         NameLen;
  UINTN         TotalSize;
  UINT16        Id;
  EFI_STATUS    Status;

  PacketData = AllocateZeroPool(512);
  if (PacketData == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  /* Generate a random identifier */
  Id = (UINT16)((1103515245 * (UINT32)DnsRandom() + 12345) % 0xFFFFFFFF);

  /* Build DNS header */
  DnsHeader = (DNS_HEADER *)PacketData;
  DnsHeader->Identification = SwapBytes16(Id);
  DnsHeader->Flags          = SwapBytes16(0x0100);  /* Standard query, RD=1 */
  DnsHeader->Questions      = SwapBytes16(1);

  /* Encode the query name as DNS label sequence */
  NamePtr = PacketData + sizeof(DNS_HEADER);
  NameLen = AsciiStrLen(QueryName) + 2;  /* +2 for root terminator */
  DnsNameToLabels(NamePtr, QueryName);

  /* Question footer */
  NamePtr += NameLen;
  *(UINT16 *)NamePtr   = SwapBytes16(QueryType);
  *(UINT16 *)(NamePtr + 2) = SwapBytes16(QueryClass);

  TotalSize = (UINTN)(NamePtr + 4 - PacketData);

  /* Convert to NetBuf */
  *Packet = NetbufFromExt(PacketData, TotalSize);
  if (*Packet == NULL) {
    FreePoolAligned(PacketData);
    return EFI_OUT_OF_RESOURCES;
  }

  return EFI_SUCCESS;
}

/* ========================================================================
 * Internal DNS response parsing
 * ========================================================================
 * Parses a received DNS response packet from the wire.
 * Handles A, AAAA, CNAME, and PTR records.
 */
EFI_STATUS
DnsParseResponse(
  IN  DNS_INSTANCE         *Instance,
  IN  NET_BUF              *Packet,
  IN  DNS4_TOKEN_ENTRY     *Token4    OPTIONAL,
  IN  DNS6_TOKEN_ENTRY     *Token6    OPTIONAL
  )
{
  DNS_HEADER    *DnsHeader;
  UINT8         *RxData;
  UINTN         RxDataLen;
  UINT16        Questions;
  UINT16        Answers;
  UINT16        AuthorityRRs;
  UINT16        AdditionalRRs;
  UINT8         *Cursor;
  UINTN         Offset;
  UINTN         AnswerIndex;
  UINT16        RType;
  UINT16        RClass;
  UINT32        RTtl;
  UINT16        RdLength;
  UINT8         *RData;
  UINT8         IpVersion;
  BOOLEAN       Done;
  EFI_STATUS    Status;

  Done     = FALSE;
  Status   = EFI_SUCCESS;
  RxData   = (UINT8 *)NetbufGetData(Packet);
  RxDataLen = NetbufGetSize(Packet);
  IpVersion = Instance->IpVersion;

  if (RxDataLen <= 12) {
    *Done = TRUE;
    return EFI_UNSUPPORTED;
  }

  /* Byte-swap the DNS header fields */
  DnsHeader = (DNS_HEADER *)RxData;
  DnsHeader->Identification = SwapBytes16(DnsHeader->Identification);
  DnsHeader->Flags          = SwapBytes16(DnsHeader->Flags);
  DnsHeader->Questions      = SwapBytes16(DnsHeader->Questions);
  DnsHeader->Answers        = SwapBytes16(DnsHeader->Answers);
  DnsHeader->Authority      = SwapBytes16(DnsHeader->Authority);
  DnsHeader->Additional     = SwapBytes16(DnsHeader->Additional);

  Questions     = DnsHeader->Questions;
  Answers       = DnsHeader->Answers;
  AuthorityRRs  = DnsHeader->Authority;
  AdditionalRRs = DnsHeader->Additional;

  /* Validate: this is a response with no error */
  if ((DnsHeader->Flags & 0x800F) != 0x8000) {
    *Done = TRUE;
    return EFI_PROTOCOL_ERROR;
  }

  /* Skip questions */
  if (Questions > 1) {
    *Done = TRUE;
    return EFI_PROTOCOL_ERROR;
  }

  /* Process answer records */
  AnswerIndex = 0;

  while (AnswerIndex < Answers) {
    /* Skip compressed name */
    Cursor = RxData + 12;
    /* Parse RR header */
    if (Cursor[0] & 0xC0) {
      Cursor += 2;
    } else {
      while (*Cursor != 0) Cursor++;
      Cursor++;
    }

    /* Byte-swap RR header fields */
    RType    = SwapBytes16(*(UINT16 *)Cursor);
    RClass   = SwapBytes16(*(UINT16 *)(Cursor + 2));
    RTtl     = SwapBytes32(*(UINT32 *)(Cursor + 4));
    RdLength = SwapBytes16(*(UINT16 *)(Cursor + 8));
    RData    = Cursor + 10;

    if ((IpVersion == 4 && Token4 &&
         Token4->IsHostByName && RType == DNS_TYPE_A) ||
        (IpVersion == 6 && Token6 &&
         Token6->IsHostByName && RType == DNS_TYPE_AAAA)) {
      /* Save address */
      if (IpVersion == 4) {
        if (RdLength >= 4) {
          CopyMem(Token4->RcvData, RData, 4);
        }
      } else {
        if (RdLength >= 16) {
          CopyMem(Token6->RcvData, RData, 16);
        }
      }
    }

    /* Insert into cache */
    if (RType == DNS_TYPE_A && Token4 != NULL && Token4->IsHostByName) {
      DnsCacheInsert(Instance, 4, Token4->HostName, Cursor + 10, RTtl);
    }

    /* Update cache TTL for subsequent records */
    Cursor += RdLength + 10;
    AnswerIndex++;
  }

  *Done = TRUE;
  return EFI_SUCCESS;
}

/* ========================================================================
 * DnsSubmitPacket4 -- Submit a DNSv4 query packet for transmission.
 * ======================================================================== */
EFI_STATUS
DnsSubmitPacket4(
  IN DNS_INSTANCE                *Instance,
  IN CHAR16                      *Hostname,
  IN UINT16                      QueryType,
  IN UINT16                      QueryClass,
  IN EFI_DNS4_COMPLETION_TOKEN   *Token
  )
{
  DNS4_TOKEN_ENTRY  *TokenEntry;
  NET_BUF           *Packet;
  EFI_STATUS        Status;

  if (Instance == NULL || Hostname == NULL || Token == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  TokenEntry = AllocateZeroPool(sizeof(DNS4_TOKEN_ENTRY));
  if (TokenEntry == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  TokenEntry->RetryCount   = Token->RspData.General.ResponseBufferSize;
  TokenEntry->IsHostByName = (QueryType == DNS_TYPE_A);
  TokenEntry->Token        = Token;
  TokenEntry->HostName     = Hostname;

  /* Build and transmit the query */
  Status = DnsBuildQuery(Instance, Hostname, QueryType, QueryClass, &Packet);
  if (EFI_ERROR(Status)) {
    FreePoolAligned(TokenEntry);
    return Status;
  }

  /* Insert into Tx token map */
  Status = NetMapInsert(&Instance->Dns4TxTokens, 0, 0, TokenEntry);
  if (EFI_ERROR(Status)) {
    NetbufFree(Packet);
    FreePoolAligned(TokenEntry);
    return Status;
  }

  /* Transmit packet */
  Status = DnsTransmit(Instance, Packet);
  if (EFI_ERROR(Status)) {
    NetMapIterate(&Instance->Dns4TxTokens, NULL, TokenEntry);
    NetbufFree(Packet);
    FreePoolAligned(TokenEntry);
    return Status;
  }

  return EFI_SUCCESS;
}

/* ========================================================================
 * Periodic timer callback.
 * ========================================================================
 * Fires every second. Decrements retry timers for pending token entries.
 * When a retry count reaches zero, the token is aborted and a timeout
 * status is signaled.
 */
VOID
EFIAPI
DnsPeriodicTimerNotify(
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  DNS_SERVICE_BINDING  *Service = (DNS_SERVICE_BINDING *)Context;
  LIST_ENTRY           *Entry4, *Entry6, *Next4, *Next6;
  DNS4_TOKEN_ENTRY     *Token4;
  DNS6_TOKEN_ENTRY     *Token6;

  if (Service == NULL) {
    return;
  }

  if (Service->IpVersion == 4) {
    /* Walk the IPv4 token map */
    Entry4 = Service->Dns4TxTokens.List.ForwardLink;
    while (Entry4 != &Service->Dns4TxTokens.List) {
      DNS4_TOKEN_ENTRY *T4;
      Next4 = Entry4->ForwardLink;
      T4    = DNS4_TOKEN_ENTRY_FROM_MAP_ITEM(Entry4);

      if (T4->RetryTimer > 0) {
        T4->RetryTimer--;
        if (T4->RetryTimer == 0) {
          T4->RetryCount++;
          if (T4->RetryCount >= T4->TimeoutValue) {
            /* Abort this entry */
            NetMapIterate(&Service->Dns4TxTokens, NULL, T4);

            /* Signal completion with timeout */
            T4->Token->Status = EFI_TIMEOUT;
            BootServices->SignalEvent(T4->Token->Event);
            if (Service->UdpIo != NULL) {
              UdpIoRecycle(Service->UdpIo);
            }

            /* Done with this entry */
            NetMapIterate(&Service->Dns4TxTokens, NULL, T4);
          } else {
            /* Retransmit */
            NetMapIterate(&Service->Dns4TxTokens, NULL, T4);
            T4->RetryTimer = T4->TimeoutValue;
          }
        }
      }
      Entry4 = Next4;
    }
  } else {
    /* Walk the IPv6 token map */
    Entry6 = Service->Dns6TxTokens.List.ForwardLink;
    while (Entry6 != &Service->Dns6TxTokens.List) {
      DNS6_TOKEN_ENTRY *T6;
      Next6 = Entry6->ForwardLink;
      T6    = DNS6_TOKEN_ENTRY_FROM_MAP_ITEM(Entry6);

      if (T6->RetryTimer > 0) {
        T6->RetryTimer--;
        if (T6->RetryTimer == 0) {
          T6->RetryCount++;
          if (T6->RetryCount >= T6->TimeoutValue) {
            /* Abort */
            T6->Token->Status = EFI_TIMEOUT;
            BootServices->SignalEvent(T6->Token->Event);
            if (Service->UdpIo != NULL) {
              UdpIoRecycle(Service->UdpIo);
            }
          } else {
            /* Retransmit */
            T6->RetryTimer = T6->TimeoutValue;
          }
        }
      }
      Entry6 = Next6;
    }
  }
}

/* ========================================================================
 * Cache manager periodic timer.
 * ========================================================================
 * Decrements TTL for every cache entry (both IPv4 and IPv6).
 * When TTL hits zero, the entry is freed.
 */
VOID
EFIAPI
DnsCacheTimerNotify(
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  DNS_CACHE_MANAGER  *CacheMgr = (DNS_CACHE_MANAGER *)Context;
  LIST_ENTRY         *Entry, *Next;

  if (CacheMgr == NULL) {
    CacheMgr = (DNS_CACHE_MANAGER *)qword_BF90;
    if (CacheMgr == NULL) {
      return;
    }
  }

  /* Process IPv4 cache list */
  Entry = CacheMgr->CacheList.ForwardLink;
  while (Entry != &CacheMgr->CacheList) {
    Next = Entry->ForwardLink;
    ((DNS_CACHE_ENTRY *)Entry)->TtlValue--;

    if (((DNS_CACHE_ENTRY *)Entry)->TtlValue == 0) {
      /* Remove from cache */
      RemoveEntryList(Entry);
      DnsFreeCacheEntry((DNS_CACHE_ENTRY *)Entry);
    }
    Entry = Next;
  }

  /* Process IPv6 cache list */
  Entry = CacheMgr->CacheList6.ForwardLink;
  while (Entry != &CacheMgr->CacheList6) {
    Next = Entry->ForwardLink;
    ((DNS_CACHE_ENTRY *)Entry)->TtlValue--;

    if (((DNS_CACHE_ENTRY *)Entry)->TtlValue == 0) {
      RemoveEntryList(Entry);
      DnsFreeCacheEntry((DNS_CACHE_ENTRY *)Entry);
    }
    Entry = Next;
  }
}

/* ========================================================================
 * DnsSetConfigData -- common handler for both IPv4 and IPv6.
 * ========================================================================
 */
EFI_STATUS
DnsSetConfigData(
  IN VOID    *This,
  IN VOID    *ConfigData,
  IN UINT8   IpVersion
  )
{
  DNS_INSTANCE       *Instance;
  DNS_SERVICE_BINDING *Service;
  EFI_STATUS         Status;

  Instance = NULL;

  /* Create new instance */
  Status = DnsCreateInstance(&Instance, (UINTN)ConfigData, IpVersion);
  if (EFI_ERROR(Status)) {
    return Status;
  }

  /* Save configuration */
  if (IpVersion == 4) {
    CopyMem(&Instance->Dns4CfgData, ConfigData, sizeof(EFI_DNS4_CONFIG_DATA));
  } else {
    CopyMem(&Instance->Dns6CfgData, ConfigData, sizeof(EFI_DNS6_CONFIG_DATA));
  }

  /* Create UDP IO for this configuration */
  Status = DnsCreateUdpIo(Instance);
  if (EFI_ERROR(Status)) {
    DnsDestroyInstance(Instance);
    return Status;
  }

  return EFI_SUCCESS;
}

/* ========================================================================
 * DnsTransmit -- send a DNS query packet via UDP.
 * ========================================================================
 */
EFI_STATUS
DnsTransmit(
  IN DNS_INSTANCE  *Instance,
  IN NET_BUF       *Packet
  )
{
  EFI_STATUS  Status;

  /* Check if we need to create a UDP IO first */
  if (Instance->UdpIo == NULL) {
    Status = DnsCreateUdpIo(Instance);
    if (EFI_ERROR(Status)) {
      return Status;
    }
  }

  /* Increase reference count on packet */
  Packet->RefCnt++;

  /* Queue the packet for transmission */
  Status = UdpIoSend(Instance->UdpIo, Packet, 0, 0);
  if (EFI_ERROR(Status)) {
    Packet->RefCnt--;
    return Status;
  }

  return EFI_SUCCESS;
}

/* ========================================================================
 * DnsRandom -- get a random value for DNS query IDs.
 * ========================================================================
 */
UINT32
DnsRandom(
  VOID
  )
{
  UINT32  Value;

  /* Use RTC to seed a simple LCG */
  Value = (UINT32)(BootServices->GetTimer(&Value, 0) + Value);
  Value = (1103515245 * Value + 12345) % 0xFFFFFFFF;

  return Value;
}

/* ========================================================================
 * ASSERT / debug support.
 * ========================================================================
 */
VOID
DebugAssert(
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Desc
  )
{
  /* In DEBUG builds, print assertion info and halt */
  if (DebugOutProtocol != NULL) {
    ((VOID (*)(VOID *))DebugOutProtocol)(...);
  }

  /* In RELEASE builds: no-op */
  return;
}

/* ========================================================================
 * DnsCreateInstance -- allocates and initializes DNS instance structure.
 * ========================================================================
 */
EFI_STATUS
DnsCreateInstance(
  OUT DNS_INSTANCE  **Instance,
  IN  UINTN         ServiceBinding,
  IN  UINT8         IpVersion
  )
{
  DNS_INSTANCE  *DnsIns;

  *Instance = NULL;

  DnsIns = AllocateZeroPool(sizeof(DNS_INSTANCE));
  if (DnsIns == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  DnsIns->Signature = DNS_INSTANCE_SIGNATURE;
  InitializeListHead(&DnsIns->Dns4TxTokens.List);
  DnsIns->Mode        = 1;     /* Started */
  DnsIns->IpVersion   = IpVersion;

  /* Initialize cache map, tx token map, server list */
  if (IpVersion == 4) {
    InitializeListHead(&DnsIns->Dns4CacheMap.List);
    InitializeListHead(&DnsIns->Dns4ServerList);
  } else {
    InitializeListHead(&DnsIns->Dns6CacheMap.List);
    InitializeListHead(&DnsIns->Dns6ServerList);
  }

  *Instance = DnsIns;
  return EFI_SUCCESS;
}

/* ========================================================================
 * DnsDestroyInstance --  tears down DNS instance
 * ========================================================================
 */
VOID
DnsDestroyInstance(
  IN DNS_INSTANCE  *Instance
  )
{
  if (Instance == NULL) {
    return;
  }

  /* Clean up any pending tokens */
  if (!NetMapIsEmpty(&Instance->Dns4TxTokens)) {
    DnsCleanupTokenMap(&Instance->Dns4TxTokens,
                       DNS4_TOKEN_ENTRY_FROM_MAP_ITEM,
                       EFI_ABORTED);
  }
  if (!NetMapIsEmpty(&Instance->Dns6TxTokens)) {
    DnsCleanupTokenMap(&Instance->Dns6TxTokens,
                       DNS6_TOKEN_ENTRY_FROM_MAP_ITEM,
                       EFI_ABORTED);
  }

  /* Free the UDP IO */
  if (Instance->UdpIo != NULL) {
    UdpIoDestroyIo(Instance->UdpIo);
  }

  FreePoolAligned(Instance);
}

/* ========================================================================
 * DnsNameToLabels -- convert dotted name to DNS label format
 * e.g., "www.example.com" -> 3www7example3com0
 * ========================================================================
 */
UINT8 *
DnsNameToLabels(
  OUT UINT8        *Buf,
  IN  CONST CHAR8  *Name
  )
{
  UINT8 *Orig  = Buf;
  UINT8 *Label = ++Buf;

  while (*Name) {
    if (*Name == '.') {
      /* Terminate label with its length */
      *Orig = (UINT8)(Buf - Orig - 1);
      Orig  = Buf;
      Buf++;
      Name++;
    } else {
      *Buf++ = (UINT8)*Name++;
    }
  }

  /* Terminate last label */
  *Orig = (UINT8)(Buf - Orig - 1);
  *Buf++ = 0;

  return Buf;
}

/* ========================================================================
 * UdpIo support - helper wrappers
 * ========================================================================*/

/* UdpIoCreateIo -- creates UDP IO protocol for given version */
UDP_IO *
UdpIoCreateIo(
  IN EFI_HANDLE  ControllerHandle,
  IN EFI_HANDLE  DriverHandle,
  IN UINT8       UdpVersion,
  IN UINT32      Flags
  )
{
  /* TODO: actual implementation opens Udp4 or Udp6 protocol
   * on controller and wraps into UDP_IO structure */
  return NULL;
}

VOID
UdpIoDestroyIo(
  IN UDP_IO  *UdpIo
  )
{
  /* TODO: close protocol and free resources */
}

EFI_STATUS
UdpIoSend(
  IN UDP_IO    *UdpIo,
  IN NET_BUF   *Packet,
  IN UINTN     EndPoint,
  IN UINTN     RemoteAddr
  )
{
  /* TODO: transmit NET_BUF via UDP */
  return EFI_SUCCESS;
}

EFI_STATUS
UdpIoPoll(
  IN UDP_IO  *UdpIo
  )
{
  /* TODO: poll underlying UDP for receive */
  return EFI_SUCCESS;
}

VOID
UdpIoRecycle(
  IN UDP_IO  *UdpIo
  )
{
  /* TODO: recycle UDP receive buffer */
}

EFI_STATUS
DnsCreateUdpIo(
  IN DNS_INSTANCE  *Instance
  )
{
  /* Called when DnsSetConfigData needs to create UDP transport */
  return EFI_UNSUPPORTED;
}

/* ========================================================================
 * DnsCancel -- cancel a specific token from the TX list
 * ========================================================================
 */
EFI_STATUS
DnsCancelTokenMap(
  IN  NET_MAP     *Map,
  IN  VOID        *Token
  )
{
  /* Walk the map items and cancel matching token */
  /* TODO: implement */
  return EFI_NOT_FOUND;
}

EFI_STATUS
DnsCleanupTokenMap(
  IN  NET_MAP      *Map,
  IN  VOID         *TokenEntry,
  IN  EFI_STATUS   CompletionStatus
  )
{
  /* Walk the map, signal tokens, remove items */
  /* TODO: implement */
  return EFI_SUCCESS;
}

BOOLEAN
NetMapIsEmpty(
  IN  NET_MAP  *Map
  )
{
  return IsListEmpty(&Map->List);
}

/* ========================================================================
 * Utility function implementations
 * ======================================================================== */

static EFI_STATUS
DxeAllocatePool(
  IN  UINTN   Size,
  OUT VOID    **Buffer
  )
{
  return BootServices->AllocatePool(EfiBootServicesData, Size, Buffer);
}

static VOID *
AllocateZeroPool(
  IN UINTN  Size
  )
{
  VOID  *Buffer;

  if (Size == 0) {
    return NULL;
  }

  if (BootServices->AllocatePool(EfiBootServicesData, Size, &Buffer) !=
      EFI_SUCCESS) {
    return NULL;
  }

  ZeroMem(Buffer, Size);
  return Buffer;
}

static EFI_STATUS
FreePoolAligned(
  IN VOID  *Buffer
  )
{
  return BootServices->FreePool(Buffer);
}

static VOID
ZeroMem(
  IN VOID    *Buffer,
  IN UINTN   Size
  )
{
  UINT8  *Ptr = (UINT8 *)Buffer;
  UINTN  i;

  for (i = 0; i < Size; i++) {
    Ptr[i] = 0;
  }
}

static VOID
CopyMem(
  IN VOID    *Dest,
  IN VOID    *Source,
  IN UINTN   Size
  )
{
  UINT8  *D = (UINT8 *)Dest;
  UINT8  *S = (UINT8 *)Source;
  UINTN  i;

  for (i = 0; i < Size; i++) {
    D[i] = S[i];
  }
}

static BOOLEAN
IsListEmpty(
  IN CONST LIST_ENTRY  *Entry
  )
{
  return (Entry->ForwardLink == Entry);
}

static VOID
RemoveEntryList(
  IN LIST_ENTRY  *Entry
  )
{
  Entry->ForwardLink->BackLink = Entry->BackLink;
  Entry->BackLink->ForwardLink = Entry->ForwardLink;
}

static VOID
InsertHeadList(
  IN LIST_ENTRY  *ListHead,
  IN LIST_ENTRY  *Entry
  )
{
  Entry->ForwardLink = ListHead->ForwardLink;
  Entry->BackLink    = ListHead;
  ListHead->ForwardLink->BackLink = Entry;
  ListHead->ForwardLink = Entry;
}

static VOID
InsertTailList(
  IN LIST_ENTRY  *ListHead,
  IN LIST_ENTRY  *Entry
  )
{
  Entry->ForwardLink = ListHead;
  Entry->BackLink    = ListHead->BackLink;
  ListHead->BackLink->ForwardLink = Entry;
  ListHead->BackLink = Entry;
}

/* SwapBytes16 */
static UINT16
SwapBytes16(
  IN UINT16  Value
  )
{
  return (UINT16)((Value >> 8) | (Value << 8));
}

/* SwapBytes32 */
static UINT32
SwapBytes32(
  IN UINT32  Value
  )
{
  return (Value >> 24) | ((Value >> 8) & 0xFF00) |
         ((Value << 8) & 0xFF0000) | (Value << 24);
}

/* CR macro based container access */
#define CR(Record, Type, Field, Signature)       \
  (VOID *)((UINT8 *)(Record) - OFFSET_OF(Type, Field))

/* ========================================================================
 * Protocol function table definitions
 * ======================================================================== */

EFI_DNS4_PROTOCOL  mDns4ProtocolTemplate = {
  Dns4GetHostByName,          /* 0x00 GetHostByName */
  Dns4GetHostByAddr,          /* 0x08 GetHostByAddr */
  Dns4Poll,                   /* 0x10 Poll */
  Dns4Cancel,                 /* 0x18 Cancel */
  Dns4GetConfigData,          /* 0x20 GetConfigData */
  Dns4SetConfigData,          /* 0x28 SetConfigData */
  Dns4GetServerList,          /* 0x30 GetServerList */
  Dns4GetHostName,            /* 0x38 GetHostName */
  Dns4SetHostName             /* 0x40 SetHostName */
};

EFI_DNS6_PROTOCOL  mDns6ProtocolTemplate = {
  Dns6GetHostByName,          /* 0x00 GetHostByName */
  Dns6GetHostByAddr,          /* 0x08 GetHostByAddr */
  Dns6Poll,                   /* 0x10 Poll */
  Dns6Cancel,                 /* 0x18 Cancel */
  Dns6GetConfigData,          /* 0x20 GetConfigData */
  Dns6SetConfigData,          /* 0x28 SetConfigData */
  Dns6GetServerList,          /* 0x30 GetServerList */
  Dns6GetHostName,            /* 0x38 GetHostName */
  Dns6SetHostName             /* 0x40 SetHostName */
};