/*

Copyright (c) 1998, Microsoft Corporation, all rights reserved

Description:
    The old code is in SaveRegistry and LoadRegistry functions in
    ncpa1.1\tcpip\tcpipcpl.cxx

History:
    Dec 1997: Vijay Baliga created original version.

*/

#include "tcpreg_.h"

/*

Returns:
    Number of bytes in the mwsz including the two terminating NULLs.

Notes:
    
*/

DWORD
MwszLength(
    IN  WCHAR*  mwsz
)
{
    DWORD   dwLength = 2;

    RTASSERT(NULL != mwsz);

    while (mwsz[0] != 0 || mwsz[1] != 0)
    {
        dwLength++;
        mwsz++;
    }

    return(dwLength);
}

/*

Returns:
    VOID

Notes:
    There should be atleast two zeros at the end
    
*/

VOID
ConvertSzToMultiSz(
    IN  CHAR*   sz
)
{
    while (TRUE)
    {
        if (   (0 == sz[0])
            && (0 == sz[1]))
        {
            break;
        }

        if (   (' ' == sz[0])
            || (',' == sz[0]))
        {
            sz[0] = 0;
        }

        sz++;
    }
}

/*

Returns:
    Win32 error code

Notes:
    
*/

DWORD
RegQueryValueWithAllocA(
    IN  HKEY    hKey,
    IN  CHAR*   szValueName,
    IN  DWORD   dwTypeRequired,
    IN  BYTE**  ppbData
)
{
    DWORD   dwType = 0;
    DWORD   dwSize = 0;

    DWORD   dwErr   = ERROR_SUCCESS;

    RTASSERT(NULL != szValueName);
    RTASSERT(NULL != ppbData);

    *ppbData = NULL;

    dwErr = RegQueryValueExA(hKey,
                             szValueName,
                             NULL,
                             &dwType,
                             NULL,
                             &dwSize);

    if (ERROR_SUCCESS != dwErr)
    {
        // TraceHlp("RegQueryValueEx(%s) failed and returned %d.",
        //      szValueName, dwErr);

        goto LDone;
    }

    if (dwTypeRequired != dwType)
    {
        dwErr = E_FAIL;
        TraceHlp("The type of the value %s should be %d, not %d",
              szValueName, dwTypeRequired, dwType);

        goto LDone;
    }

    // For an empty MULTI-SZ, dwSize will be sizeof(CHAR) instead of
    // 2 * sizeof(CHAR). We also want to make sure that no matter what
    // the type, there will be 2 zeros at the end.
    dwSize += 2 * sizeof(CHAR);

    *ppbData = LocalAlloc(LPTR, dwSize);

    if (NULL == *ppbData)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);

        goto LDone;
    }

    dwErr = RegQueryValueExA(hKey,
                             szValueName,
                             NULL,
                             &dwType,
                             *ppbData,
                             &dwSize);

    if (ERROR_SUCCESS != dwErr)
    {
        // TraceHlp("RegQueryValueEx(%s) failed and returned %d.",
        //      szValueName, dwErr);

        goto LDone;
    }

LDone:

    if (NO_ERROR != dwErr)
    {
        LocalFree(*ppbData);
        *ppbData = NULL;
    }

    return(dwErr);
}

/*

Returns:
    Win32 error code

Notes:
    
*/

DWORD
RegQueryValueWithAllocW(
    IN  HKEY    hKey,
    IN  WCHAR*  wszValueName,
    IN  DWORD   dwTypeRequired,
    IN  BYTE**  ppbData
)
{
    DWORD   dwType;
    DWORD   dwSize;

    DWORD   dwErr   = ERROR_SUCCESS;

    RTASSERT(NULL != wszValueName);
    RTASSERT(NULL != ppbData);

    *ppbData = NULL;

    dwErr = RegQueryValueExW(hKey,
                             wszValueName,
                             NULL,
                             &dwType,
                             NULL,
                             &dwSize);

    if (ERROR_SUCCESS != dwErr)
    {
        // TraceHlp("RegQueryValueEx(%ws) failed and returned %d.",
        //      wszValueName, dwErr);

        goto LDone;
    }

    if (dwTypeRequired != dwType)
    {
        dwErr = E_FAIL;
        TraceHlp("The type of the value %ws should be %d, not %d",
              wszValueName, dwTypeRequired, dwType);

        goto LDone;
    }

    // For an empty MULTI-SZ, dwSize will be sizeof(WCHAR) instead of
    // 2 * sizeof(WCHAR). We also want to make sure that no matter what
    // the type, there will be 2 zeros at the end.
    dwSize += sizeof(WCHAR);

    *ppbData = LocalAlloc(LPTR, dwSize);

    if (NULL == *ppbData)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);

        goto LDone;
    }

    dwErr = RegQueryValueExW(hKey,
                             wszValueName,
                             NULL,
                             &dwType,
                             *ppbData,
                             &dwSize);

    if (ERROR_SUCCESS != dwErr)
    {
        // TraceHlp("RegQueryValueEx(%ws) failed and returned %d.",
        //      wszValueName, dwErr);

        goto LDone;
    }

LDone:

    if (NO_ERROR != dwErr)
    {
        LocalFree(*ppbData);
        *ppbData = NULL;
    }

    return(dwErr);
}

/*

Returns:
    IP address

Notes:
    Converts caller's a.b.c.d IP address string to a network byte order IP 
    address. 0 if formatted incorrectly.
    
*/

IPADDR
IpAddressFromAbcdWsz(
    IN  WCHAR*  wszIpAddress
)
{
    CHAR    szIpAddress[MAXIPSTRLEN + 1];
    IPADDR  nboIpAddr;

    if (0 == WideCharToMultiByte(
                CP_UTF8,
                0,
                wszIpAddress,
                -1,
                szIpAddress,
                MAXIPSTRLEN + 1,
                NULL,
                NULL))
    {
        return(0);
    }

    nboIpAddr = inet_addr(szIpAddress);

    if (INADDR_NONE == nboIpAddr)
    {
        nboIpAddr = 0;
    }

    return(nboIpAddr);
}

/*

Returns:
    VOID

Description:
    Converts nboIpAddr to a string in the a.b.c.d form and returns same in 
    caller's szIpAddress buffer. The buffer should be at least 
    MAXIPSTRLEN + 1 characters long.

*/

VOID
AbcdSzFromIpAddress(
    IN  IPADDR  nboIpAddr,
    OUT CHAR*   szIpAddress
)
{
    struct in_addr  in_addr;
    CHAR*           sz;

    in_addr.s_addr = nboIpAddr;
    sz = inet_ntoa(in_addr);

    strcpy(szIpAddress, sz ? sz : "");
}

