//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
/*++



Module Name:

    apis.c

Abstract:

    Interface manager for DhcpV6 windows APIs.



    FrancisD

Environment:

    User Level: Windows

Revision History:


--*/

#include "dhcpv6p.h"
//#include "precomp.h"
//#include "apis.tmh"


DWORD
IniEnumDhcpV6InterfacesByTemplate(
    PDHCPV6_INTERFACE pTemplateDhcpV6Interface,
    DWORD dwPreferredNumEntries,
    PDHCPV6_INTERFACE * ppDhcpV6Interfaces,
    LPDWORD pdwNumInterfaces,
    LPDWORD pdwTotalNumInterfaces,
    LPDWORD pdwResumeHandle
    )
{
    DWORD dwError = ERROR_NOT_SUPPORTED;

    return (dwError);
}


DWORD
IniEnumDhcpV6Interfaces(
    DWORD dwPreferredNumEntries,
    PDHCPV6_INTERFACE * ppDhcpV6Interfaces,
    LPDWORD pdwNumInterfaces,
    LPDWORD pdwTotalNumInterfaces,
    LPDWORD pdwResumeHandle
    )
{
    DWORD dwError = 0;
    DWORD dwResumeHandle = 0;
    DWORD dwNumToEnum = 0;
    DWORD dwTotalNumInterfaces = 0;
    DWORD i = 0;
    DWORD dwNumInterfaces = 0;
    PDHCPV6_INTERFACE pDhcpV6Interfaces = NULL;
    PDHCPV6_INTERFACE pTempDhcpV6Interface = NULL;
    PLIST_ENTRY pHead = &AdapterList;
    PLIST_ENTRY pEntry = pHead->Flink;


    dwResumeHandle = *pdwResumeHandle;

    if (!dwPreferredNumEntries ||
        (dwPreferredNumEntries > MAX_DHCPV6_INTERFACE_ENUM_COUNT)) {
        dwNumToEnum = MAX_DHCPV6_INTERFACE_ENUM_COUNT;
    }
    else {
        dwNumToEnum = dwPreferredNumEntries;
    }

    AcquireSharedLock(gpAdapterRWLock);
    dwTotalNumInterfaces = 0;
    while(pEntry != pHead) {
        dwTotalNumInterfaces++;
        pEntry = pEntry->Flink;
    }

    if (dwTotalNumInterfaces <= dwResumeHandle) {
        dwError = ERROR_NO_DATA;
        BAIL_ON_LOCK_ERROR(dwError);
    }

    pHead = &AdapterList;
    pEntry = pHead->Flink;
    for (i = 0; i < dwResumeHandle; i++) {
        pEntry = pEntry->Flink;
    }

    dwNumInterfaces = dwTotalNumInterfaces - dwResumeHandle;

    if (dwNumInterfaces > dwNumToEnum) {
        dwNumInterfaces = dwNumToEnum;
    }

    dwError = DHCPv6AllocateBuffer(
                  sizeof(DHCPV6_INTERFACE)*dwNumInterfaces,
                  &pDhcpV6Interfaces
                  );
    BAIL_ON_LOCK_ERROR(dwError);

    pTempDhcpV6Interface = pDhcpV6Interfaces;

    for (i = 0; i < dwNumInterfaces; i++) {
        PDHCPV6_ADAPT pTempDhcpV6Adapt = NULL;

        pTempDhcpV6Adapt = CONTAINING_RECORD(pEntry, DHCPV6_ADAPT, Link);
        pEntry = pEntry->Flink;

        pTempDhcpV6Interface->dwInterfaceID = pTempDhcpV6Adapt->dwIPv6IfIndex;

#ifdef UNDER_CE
        if (! (DHCPv6AllocateBuffer(
            (wcslen(pTempDhcpV6Adapt->wszAdapterName) + 1) * sizeof(WCHAR),
            &pTempDhcpV6Interface->pszDescription))) {
            wcscpy(pTempDhcpV6Interface->pszDescription, 
                pTempDhcpV6Adapt->wszAdapterName);
        }
#endif

        pTempDhcpV6Interface++;
    }
    ReleaseSharedLock(gpAdapterRWLock);


    *ppDhcpV6Interfaces = pDhcpV6Interfaces;
    *pdwResumeHandle = dwResumeHandle + dwNumInterfaces;
    *pdwNumInterfaces = dwNumInterfaces;
    *pdwTotalNumInterfaces = dwTotalNumInterfaces;

cleanup:

    return (dwError);

error:

    if (pDhcpV6Interfaces) {
        DHCPv6FreeBuffer(pDhcpV6Interfaces);
    }

    *ppDhcpV6Interfaces = NULL;
    *pdwResumeHandle = dwResumeHandle;
    *pdwNumInterfaces = 0;
    *pdwTotalNumInterfaces = 0;

    goto cleanup;

lock:
    ReleaseSharedLock(gpAdapterRWLock);

    goto error;
}


