windows-nt/Source/XPSP1/NT/net/rras/ras/ppp/rasiphlp/tcpreg.c
2020-09-26 16:20:57 +08:00

2007 lines
43 KiB
C

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