/*

Returns:
    VOID

Description:
    Converts nboIpAddr to a string in the a.b.c.d form and returns same in 
    caller's wszIpAddress buffer. The buffer should be at least 
    MAXIPSTRLEN + 1 characters long.

*/

VOID
AbcdWszFromIpAddress(
    IN  IPADDR  nboIpAddr,
    OUT WCHAR*  wszIpAddress
)
{
    CHAR    szIpAddress[MAXIPSTRLEN + 1];

    AbcdSzFromIpAddress(nboIpAddr, szIpAddress);

    if (0 == MultiByteToWideChar(
                CP_UTF8,
                0,
                szIpAddress,
                -1,
                wszIpAddress,
                MAXIPSTRLEN + 1))
    {
        wszIpAddress[0] = 0;
    }
}

/*

Returns:
    ERROR_SUCCESS: Success (including not finding a.b.c.d)
    ERROR_NOT_ENOUGH_MEMORY: Failure

Notes:
    Remove the a.b.c.d string wszIpAddress from the space-separated 
    LocalAlloc'ed list *pwsz. *pwsz is LocalFree'ed and a new string 
    LocalAlloc'ed and stored in *pwsz.

*/

DWORD
RemoveWszIpAddress(
    IN  WCHAR** pwsz,
    IN  WCHAR*  wszIpAddress
)
{
    DWORD   cwchIpAddress;
    DWORD   cwchNew;
    WCHAR*  wszFound;
    WCHAR*  wszNew;
    DWORD   nFoundOffset;

    if (NULL == *pwsz)
    {
        return(ERROR_SUCCESS);
    }

    cwchIpAddress = wcslen(wszIpAddress);

    wszFound = wcsstr(*pwsz, wszIpAddress);
    if (!wszFound)
    {
        return(ERROR_SUCCESS);
    }

    if (wszFound[cwchIpAddress] == L' ')
    {
        ++cwchIpAddress;
    }

    cwchNew = wcslen(*pwsz) - cwchIpAddress + 1;
    wszNew = LocalAlloc(LPTR, cwchNew * sizeof(WCHAR));

    if (!wszNew)
    {
        TraceHlp("RemoveWszIpAddress: LocalAlloc returned NULL");
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    nFoundOffset = (ULONG) (wszFound - *pwsz);
    wcsncpy(wszNew, *pwsz, nFoundOffset);
    wcscpy(wszNew + nFoundOffset, *pwsz + nFoundOffset + cwchIpAddress);

    LocalFree(*pwsz);
    *pwsz = wszNew;

    return(ERROR_SUCCESS);
}

/*

Returns:
    Win32 error code

Notes:
    Add the a.b.c.d string wszIpAddress to the front of the space-separated 
    LocalAlloc'ed list *pwsz. *pwsz is LocalFree'ed and a new string 
    LocalAlloc'ed and stored in *pwsz.

*/

DWORD
PrependWszIpAddress(
    IN  WCHAR** pwsz,
    IN  WCHAR*  wszIpAddress
)
{
    DWORD   cwchOld;
    DWORD   cwchNew;
    WCHAR*  wszNew;

    if (0 == IpAddressFromAbcdWsz(wszIpAddress))
    {
        TraceHlp("PrependWszIpAddress: Not prepending %ws", wszIpAddress);
        return(ERROR_SUCCESS);
    }

    cwchOld = *pwsz ? wcslen(*pwsz) : 0;
    cwchNew = cwchOld + wcslen(wszIpAddress) + 6;
    wszNew = LocalAlloc(LPTR, cwchNew * sizeof(WCHAR));

    if (!wszNew)
    {
        TraceHlp("PrependWszIpAddress: LocalAlloc returned NULL");
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    wcscpy(wszNew, wszIpAddress);

    if (cwchOld)
    {
        wcscat(wszNew, L" ");
        wcscat(wszNew, *pwsz);
        LocalFree(*pwsz);
    }

    wcscat(wszNew, L"\0");


    *pwsz = wszNew;
    return(ERROR_SUCCESS);
}

/*

Returns:
    ERROR_SUCCESS: Success (including not finding a.b.c.d)
    ERROR_NOT_ENOUGH_MEMORY: Failure

Notes:
    Remove the a.b.c.d string wszIpAddress from the LocalAlloc'ed MULTI_SZ 
    *pmwsz. *pmwsz is LocalFree'ed and a new string LocalAlloc'ed and stored in 
    *pmwsz.

*/

DWORD
RemoveWszIpAddressFromMwsz(
    IN  WCHAR** pmwsz,
    IN  WCHAR*  wszIpAddress
)
{
    DWORD   cwchIpAddress;
    DWORD   cwchNew;
    WCHAR*  wszFound;
    WCHAR*  mwszNew;
    DWORD   nFoundOffset;

    if (NULL == *pmwsz)
    {
        return(ERROR_SUCCESS);
    }

    cwchIpAddress = wcslen(wszIpAddress);

    for (wszFound = *pmwsz;
         wszFound[0] != 0;
         wszFound += wcslen(wszFound) + 1)
    {
        if (!wcscmp(wszFound, wszIpAddress))
        {
            break;
        }
    }

    if (!wszFound[0])
    {
        return(ERROR_SUCCESS);
    }

    if (wszFound[cwchIpAddress + 1] != 0)
    {
        ++cwchIpAddress;
    }

    cwchNew = MwszLength(*pmwsz) - cwchIpAddress;
    mwszNew = LocalAlloc(LPTR, cwchNew * sizeof(WCHAR));

    if (!mwszNew)
    {
        TraceHlp("RemoveWszIpAddress: LocalAlloc returned NULL");
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    nFoundOffset = (ULONG) (wszFound - *pmwsz);
    CopyMemory(mwszNew, *pmwsz, nFoundOffset * sizeof(WCHAR));
    CopyMemory(mwszNew + nFoundOffset,
               *pmwsz + nFoundOffset + cwchIpAddress,
               (cwchNew - nFoundOffset) * sizeof(WCHAR));

    LocalFree(*pmwsz);
    *pmwsz = mwszNew;

    return(ERROR_SUCCESS);
}

/*

Returns:
    Win32 error code

Notes:
    Add the a.b.c.d string wszIpAddress to the front of the LocalAlloc'ed 
    MULTI_SZ *pmwsz. *pmwsz is LocalFree'ed and a new string LocalAlloc'ed and 
    stored in *pmwsz.

*/

DWORD
PrependWszIpAddressToMwsz(
    IN  WCHAR** pmwsz,
    IN  WCHAR*  wszIpAddress
)
{
    DWORD   cwchIpAddress;
    DWORD   cwchOld;
    DWORD   cwchNew;
    WCHAR*  mwszNew;

    cwchIpAddress = wcslen(wszIpAddress);

    cwchOld = *pmwsz ? MwszLength(*pmwsz) : 0;
    cwchNew = cwchOld + cwchIpAddress + 6;
    mwszNew = LocalAlloc(LPTR, cwchNew * sizeof(WCHAR));

    if (!mwszNew)
    {
        TraceHlp("PrependWszIpAddress: LocalAlloc returned NULL");
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    wcscpy(mwszNew, wszIpAddress);

    if (cwchOld)
    {
        CopyMemory(mwszNew + cwchIpAddress + 1, *pmwsz, cwchOld *sizeof(WCHAR));
        LocalFree(*pmwsz);
    }
    *pmwsz = mwszNew;
    return(ERROR_SUCCESS);
}

/*

Returns:
    Win32 error code

Notes:
    Add the address nboIpAddr to the front of the space-separated 
    LocalAlloc'ed list *pwsz. *pwsz is LocalFree'ed and a new string 
    LocalAlloc'ed and stored in *pwsz.

*/

DWORD
PrependDwIpAddress(
    IN  WCHAR** pwsz,
    IN  IPADDR  nboIpAddr
)
{
    WCHAR  wszIpAddress[MAXIPSTRLEN + 1];
    AbcdWszFromIpAddress(nboIpAddr, wszIpAddress);
    return(PrependWszIpAddress(pwsz, wszIpAddress));
}

/*

Returns:
    Win32 error code

Notes:
    Add the address nboIpAddr to the front of the LocalAlloc'ed MULTI_SZ 
    *pmwsz. *pmwsz is LocalFree'ed and a new string LocalAlloc'ed and stored in 
    *pmwsz.

*/

DWORD
PrependDwIpAddressToMwsz(
    IN  WCHAR** pmwsz,
    IN  IPADDR  nboIpAddr
)
{
    WCHAR  wszIpAddress[MAXIPSTRLEN + 1];
    AbcdWszFromIpAddress(nboIpAddr, wszIpAddress);
    return(PrependWszIpAddressToMwsz(pmwsz, wszIpAddress));
}

/*

Returns:
    BOOL

Description:
    Returns TRUE if there is any non zero value in pTcpipInfo. It also zeroes
    the value.

*/

BOOL
FJunkExists(
    TCPIP_INFO* pTcpipInfo
)
{
    BOOL    fRet    = FALSE;

    if (   0 != pTcpipInfo->wszIPAddress[0]
        && wcscmp(pTcpipInfo->wszIPAddress, WCH_ZEROADDRESS))
    {
        fRet = TRUE;
        pTcpipInfo->fChanged = TRUE;
        pTcpipInfo->wszIPAddress[0] = 0;
    }

    if (   0 != pTcpipInfo->wszSubnetMask[0]
        && wcscmp(pTcpipInfo->wszSubnetMask, WCH_ZEROADDRESS))
    {
        fRet = TRUE;
        pTcpipInfo->fChanged = TRUE;
        pTcpipInfo->wszSubnetMask[0] = 0;
    }

    if (   NULL != pTcpipInfo->wszDNSNameServers
        && 0 != pTcpipInfo->wszDNSNameServers[0])
    {
        fRet = TRUE;
        LocalFree(pTcpipInfo->wszDNSNameServers);
        pTcpipInfo->wszDNSNameServers = NULL;
    }

    if (   NULL != pTcpipInfo->mwszNetBIOSNameServers
        && 0 != pTcpipInfo->mwszNetBIOSNameServers[0]
        && 0 != pTcpipInfo->mwszNetBIOSNameServers[1])
    {
        fRet = TRUE;
        LocalFree(pTcpipInfo->mwszNetBIOSNameServers);
        pTcpipInfo->mwszNetBIOSNameServers = NULL;
    }

    if (   NULL != pTcpipInfo->wszDNSDomainName
        && 0 != pTcpipInfo->wszDNSDomainName[0])
    {
        fRet = TRUE;
        LocalFree(pTcpipInfo->wszDNSDomainName);
        pTcpipInfo->wszDNSDomainName = NULL;
    }

    return(fRet);
}

/*

Returns:
    VOID

Description:
    Clears up the stale information left in the regsitry when we AV or the
    machine BS's.
*/

VOID
ClearTcpipInfo(
    VOID
)
{
    LONG        lRet;
    DWORD       dwErr;
    HKEY        hKeyNdisWanIp   = NULL;
    WCHAR*      mwszAdapters    = NULL;
    WCHAR*      mwszTemp;
    WCHAR*      wszAdapterName;
    TCPIP_INFO* pTcpipInfo      = NULL;
    DWORD       dwPrefixLen     = wcslen(WCH_TCPIP_PARAM_INT_W);
    DWORD       dwStrLen;

    lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REGKEY_TCPIP_NDISWANIP_W, 0, 
                KEY_READ, &hKeyNdisWanIp);

    if (ERROR_SUCCESS != lRet)
    {
        goto LDone;
    }

    dwErr = RegQueryValueWithAllocW(hKeyNdisWanIp, REGVAL_IPCONFIG_W, 
                REG_MULTI_SZ, (BYTE**)&mwszAdapters);

    if (NO_ERROR != dwErr)
    {
        goto LDone;
    }

    mwszTemp = mwszAdapters;

    while (mwszTemp[0] != 0)
    {
        pTcpipInfo = NULL;

        dwStrLen = wcslen(mwszTemp);

        if (dwPrefixLen >= dwStrLen)
        {
            goto LWhileEnd;
        }

        wszAdapterName = mwszTemp + dwPrefixLen;

        RTASSERT('{' == wszAdapterName[0]);

        dwErr = LoadTcpipInfo(&pTcpipInfo, wszAdapterName, FALSE);

        if (NO_ERROR != dwErr)
        {
            goto LWhileEnd;
        }

        if (!FJunkExists(pTcpipInfo))
        {
            goto LWhileEnd;
        }

        dwErr = SaveTcpipInfo(pTcpipInfo);

        if (NO_ERROR != dwErr)
        {
            goto LWhileEnd;
        }

        TraceHlp("Clearing Tcpip info for adapter %ws", wszAdapterName);

        dwErr = PDhcpNotifyConfigChange(NULL, wszAdapterName, TRUE, 0, 
                    0, 0, IgnoreFlag);

LWhileEnd:

        if (NULL != pTcpipInfo)
        {
            FreeTcpipInfo(&pTcpipInfo);
        }

        mwszTemp = mwszTemp + dwStrLen + 1;
    }

LDone:

    if (NULL != hKeyNdisWanIp)
    {
        RegCloseKey(hKeyNdisWanIp);
    }

    LocalFree(mwszAdapters);
}

/*

Returns:
    Win32 error code

Description:
    Frees the TCPIP_INFO buffer.

*/

DWORD
FreeTcpipInfo(
    IN  TCPIP_INFO**    ppTcpipInfo
)
{
    if (NULL == *ppTcpipInfo)
    {
        return(NO_ERROR);
    }
    TraceHlp("Freeing Tcpip info for adapter %ws", (*ppTcpipInfo)->wszAdapterName);
    LocalFree((*ppTcpipInfo)->wszAdapterName);
    LocalFree((*ppTcpipInfo)->mwszNetBIOSNameServers);
    LocalFree((*ppTcpipInfo)->wszDNSDomainName);
    LocalFree((*ppTcpipInfo)->wszDNSNameServers);
    LocalFree(*ppTcpipInfo);

    *ppTcpipInfo = NULL;

    return(NO_ERROR);
}

/*

Returns:
    Win32 error code

Description:
    Reads NETBT information for the adapter pTcpipInfo->wszAdapterName from
    the registry.

*/

DWORD
LoadWinsParam(
    IN  HKEY        hKeyWinsParam,
    IN  TCPIP_INFO* pTcpipInfo
)
{
    HKEY    hKeyInterfaces      = NULL;
    HKEY    hKeyInterfaceParam  = NULL;
    WCHAR*  wszNetBtBindPath    = NULL;

    DWORD   dwStrLenTcpip_;
    DWORD   dwStrLenTcpipBindPath;

    DWORD   dwErr               = ERROR_SUCCESS;

    RTASSERT(NULL != pTcpipInfo);
    RTASSERT(NULL != pTcpipInfo->wszAdapterName);
    RTASSERT(NULL == pTcpipInfo->mwszNetBIOSNameServers);

    dwErr = RegOpenKeyExW(hKeyWinsParam,
                          REGKEY_INTERFACES_W,
                          0,
                          KEY_READ,
                          &hKeyInterfaces);

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
              REGKEY_INTERFACES_W, dwErr);

        goto LDone;
    }

    dwStrLenTcpip_ = wcslen(WCH_TCPIP_);
    dwStrLenTcpipBindPath = wcslen(pTcpipInfo->wszAdapterName);

    wszNetBtBindPath = LocalAlloc(
            LPTR, (dwStrLenTcpip_ + dwStrLenTcpipBindPath + 1) * sizeof(WCHAR));

    if (NULL == wszNetBtBindPath)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);
        goto LDone;
    }

    wcscpy(wszNetBtBindPath, WCH_TCPIP_);
    wcscat(wszNetBtBindPath, pTcpipInfo->wszAdapterName);

    dwErr = RegOpenKeyExW(hKeyInterfaces,
                          wszNetBtBindPath,
                          0,
                          KEY_READ,
                          &hKeyInterfaceParam);

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
              wszNetBtBindPath, dwErr);

        goto LDone;
    }

    // Its OK if we cannot find the value. Ignore the error.
    RegQueryValueWithAllocW(hKeyInterfaceParam,
        REGVAL_NAMESERVERLIST_W,
        REG_MULTI_SZ,
        (BYTE**)&(pTcpipInfo->mwszNetBIOSNameServers));