DWORD
EnumDhcpV6Interfaces(
    LPWSTR pServerName,
    DWORD dwVersion,
    PDHCPV6_INTERFACE pTemplateDhcpV6Interface,
    DWORD dwPreferredNumEntries,
    PDHCPV6_INTERFACE * ppDhcpV6Interfaces,
    LPDWORD pdwNumInterfaces,
    LPDWORD pdwTotalNumInterfaces,
    LPDWORD pdwResumeHandle,
    LPVOID pvReserved
    )
{
    DWORD dwError = 0;


    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("Begin Enumerating Interfaces"));

    ENTER_DHCPV6_SECTION();
    dwError = ValidateSecurity(
                  DHCPV6_OBJECT_SERVER,
                  SERVER_ACCESS_ADMINISTER,
                  NULL,
                  NULL
                  );
    LEAVE_DHCPV6_SECTION();
    BAIL_ON_WIN32_ERROR(dwError);

    if (pTemplateDhcpV6Interface) {
        dwError = IniEnumDhcpV6InterfacesByTemplate(
                      pTemplateDhcpV6Interface,
                      dwPreferredNumEntries,
                      ppDhcpV6Interfaces,
                      pdwNumInterfaces,
                      pdwTotalNumInterfaces,
                      pdwResumeHandle
                      );
    }
    else {
        dwError = IniEnumDhcpV6Interfaces(
                      dwPreferredNumEntries,
                      ppDhcpV6Interfaces,
                      pdwNumInterfaces,
                      pdwTotalNumInterfaces,
                      pdwResumeHandle
                      );
    }
    BAIL_ON_WIN32_ERROR(dwError);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("End Enumerating Interfaces"));

    return (dwError);

error:

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_ERROR, ("FAILED Enumerating Interfaces with Error: %!status!", dwError));

    return (dwError);
}


DWORD
CreateIniInterfaceHandle(
    PINI_INTERFACE_HANDLE * ppIniInterfaceHandle
    )
{
    DWORD dwError = 0;
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = NULL;


    dwError = AllocateDHCPV6Memory(
                  sizeof(INI_INTERFACE_HANDLE),
                  &pIniInterfaceHandle
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    memset(pIniInterfaceHandle, 0, sizeof(INI_INTERFACE_HANDLE));

    *ppIniInterfaceHandle = pIniInterfaceHandle;
    return (dwError);

error:

    *ppIniInterfaceHandle = NULL;
    return (dwError);
}


DWORD
IniOpenDhcpV6InterfaceHandle(
    PDHCPV6_INTERFACE pDhcpV6Interface,
    PINI_INTERFACE_HANDLE pIniInterfaceHandle
    )
{
    DWORD dwError = 0;


    pIniInterfaceHandle->dwInterfaceID = pDhcpV6Interface->dwInterfaceID;

    dwError = DHCPV6AdaptFindAndReference(
                pIniInterfaceHandle->dwInterfaceID,
                NULL                                    // Find if the adapter exist only
                );
    BAIL_ON_WIN32_ERROR(dwError);

error:

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("Handle for Interface: %d with Error: %!status!", pDhcpV6Interface->dwInterfaceID, dwError));

    return (dwError);
}


VOID
FreeIniInterfaceHandle(
    PINI_INTERFACE_HANDLE pIniInterfaceHandle
    )
{
    if (pIniInterfaceHandle) {
        FreeDHCPV6Memory(pIniInterfaceHandle);
    }
    return;
}