LDone:

    LocalFree(wszNetBtBindPath);

    if (NULL != hKeyInterfaces)
    {
        RegCloseKey(hKeyInterfaces);
    }

    if (NULL != hKeyInterfaceParam)
    {
        RegCloseKey(hKeyInterfaceParam);
    }

    if (NO_ERROR != dwErr)
    {
        LocalFree(pTcpipInfo->mwszNetBIOSNameServers);
        pTcpipInfo->mwszNetBIOSNameServers = NULL;
    }

    return(dwErr);
}

/*

Returns:
    Win32 error code

Description:
    Reads TCPIP information for the adapter pTcpipInfo->wszAdapterName from
    the registry.

*/

DWORD
LoadTcpipParam(
    IN  HKEY        hKeyTcpipParam,
    IN  TCPIP_INFO* pTcpipInfo
)
{
    HKEY            hKeyInterfaces      = NULL;
    HKEY            hKeyInterfaceParam  = NULL;

    DWORD           dwType;
    DWORD           dwSize;

    DWORD           dwErr               = ERROR_SUCCESS;

    RTASSERT(NULL != pTcpipInfo);
    RTASSERT(NULL != pTcpipInfo->wszAdapterName);
    _wcslwr(pTcpipInfo->wszAdapterName);

    RTASSERT(0 == pTcpipInfo->wszIPAddress[0]);
    RTASSERT(0 == pTcpipInfo->wszSubnetMask[0]);
    RTASSERT(NULL == pTcpipInfo->wszDNSDomainName);
    RTASSERT(NULL == pTcpipInfo->wszDNSNameServers);

    dwErr = RegOpenKeyExW(hKeyTcpipParam,
                          REGKEY_INTERFACES_W,
                          0,
                          KEY_READ,
                          &hKeyInterfaces);

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
              REGKEY_INTERFACES_W, dwErr);
        goto LDone;
    }

    // Open subkey for this adapter under "Interfaces"
    dwErr = RegOpenKeyExW(hKeyInterfaces,
                          pTcpipInfo->wszAdapterName,
                          0,
                          KEY_READ,
                          &hKeyInterfaceParam);

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
              pTcpipInfo->wszAdapterName, dwErr);
        goto LDone;
    }

    dwSize = sizeof(pTcpipInfo->wszIPAddress);

    // Its OK if we cannot find the value. Ignore the error.
    dwErr = RegQueryValueExW(hKeyInterfaceParam,
                                REGVAL_DHCPIPADDRESS_W,
                                NULL,
                                &dwType,
                                (BYTE*)pTcpipInfo->wszIPAddress,
                                &dwSize);

    if (ERROR_SUCCESS != dwErr || REG_SZ != dwType)
    {
        dwErr = ERROR_SUCCESS;
        RTASSERT(0 == pTcpipInfo->wszIPAddress[0]);
        pTcpipInfo->wszIPAddress[0] = 0;
    }

    dwSize = sizeof(pTcpipInfo->wszSubnetMask);

    // Its OK if we cannot find the value. Ignore the error.
    dwErr = RegQueryValueExW(hKeyInterfaceParam,
                                REGVAL_DHCPSUBNETMASK_W,
                                NULL,
                                &dwType,
                                (BYTE*)pTcpipInfo->wszSubnetMask,
                                &dwSize);

    if (ERROR_SUCCESS != dwErr || REG_SZ != dwType)
    {
        dwErr = ERROR_SUCCESS;
        RTASSERT(0 == pTcpipInfo->wszSubnetMask[0]);
        pTcpipInfo->wszSubnetMask[0] = 0;

        // No point in having a valid IP address with an invalid mask.
        pTcpipInfo->wszIPAddress[0] = 0;
    }

    // Its OK if we cannot find the value. Ignore the error.
    RegQueryValueWithAllocW(hKeyInterfaceParam,
        REGVAL_DOMAIN_W,
        REG_SZ,
        (BYTE**)&(pTcpipInfo->wszDNSDomainName));

    // Its OK if we cannot find the value. Ignore the error.
    RegQueryValueWithAllocW(hKeyInterfaceParam,
        REGVAL_NAMESERVER_W,
        REG_SZ,
        (BYTE**)&(pTcpipInfo->wszDNSNameServers));

LDone:

    if (NULL != hKeyInterfaces)
    {
        RegCloseKey(hKeyInterfaces);
    }

    if (NULL != hKeyInterfaceParam)
    {
        RegCloseKey(hKeyInterfaceParam);
    }

    if (ERROR_SUCCESS != dwErr)
    {
        LocalFree(pTcpipInfo->wszDNSDomainName);
        pTcpipInfo->wszDNSDomainName = NULL;

        LocalFree(pTcpipInfo->wszDNSNameServers);
        pTcpipInfo->wszDNSNameServers = NULL;
    }

    return(dwErr);
}

/*

Returns:
    Win32 error code

Description:
    If fAdapterOnly is FALSE, reads NETBT and TCPIP information for the adapter
    wszAdapterName from the registry. *ppTcpipInfo must ultimately be freed by 
    calling FreeTcpipInfo(). 

*/

DWORD
LoadTcpipInfo(
    IN  TCPIP_INFO**    ppTcpipInfo,
    IN  WCHAR*          wszAdapterName,
    IN  BOOL            fAdapterOnly
)
{
    HKEY                hKeyTcpipParam  = NULL;
    HKEY                hKeyWinsParam   = NULL;

    DWORD               dwErr           = ERROR_SUCCESS;

    RTASSERT(NULL != wszAdapterName);

    if (NULL == wszAdapterName)
    {
        dwErr = E_FAIL;
        TraceHlp("wszAdapterName is NULL");
        goto LDone;
    }

    *ppTcpipInfo = NULL;

    *ppTcpipInfo = LocalAlloc(LPTR, sizeof(TCPIP_INFO));

    if (NULL == *ppTcpipInfo)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);
        goto LDone;
    }

    (*ppTcpipInfo)->wszAdapterName = LocalAlloc(
                LPTR, (wcslen(wszAdapterName) + 1) * sizeof(WCHAR));

    if (NULL == (*ppTcpipInfo)->wszAdapterName)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);
        goto LDone;
    }

    wcscpy((*ppTcpipInfo)->wszAdapterName, wszAdapterName);

    if (fAdapterOnly)
    {
        goto LDone;
    }

    dwErr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                          REGKEY_TCPIP_PARAM_W,
                          0,
                          KEY_READ,
                          &hKeyTcpipParam);

    if (ERROR_SUCCESS != dwErr)
    {
        if (ERROR_FILE_NOT_FOUND == dwErr)
        {
            // Mask the error
            dwErr = ERROR_SUCCESS;
        }
        else
        {
            TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
                  REGKEY_TCPIP_PARAM_W, dwErr);
            goto LDone;
        }
    }
    else
    {
        dwErr = LoadTcpipParam(hKeyTcpipParam, *ppTcpipInfo);

        if (ERROR_SUCCESS != dwErr)
        {
            goto LDone;
        }
    }

    // Open NETBT's parameters key

    dwErr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                          REGKEY_NETBT_PARAM_W,
                          0,
                          KEY_READ,
                          &hKeyWinsParam);

    if (ERROR_SUCCESS != dwErr)
    {
        if (ERROR_FILE_NOT_FOUND == dwErr)
        {
            // Mask the error
            dwErr = ERROR_SUCCESS;
        }
        else
        {
            TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
                  REGKEY_NETBT_PARAM_W, dwErr);
            goto LDone;
        }
    }
    else
    {
        dwErr = LoadWinsParam(hKeyWinsParam, *ppTcpipInfo);

        if (ERROR_SUCCESS != dwErr)
        {
            goto LDone;
        }
    }