DWORD
OpenDhcpV6InterfaceHandle(
    LPWSTR pServerName,
    DWORD dwVersion,
    PDHCPV6_INTERFACE pDhcpV6Interface,
    LPVOID pvReserved,
    PHANDLE phInterface
    )
{
    DWORD dwError = 0;
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = NULL;


    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("Begin Opening Interfaces"));

    ENTER_DHCPV6_SECTION();
    dwError = ValidateSecurity(
                  DHCPV6_OBJECT_SERVER,
                  SERVER_ACCESS_ADMINISTER,
                  NULL,
                  NULL
                  );
    LEAVE_DHCPV6_SECTION();
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = CreateIniInterfaceHandle(
                  &pIniInterfaceHandle
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    AcquireExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    dwError = IniOpenDhcpV6InterfaceHandle(pDhcpV6Interface, pIniInterfaceHandle);
    BAIL_ON_LOCK_ERROR(dwError);

    pIniInterfaceHandle->pNext = gpIniInterfaceHandle;
    gpIniInterfaceHandle = pIniInterfaceHandle;

    *phInterface = (HANDLE) pIniInterfaceHandle;

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("End Opening Interfaces - Handle: %p", *phInterface));

    return (dwError);

lock:

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

error:

    if (pIniInterfaceHandle) {
        FreeIniInterfaceHandle(pIniInterfaceHandle);
    }

    *phInterface = NULL;

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_ERROR, ("FAILED Opening Interfaces with Error: %!status!", dwError));

    return (dwError);
}


DWORD
GetIniInterfaceHandle(
    HANDLE hInterface,
    PINI_INTERFACE_HANDLE * ppIniInterfaceHandle
    )
{
    DWORD dwError = ERROR_INVALID_HANDLE;
    PINI_INTERFACE_HANDLE * ppTemp = NULL;
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = (PINI_INTERFACE_HANDLE) hInterface;


    *ppIniInterfaceHandle = NULL;

    ppTemp = &gpIniInterfaceHandle;

    while (*ppTemp) {

        if (*ppTemp == pIniInterfaceHandle) {
            break;
        }
        ppTemp = &((*ppTemp)->pNext);
    }

    if (*ppTemp) {
        *ppIniInterfaceHandle = *ppTemp;
        dwError = ERROR_SUCCESS;
    }

    return (dwError);
}


DWORD
IniCloseDhcpV6InterfaceHandle(
    PINI_INTERFACE_HANDLE pIniInterfaceHandle
    )
{
    DWORD dwError = 0;

    return (dwError);
}


VOID
RemoveIniInterfaceHandle(
    PINI_INTERFACE_HANDLE pIniInterfaceHandle
    )
{
    PINI_INTERFACE_HANDLE * ppTemp = NULL;


    ppTemp = &gpIniInterfaceHandle;

    while (*ppTemp) {

        if (*ppTemp == pIniInterfaceHandle) {
            break;
        }
        ppTemp = &((*ppTemp)->pNext);
    }

    if (*ppTemp) {
        *ppTemp = pIniInterfaceHandle->pNext;
    }

    return;
}


VOID
DestroyIniInterfaceHandleList(
    PINI_INTERFACE_HANDLE pIniInterfaceHandleList
    )
{
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = NULL;
    PINI_INTERFACE_HANDLE pTemp = NULL;

    pIniInterfaceHandle = pIniInterfaceHandleList;

    while (pIniInterfaceHandle) {

        pTemp = pIniInterfaceHandle;
        pIniInterfaceHandle = pIniInterfaceHandle->pNext;

        (VOID) IniCloseDhcpV6InterfaceHandle(pTemp);
        FreeIniInterfaceHandle(pTemp);

    }
}


DWORD
CloseDhcpV6InterfaceHandle(
    HANDLE hInterface
    )
{
    DWORD dwError = 0;
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = NULL;


    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("Begin Closing Interfaces: Handle: %p", hInterface));

    ENTER_DHCPV6_SECTION();
    dwError = ValidateSecurity(
                  DHCPV6_OBJECT_SERVER,
                  SERVER_ACCESS_ADMINISTER,
                  NULL,
                  NULL
                  );
    LEAVE_DHCPV6_SECTION();
    BAIL_ON_WIN32_ERROR(dwError);

    AcquireExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    dwError = GetIniInterfaceHandle(hInterface, &pIniInterfaceHandle);
    BAIL_ON_LOCK_ERROR(dwError);

    dwError = IniCloseDhcpV6InterfaceHandle(pIniInterfaceHandle);
    BAIL_ON_LOCK_ERROR(dwError);

    RemoveIniInterfaceHandle(
        pIniInterfaceHandle
        );

    FreeIniInterfaceHandle(pIniInterfaceHandle);

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("End Closing Interfaces"));

    return (dwError);

lock:

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

error:

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_ERROR, ("FAILED Closing Interfaces"));

    return (dwError);
}


DWORD
PerformDhcpV6Refresh(
    HANDLE hInterface,
    DWORD dwVersion,
    LPVOID pvReserved
    )
{
    DWORD dwError = 0;
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = NULL;
    PDHCPV6_ADAPT pDhcpV6Adapt = NULL;


    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("Begin Refreshing Interface: Handle: %p", hInterface));

    ENTER_DHCPV6_SECTION();
    dwError = ValidateSecurity(
                  DHCPV6_OBJECT_SERVER,
                  SERVER_ACCESS_ADMINISTER,
                  NULL,
                  NULL
                  );
    LEAVE_DHCPV6_SECTION();
    BAIL_ON_WIN32_ERROR(dwError);

    AcquireExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    dwError = GetIniInterfaceHandle(hInterface, &pIniInterfaceHandle);
    BAIL_ON_LOCK_ERROR(dwError);

    dwError = DHCPV6AdaptFindAndReference(
                    pIniInterfaceHandle->dwInterfaceID,
                    &pDhcpV6Adapt
                    );
    BAIL_ON_LOCK_ERROR(dwError);

    dwError = DHCPV6MessageMgrPerformRefresh(
                    pDhcpV6Adapt
                    );
    BAIL_ON_LOCK_ERROR(dwError);

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);
    DereferenceDHCPV6Adapt(pDhcpV6Adapt);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("End Refreshing Interface"));

    return (dwError);

lock:

    if (pDhcpV6Adapt) {
        DereferenceDHCPV6Adapt(pDhcpV6Adapt);
    }

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

error:

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_ERROR, ("FAILED Refreshing Interface: Handle: %p with Error: %!status!", hInterface, dwError));

    return (dwError);
}


DWORD
GetDhcpV6DNSList(
    HANDLE hInterface,
    DWORD dwVersion,
    PDHCPV6_DNS_LIST * ppDhcpV6DNSList,
    LPVOID pvReserved
    )
{
    DWORD dwError = 0;
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = NULL;
    PDHCPV6_ADAPT pDhcpV6Adapt = NULL;
    PDHCPV6_DNS_LIST pDhcpV6DNSList = NULL;


    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("Begin Fetching DNS Information: Handle: %p", hInterface));

    ENTER_DHCPV6_SECTION();
    dwError = ValidateSecurity(
                  DHCPV6_OBJECT_SERVER,
                  SERVER_ACCESS_ADMINISTER,
                  NULL,
                  NULL
                  );
    LEAVE_DHCPV6_SECTION();
    BAIL_ON_WIN32_ERROR(dwError);

    AcquireExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    dwError = GetIniInterfaceHandle(hInterface, &pIniInterfaceHandle);
    BAIL_ON_LOCK_ERROR(dwError);

    dwError = DHCPV6AdaptFindAndReference(
                    pIniInterfaceHandle->dwInterfaceID,
                    &pDhcpV6Adapt
                    );
    BAIL_ON_LOCK_ERROR(dwError);

    AcquireSharedLock(&pDhcpV6Adapt->RWLock);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_INFO, ("Fetching DNS Information for Interface: %d", pDhcpV6Adapt->dwIPv6IfIndex));

    if (pDhcpV6Adapt->uNumOfDNSServers == 0) {
        dwError = ERROR_NOT_FOUND;
        BAIL_ON_LOCK_ERROR(dwError);
    }

    dwError = DHCPv6AllocateBuffer(
                  sizeof(DHCPV6_DNS_LIST),
                  &pDhcpV6DNSList
                  );
    BAIL_ON_LOCK_ERROR(dwError);

    dwError = DHCPv6AllocateBuffer(
              sizeof(DHCPV6_DNS) * (pDhcpV6Adapt->uNumOfDNSServers),
              &(pDhcpV6DNSList->pDhcpV6DNS)
              );
    BAIL_ON_LOCK_ERROR(dwError);

    pDhcpV6DNSList->uNumOfEntries = pDhcpV6Adapt->uNumOfDNSServers;
    memcpy(
        pDhcpV6DNSList->pDhcpV6DNS,
        pDhcpV6Adapt->pIpv6DNSServers,
        sizeof(DHCPV6_DNS) * (pDhcpV6Adapt->uNumOfDNSServers)
        );

    *ppDhcpV6DNSList = pDhcpV6DNSList;

    ReleaseSharedLock(&pDhcpV6Adapt->RWLock);
    DereferenceDHCPV6Adapt(pDhcpV6Adapt);

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("End Fetching DNS Information"));

    return (dwError);