LDone:

    if (NULL != hKeyTcpipParam)
    {
        RegCloseKey(hKeyTcpipParam);
    }

    if (NULL != hKeyWinsParam)
    {
        RegCloseKey(hKeyWinsParam);
    }

    if (ERROR_SUCCESS != dwErr)
    {
        FreeTcpipInfo(ppTcpipInfo);
    }

    return(dwErr);
}

/*

Returns:
    Win32 error code

Description:
    Saves NETBT information for the adapter pTcpipInfo->wszAdapterName to
    the registry.

*/

DWORD
SaveWinsParam(
    IN  HKEY        hKeyWinsParam,
    IN  TCPIP_INFO* pTcpipInfo
)
{
    HKEY    hKeyInterfaces          = NULL;
    HKEY    hKeyInterfaceParam      = NULL;
    WCHAR*  wszNetBtBindPath        = NULL;

    DWORD   dwStrLenTcpip_;
    DWORD   dwStrLenAdapterName;

    WCHAR*  mwszData                = NULL;
    WCHAR   mwszBlank[2];

    DWORD   dw;
    DWORD   dwErr                   = ERROR_SUCCESS;

    RTASSERT(NULL != pTcpipInfo);
    RTASSERT(NULL != pTcpipInfo->wszAdapterName);

    dwErr = RegOpenKeyExW(hKeyWinsParam,
                          REGKEY_INTERFACES_W,
                          0,
                          KEY_WRITE,
                          &hKeyInterfaces);

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
              REGKEY_INTERFACES_W, dwErr);

        goto LDone;
    }

    dwStrLenTcpip_ = wcslen(WCH_TCPIP_);
    dwStrLenAdapterName = wcslen(pTcpipInfo->wszAdapterName);

    wszNetBtBindPath = LocalAlloc(
            LPTR, (dwStrLenTcpip_ + dwStrLenAdapterName + 1) * sizeof(WCHAR));

    if (NULL == wszNetBtBindPath)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);
        goto LDone;
    }

    wcscpy(wszNetBtBindPath, WCH_TCPIP_);
    wcscat(wszNetBtBindPath, pTcpipInfo->wszAdapterName);

    dwErr = RegOpenKeyExW(hKeyInterfaces,
                          wszNetBtBindPath,
                          0,
                          KEY_WRITE,
                          &hKeyInterfaceParam);

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
              wszNetBtBindPath, dwErr);

        goto LDone;
    }

    if (pTcpipInfo->fDisableNetBIOSoverTcpip)
    {
        dw = REGVAL_DISABLE_NETBT;

        dwErr = RegSetValueExW(hKeyInterfaceParam,
                               REGVAL_NETBIOSOPTIONS_W,
                               0,
                               REG_DWORD,
                               (BYTE*)&dw,
                               sizeof(DWORD));

        if (ERROR_SUCCESS != dwErr)
        {
            TraceHlp("RegSetValueEx(%ws) failed: %d",
                  REGVAL_NETBIOSOPTIONS_W, dwErr);

            dwErr = NO_ERROR;   // Ignore this error
        }
    }
    else
    {
        dwErr = RegDeleteValueW(hKeyInterfaceParam, REGVAL_NETBIOSOPTIONS_W);

        if (ERROR_SUCCESS != dwErr)
        {
            TraceHlp("RegDeleteValue(%ws) failed: %d",
                  REGVAL_NETBIOSOPTIONS_W, dwErr);

            dwErr = NO_ERROR;   // Ignore this error
        }
    }

    if (NULL == pTcpipInfo->mwszNetBIOSNameServers)
    {
        ZeroMemory(mwszBlank, sizeof(mwszBlank));
        mwszData = mwszBlank;
    }
    else
    {
        mwszData = pTcpipInfo->mwszNetBIOSNameServers;
    }

    dwErr = RegSetValueExW(hKeyInterfaceParam,
                           REGVAL_NAMESERVERLIST_W,
                           0,
                           REG_MULTI_SZ,
                           (BYTE*)mwszData,
                           sizeof(WCHAR) * MwszLength(mwszData));

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegSetValueEx(%ws) failed and returned %d",
              REGVAL_NAMESERVERLIST_W, dwErr);

        goto LDone;
    }