lock:

    if (pDhcpV6Adapt) {
        ReleaseSharedLock(&pDhcpV6Adapt->RWLock);
        DereferenceDHCPV6Adapt(pDhcpV6Adapt);
    }

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

error:

    if (pDhcpV6DNSList) {
        if (pDhcpV6DNSList->pDhcpV6DNS) {
            DHCPv6FreeBuffer(pDhcpV6DNSList->pDhcpV6DNS);
        }
        DHCPv6FreeBuffer(pDhcpV6DNSList);
    }

    *ppDhcpV6DNSList = NULL;

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_ERROR, ("FAILED Fetching DNS Information: Handle: %p with Error: %!status!", hInterface, dwError));

    return (dwError);
}


DWORD
GetDhcpV6PDList(
    HANDLE hInterface,
    DWORD dwVersion,
    DHCPV6_PD_OPTION ** ppDhcpV6PDList,
    LPVOID pvReserved
    )
{
    DWORD dwError = 0;
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = NULL;
    PDHCPV6_ADAPT pDhcpV6Adapt = NULL;
    DHCPV6_PD_OPTION *pDhcpV6PDList = NULL;


    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("Begin Fetching DNS Information: Handle: %p", hInterface));

    ENTER_DHCPV6_SECTION();
    dwError = ValidateSecurity(
                  DHCPV6_OBJECT_SERVER,
                  SERVER_ACCESS_ADMINISTER,
                  NULL,
                  NULL
                  );
    LEAVE_DHCPV6_SECTION();
    BAIL_ON_WIN32_ERROR(dwError);

    AcquireExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    dwError = GetIniInterfaceHandle(hInterface, &pIniInterfaceHandle);
    BAIL_ON_LOCK_ERROR(dwError);

    dwError = DHCPV6AdaptFindAndReference(
                    pIniInterfaceHandle->dwInterfaceID,
                    &pDhcpV6Adapt
                    );
    BAIL_ON_LOCK_ERROR(dwError);

    AcquireSharedLock(&pDhcpV6Adapt->RWLock);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_INFO, ("Fetching DNS Information for Interface: %d", pDhcpV6Adapt->dwIPv6IfIndex));

    if (! pDhcpV6Adapt->pPdOption) {
        dwError = ERROR_NOT_FOUND;
        BAIL_ON_LOCK_ERROR(dwError);
    }

    dwError = DHCPv6AllocateBuffer(
              sizeof(DHCPV6_PD_OPTION),
              &pDhcpV6PDList);
    
    BAIL_ON_LOCK_ERROR(dwError);

    memcpy(pDhcpV6PDList, pDhcpV6Adapt->pPdOption, sizeof(DHCPV6_PD_OPTION));

    *ppDhcpV6PDList = pDhcpV6PDList;

    ReleaseSharedLock(&pDhcpV6Adapt->RWLock);
    DereferenceDHCPV6Adapt(pDhcpV6Adapt);

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("End Fetching DNS Information"));

    return (dwError);

lock:

    if (pDhcpV6Adapt) {
        ReleaseSharedLock(&pDhcpV6Adapt->RWLock);
        DereferenceDHCPV6Adapt(pDhcpV6Adapt);
    }

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

error:

    if (pDhcpV6PDList) {
        DHCPv6FreeBuffer(pDhcpV6PDList);
    }

    *ppDhcpV6PDList = NULL;

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_ERROR, ("FAILED Fetching DNS Information: Handle: %p with Error: %!status!", hInterface, dwError));

    return (dwError);
}


DWORD
GetDhcpV6DomainList(
    HANDLE hInterface,
    DWORD dwVersion,
    char ** ppDhcpV6DomainList,
    LPVOID pvReserved
    )
{
    DWORD dwError = 0;
    PINI_INTERFACE_HANDLE pIniInterfaceHandle = NULL;
    PDHCPV6_ADAPT pDhcpV6Adapt = NULL;
    char * pDhcpV6DomainList = NULL;
    DWORD cLen, cAdd;


    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("Begin Fetching DNS Information: Handle: %p", hInterface));

    ENTER_DHCPV6_SECTION();
    dwError = ValidateSecurity(
                  DHCPV6_OBJECT_SERVER,
                  SERVER_ACCESS_ADMINISTER,
                  NULL,
                  NULL
                  );
    LEAVE_DHCPV6_SECTION();
    BAIL_ON_WIN32_ERROR(dwError);

    AcquireExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    dwError = GetIniInterfaceHandle(hInterface, &pIniInterfaceHandle);
    BAIL_ON_LOCK_ERROR(dwError);

    dwError = DHCPV6AdaptFindAndReference(
                    pIniInterfaceHandle->dwInterfaceID,
                    &pDhcpV6Adapt
                    );
    BAIL_ON_LOCK_ERROR(dwError);

    AcquireSharedLock(&pDhcpV6Adapt->RWLock);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_INFO, ("Fetching DNS Information for Interface: %d", pDhcpV6Adapt->dwIPv6IfIndex));

    cLen = pDhcpV6Adapt->cDomainList;
    if ((0 == cLen) || (! pDhcpV6Adapt->pDomainList)) {
        dwError = ERROR_NOT_FOUND;
        BAIL_ON_LOCK_ERROR(dwError);
    }

    if (pDhcpV6Adapt->pDomainList[cLen-1] != '\0')
        cAdd = 2;
    else if ((1 == cLen) || (pDhcpV6Adapt->pDomainList[cLen-2] != '\0'))
        cAdd = 1;
    else
        cAdd = 0;

    dwError = DHCPv6AllocateBuffer(cLen + cAdd, &pDhcpV6DomainList);
    
    BAIL_ON_LOCK_ERROR(dwError);

    memcpy(pDhcpV6DomainList, pDhcpV6Adapt->pDomainList, cLen);

    while (cAdd) {
        pDhcpV6DomainList[cLen + --cAdd] = '\0';
    }

    *ppDhcpV6DomainList = pDhcpV6DomainList;

    ReleaseSharedLock(&pDhcpV6Adapt->RWLock);
    DereferenceDHCPV6Adapt(pDhcpV6Adapt);

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_TRACE, ("End Fetching DNS Information"));

    return (dwError);

lock:

    if (pDhcpV6Adapt) {
        ReleaseSharedLock(&pDhcpV6Adapt->RWLock);
        DereferenceDHCPV6Adapt(pDhcpV6Adapt);
    }

    ReleaseExclusiveLock(gpDhcpV6IniInterfaceTblRWLock);

error:

    if (pDhcpV6DomainList) {
        DHCPv6FreeBuffer(pDhcpV6DomainList);
    }

    *ppDhcpV6DomainList = NULL;

    DhcpV6Trace(DHCPV6_IOCTL, DHCPV6_LOG_LEVEL_ERROR, ("FAILED Fetching DNS Information: Handle: %p with Error: %!status!", hInterface, dwError));

    return (dwError);
}