LDone:

    LocalFree(wszNetBtBindPath);

    if (NULL != hKeyInterfaceParam)
    {
        RegCloseKey(hKeyInterfaceParam);
    }

    if (NULL != hKeyInterfaces)
    {
        RegCloseKey(hKeyInterfaces);
    }

    return(dwErr);
}

/*

Returns:
    Win32 error code

Description:
    Saves TCPIP information for the adapter pTcpipInfo->wszAdapterName to
    the registry.

*/

DWORD
SaveTcpipParam(
    IN  HKEY        hKeyTcpipParam,
    IN  TCPIP_INFO* pTcpipInfo
)
{
    HKEY            hKeyInterfaces                      = NULL;
    HKEY            hKeyInterfaceParam                  = NULL;
    DWORD           dwLength;
    WCHAR           mwszZeroAddress[MAXIPSTRLEN + 1];

    WCHAR*          wszData                             = NULL;
    WCHAR           wszBlank[2];

    DWORD           dwErr                               = ERROR_SUCCESS;

    RTASSERT(NULL != pTcpipInfo);
    RTASSERT(NULL != pTcpipInfo->wszAdapterName);
    _wcslwr(pTcpipInfo->wszAdapterName);

    dwErr = RegOpenKeyExW(hKeyTcpipParam,
                          REGKEY_INTERFACES_W,
                          0,
                          KEY_WRITE,
                          &hKeyInterfaces);

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
              REGKEY_INTERFACES_W, dwErr);
        goto LDone;
    }

    // Open subkey for this adapter under "Interfaces"
    dwErr = RegOpenKeyExW(hKeyInterfaces,
                          pTcpipInfo->wszAdapterName,
                          0,
                          KEY_WRITE,
                          &hKeyInterfaceParam);

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
              pTcpipInfo->wszAdapterName, dwErr);
        goto LDone;
    }

    // If fChanged is set
    if (pTcpipInfo->fChanged == TRUE)
    {
        if (   0 == pTcpipInfo->wszIPAddress[0]
            || 0 == pTcpipInfo->wszSubnetMask[0])
        {
            RTASSERT(wcslen(WCH_ZEROADDRESS) <= MAXIPSTRLEN);
            wcscpy(pTcpipInfo->wszIPAddress, WCH_ZEROADDRESS);
            wcscpy(pTcpipInfo->wszSubnetMask, WCH_ZEROADDRESS);
        }

        dwErr = RegSetValueExW(hKeyInterfaceParam,
                    REGVAL_DHCPIPADDRESS_W,
                    0,
                    REG_SZ,
                    (BYTE*)pTcpipInfo->wszIPAddress,
                    sizeof(WCHAR) * wcslen(pTcpipInfo->wszIPAddress));

        if (ERROR_SUCCESS != dwErr)
        {
            TraceHlp("RegSetValueEx(%ws) failed and returned %d",
                  REGVAL_DHCPIPADDRESS_W, dwErr);

            goto LDone;
        }

        dwErr = RegSetValueExW(hKeyInterfaceParam,
                    REGVAL_DHCPSUBNETMASK_W,
                    0,
                    REG_SZ,
                    (BYTE*)pTcpipInfo->wszSubnetMask,
                    sizeof(WCHAR) *
                        wcslen(pTcpipInfo->wszSubnetMask));

        if (ERROR_SUCCESS != dwErr)
        {
            TraceHlp("RegSetValueEx(%ws) failed and returned %d",
                  REGVAL_DHCPSUBNETMASK_W, dwErr);

            goto LDone;
        }
    } // if fChanged = TRUE

    ZeroMemory(wszBlank, sizeof(wszBlank));

    if (NULL == pTcpipInfo->wszDNSDomainName)
    {
        wszData = wszBlank;
    }
    else
    {
        wszData = pTcpipInfo->wszDNSDomainName;
    }

    dwErr = RegSetValueExW(hKeyInterfaceParam, 
                           REGVAL_DOMAIN_W,
                           0,
                           REG_SZ,
                           (BYTE*)wszData,
                           sizeof(WCHAR) * (wcslen(wszData) + 1));

    if (ERROR_SUCCESS != dwErr)
    {
        TraceHlp("RegSetValueEx(%ws) failed and returned %d",
              REGVAL_DOMAIN_W, dwErr);

        goto LDone;
    }

    if (NULL == pTcpipInfo->wszDNSNameServers)
    {
        wszData = wszBlank;
    }
    else
    {
        wszData = pTcpipInfo->wszDNSNameServers;
    }

    // Check whether the value starts with a space.
    // If so, delete the key. Otherwise save the value.
    if (WCH_SPACE != wszData[0])
    {
        dwErr = RegSetValueExW(hKeyInterfaceParam, 
                               REGVAL_NAMESERVER_W,
                               0,
                               REG_SZ,
                               (BYTE*)wszData,
                               sizeof(WCHAR) * (wcslen(wszData) + 1));

        if (ERROR_SUCCESS != dwErr)
        {
            TraceHlp("RegSetValueEx(%ws) failed and returned %d",
                  REGVAL_NAMESERVER_W, dwErr);

            goto LDone;
        }
    }
    else
    {
        dwErr = RegDeleteValueW(hKeyInterfaceParam, REGVAL_NAMESERVER_W);

        if (ERROR_SUCCESS != dwErr)
        {
            TraceHlp("RegDeleteValue(%ws) failed and returned %d",
                  REGVAL_NAMESERVER_W, dwErr);

            goto LDone;
        }
    }

LDone:

    if (NULL != hKeyInterfaceParam)
    {
        RegCloseKey(hKeyInterfaceParam);
    }

    if (NULL != hKeyInterfaces)
    {
        RegCloseKey(hKeyInterfaces);
    }

    return(dwErr);
}

/*

Returns:
    Win32 error code

Description:
    Saves NETBT and TCPIP information for the adapter
    pTcpipInfo->wszAdapterName to the registry.

*/

DWORD
SaveTcpipInfo(
    IN  TCPIP_INFO* pTcpipInfo
)
{
    HKEY            hKeyTcpipParam  = NULL;
    HKEY            hKeyWinsParam   = NULL;

    DWORD           dwErr           = ERROR_SUCCESS;

    RTASSERT(NULL != pTcpipInfo);

    if (   (NULL == pTcpipInfo)
        || (NULL == pTcpipInfo->wszAdapterName))
    {
        dwErr = E_FAIL;
        TraceHlp("pTcpipInfo or wszAdapterName is NULL");
        goto LDone;
    }

    dwErr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                          REGKEY_TCPIP_PARAM_W,
                          0,
                          KEY_WRITE,
                          &hKeyTcpipParam);

    if (ERROR_SUCCESS != dwErr)
    {
        if (ERROR_FILE_NOT_FOUND == dwErr)
        {
            // Mask the error
            dwErr = ERROR_SUCCESS;
        }
        else
        {
            TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
                  REGKEY_TCPIP_PARAM_W, dwErr);
            goto LDone;
        }
    }
    else
    {
        dwErr = SaveTcpipParam(hKeyTcpipParam, pTcpipInfo);

        if (ERROR_SUCCESS != dwErr)
        {
            goto LDone;
        }
    }

    // Open NETBT's parameters key

    dwErr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                          REGKEY_NETBT_PARAM_W,
                          0,
                          KEY_WRITE,
                          &hKeyWinsParam);

    if (ERROR_SUCCESS != dwErr)
    {
        if (ERROR_FILE_NOT_FOUND == dwErr)
        {
            // Mask the error
            dwErr = ERROR_SUCCESS;
        }
        else
        {
            TraceHlp("RegOpenKeyEx(%ws) failed and returned %d",
                  REGKEY_NETBT_PARAM_W, dwErr);
            goto LDone;
        }
    }
    else
    {
        dwErr = SaveWinsParam(hKeyWinsParam, pTcpipInfo);

        if (ERROR_SUCCESS != dwErr)
        {
            goto LDone;
        }
    }

LDone:

    if (NULL != hKeyTcpipParam)
    {
        RegCloseKey(hKeyTcpipParam);
    }

    if (NULL != hKeyWinsParam)
    {
        RegCloseKey(hKeyWinsParam);
    }

    return(dwErr);
}

/*

Returns:
    Win32 error code

Notes:

*/

DWORD
GetAdapterInfo(
    IN  DWORD       dwIndex,
    OUT IPADDR*     pnboIpAddress,
    OUT IPADDR*     pnboDNS1,
    OUT IPADDR*     pnboDNS2,
    OUT IPADDR*     pnboWINS1,
    OUT IPADDR*     pnboWINS2,
    OUT IPADDR*     pnboGateway,
    OUT BYTE*       pbAddress
)
{
    IP_ADAPTER_INFO*        pAdapterInfo    = NULL;
    IP_ADAPTER_INFO*        pAdapter;
    IP_PER_ADAPTER_INFO*    pPerAdapterInfo = NULL;
    DWORD                   dwSize;
    DWORD                   dw;
    IPADDR                  nboIpAddress    = 0;
    IPADDR                  nboDNS1         = 0;
    IPADDR                  nboDNS2         = 0;
    IPADDR                  nboWINS1        = 0;
    IPADDR                  nboWINS2        = 0;
    IPADDR                  nboGateway      = 0;
    BYTE                    bAddress[MAX_ADAPTER_ADDRESS_LENGTH];
    DWORD                   dwErr           = NO_ERROR;

    TraceHlp("GetAdapterInfo");

    dwSize = 0;

    dwErr = PGetAdaptersInfo(NULL, &dwSize);

    if (ERROR_BUFFER_OVERFLOW != dwErr && NO_ERROR != dwErr )
    {
        TraceHlp("GetAdaptersInfo failed and returned %d", dwErr);
        goto LDone;
    }

    pAdapterInfo = LocalAlloc(LPTR, dwSize);

    if (NULL == pAdapterInfo)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);
        goto LDone;
    }

    dwErr = PGetAdaptersInfo(pAdapterInfo, &dwSize);

    if (NO_ERROR != dwErr)
    {
        TraceHlp("GetAdaptersInfo failed and returned %d", dwErr);
        goto LDone;
    }

    pAdapter = pAdapterInfo;

    while (pAdapter)
    {
        if (pAdapter->Index != dwIndex)
        {
            pAdapter = pAdapter->Next;
            continue;
        }

        break;
    }

    if (NULL == pAdapter)
    {
        TraceHlp("Couldn't get info for the adapter");
        dwErr = ERROR_NOT_FOUND;
        goto LDone;
    }

    nboIpAddress = inet_addr(pAdapter->IpAddressList.IpAddress.String);

    nboGateway = inet_addr(pAdapter->GatewayList.IpAddress.String);

    if (pAdapter->HaveWins)
    {
        nboWINS1 = inet_addr(pAdapter->PrimaryWinsServer.IpAddress.String);
        nboWINS2 = inet_addr(pAdapter->SecondaryWinsServer.IpAddress.String);
    }

    for (dw = 0;
         dw < pAdapter->AddressLength, dw < MAX_ADAPTER_ADDRESS_LENGTH;
         dw++)
    {
        bAddress[dw] = pAdapter->Address[dw];
    }

    dwSize = 0;

    dwErr = PGetPerAdapterInfo(dwIndex, NULL, &dwSize);

    if (ERROR_BUFFER_OVERFLOW != dwErr)
    {
        TraceHlp("GetPerAdapterInfo failed and returned %d", dwErr);
        goto LDone;
    }

    pPerAdapterInfo = LocalAlloc(LPTR, dwSize);

    if (NULL == pPerAdapterInfo)
    {
        dwErr = GetLastError();
        TraceHlp("LocalAlloc failed and returned %d", dwErr);
        goto LDone;
    }

    dwErr = PGetPerAdapterInfo(dwIndex, pPerAdapterInfo, &dwSize);

    if (NO_ERROR != dwErr)
    {
        TraceHlp("GetPerAdapterInfo failed and returned %d", dwErr);
        goto LDone;
    }

    if (NULL == pPerAdapterInfo)
    {
        TraceHlp("Couldn't get per adapter info for the adapter");
        dwErr = ERROR_NOT_FOUND;
        goto LDone;
    }

    nboDNS1 = inet_addr(pPerAdapterInfo->DnsServerList.IpAddress.String);

    if (NULL != pPerAdapterInfo->DnsServerList.Next)
    {
        nboDNS2 =
            inet_addr(pPerAdapterInfo->DnsServerList.Next->IpAddress.String);
    }

    if (   (0 == nboIpAddress)
        || (INADDR_NONE == nboIpAddress))
    {
        TraceHlp("Couldn't get IpAddress for the adapter");
        dwErr = ERROR_NOT_FOUND;
        goto LDone;
    }

    if(INADDR_NONE == nboGateway)
    {
        nboGateway = 0;
    }

    if (INADDR_NONE == nboDNS1)
    {
        nboDNS1 = 0;
    }

    if (INADDR_NONE == nboDNS2)
    {
        nboDNS2 = 0;
    }

    if (INADDR_NONE == nboWINS1)
    {
        nboWINS1 = 0;
    }

    if (INADDR_NONE == nboWINS2)
    {
        nboWINS2 = 0;
    }

LDone:

    if (NO_ERROR != dwErr)
    {
        nboIpAddress = nboGateway = nboDNS1 = nboDNS2 
                    = nboWINS1 = nboWINS2 = 0;
        ZeroMemory(bAddress, MAX_ADAPTER_ADDRESS_LENGTH);
    }

    if (pnboIpAddress)
    {
        *pnboIpAddress = nboIpAddress;
    }

    if (pnboDNS1)
    {
        *pnboDNS1 = nboDNS1;
    }

    if (pnboDNS2)
    {
        *pnboDNS2 = nboDNS2;
    }

    if (pnboWINS1)
    {
        *pnboWINS1 = nboWINS1;
    }

    if (pnboWINS2)
    {
        *pnboWINS2 = nboWINS2;
    }

    if (pbAddress)
    {
        CopyMemory(pbAddress, bAddress, MAX_ADAPTER_ADDRESS_LENGTH);
    }

    if(pnboGateway)
    {
        *pnboGateway = nboGateway;
    }

    LocalFree(pAdapterInfo);
    LocalFree(pPerAdapterInfo);

    return(dwErr);
}

/*

Returns:
    Win32 error code

Description:
    Don't cache these values because DHCP leases may have expired, etc

*/

DWORD
GetPreferredAdapterInfo(
    OUT IPADDR*     pnboIpAddress,
    OUT IPADDR*     pnboDNS1,
    OUT IPADDR*     pnboDNS2,
    OUT IPADDR*     pnboWINS1,
    OUT IPADDR*     pnboWINS2,
    OUT BYTE*       pbAddress
)
{
    HANDLE                  hHeap           = NULL;
    IP_INTERFACE_NAME_INFO* pTable          = NULL;
    DWORD                   dw;
    DWORD                   dwCount;
    DWORD                   dwIndex         = (DWORD)-1;
    DWORD                   dwErr           = NO_ERROR;

    TraceHlp("GetPreferredAdapterInfo");

    hHeap = GetProcessHeap();

    if (NULL == hHeap)
    {
        dwErr = GetLastError();
        TraceHlp("GetProcessHeap failed and returned %d", dwErr);
        goto LDone;
    }

    dwErr = PNhpAllocateAndGetInterfaceInfoFromStack(&pTable, &dwCount,
                FALSE /* bOrder */, hHeap, LPTR);

    if (NO_ERROR != dwErr)
    {
        TraceHlp("NhpAllocateAndGetInterfaceInfoFromStack failed and "
            "returned %d", dwErr);
        goto LDone;
    }

    for (dw = 0; dw < dwCount; dw++)
    {
        // Only interested in NICs

        if (IF_CONNECTION_DEDICATED != pTable[dw].ConnectionType)
        {
            continue;
        }

        // If the admin wants to use a particular NIC

        if (   HelperRegVal.fNICChosen
            && (!IsEqualGUID(&(HelperRegVal.guidChosenNIC),
                             &(pTable[dw].DeviceGuid))))
        {
            continue;
        }

        dwIndex = pTable[dw].Index;
        break;
    }

    if ((DWORD)-1 == dwIndex)
    {
        if (HelperRegVal.fNICChosen)
        {
            // The chosen NIC is not available. Let us choose another one.

            for (dw = 0; dw < dwCount; dw++)
            {
                // Only interested in NICs

                if (IF_CONNECTION_DEDICATED != pTable[dw].ConnectionType)
                {
                    continue;
                }

                dwIndex = pTable[dw].Index;
                break;
            }
        }

        if ((DWORD)-1 == dwIndex)
        {
            TraceHlp("Couldn't find an appropriate NIC");
            dwErr = ERROR_NOT_FOUND;
            goto LDone;
        }

        HelperRegVal.fNICChosen = FALSE;

        TraceHlp("The network adapter chosen by the administrator is not "
            "available. Some other adapter will be used.");
    }

    dwErr = GetAdapterInfo(
                dwIndex,
                pnboIpAddress,
                pnboDNS1, pnboDNS2,
                pnboWINS1, pnboWINS2,
                NULL,
                pbAddress);

LDone:

    if (NULL != pTable)
    {
        HeapFree(hHeap, 0, pTable);
    }

    return(dwErr);
}