/* Editor Settings: expandtabs and use 4 spaces for indentation
* ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
* -*- mode: c, c-basic-offset: 4 -*- */
/*
* Copyright Likewise Software 2004-2008
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. You should have received a copy of the GNU General
* Public License along with this program. If not, see
* .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Copyright (C) Likewise Software. All rights reserved.
*
* Module Name:
*
* lwnet-cachedb.c
*
* Abstract:
*
* Caching for Likewise Netlogon
*
* Authors: Kyle Stemen (kstemen@likewisesoftware.com)
* Brian Dunstan (bdunstan@likewisesoftware.com)
*
*/
#include "includes.h"
#define ENABLE_CACHEDB_DEBUG 0
#define FILEDB_FORMAT_TYPE "LFLT"
#define FILEDB_FORMAT_VERSION 2
struct _LWNET_CACHE_DB_HANDLE_DATA {
PDLINKEDLIST pCacheList;
// This RW lock helps us to ensure that we don't stomp
// ourselves while giving up good parallel access.
// Note, however, that SQLite might still return busy errors
// if some other process is trying to poke at the database
// (which might happen with database debugging or maintenance tools).
pthread_rwlock_t Lock;
pthread_rwlock_t* pLock;
};
static LWNET_CACHE_DB_HANDLE gDbHandle;
#define LWNET_NETLOGON_REGISTRY_KEY "Services\\netlogon"
#define LWNET_CACHE_REGISTRY_KEY "cachedb"
LWMsgTypeSpec gLWNetCacheEntrySpec[] =
{
LWMSG_STRUCT_BEGIN(LWNET_CACHE_DB_ENTRY),
LWMSG_MEMBER_PSTR(LWNET_CACHE_DB_ENTRY, pszDnsDomainName),
LWMSG_MEMBER_PSTR(LWNET_CACHE_DB_ENTRY, pszSiteName),
LWMSG_MEMBER_UINT32(LWNET_CACHE_DB_ENTRY, QueryType),
LWMSG_MEMBER_INT64(LWNET_CACHE_DB_ENTRY, LastDiscovered),
LWMSG_MEMBER_INT64(LWNET_CACHE_DB_ENTRY, LastPinged),
LWMSG_MEMBER_UINT8(LWNET_CACHE_DB_ENTRY, IsBackoffToWritableDc),
LWMSG_MEMBER_INT64(LWNET_CACHE_DB_ENTRY, LastBackoffToWritableDc),
LWMSG_MEMBER_STRUCT_BEGIN(LWNET_CACHE_DB_ENTRY, DcInfo),
LWMSG_MEMBER_UINT32(LWNET_DC_INFO, dwPingTime),
LWMSG_MEMBER_UINT32(LWNET_DC_INFO, dwDomainControllerAddressType),
LWMSG_MEMBER_UINT32(LWNET_DC_INFO, dwFlags),
LWMSG_MEMBER_UINT16(LWNET_DC_INFO, wLMToken),
LWMSG_MEMBER_UINT16(LWNET_DC_INFO, wNTToken),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszDomainControllerName),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszDomainControllerAddress),
LWMSG_MEMBER_ARRAY_BEGIN(LWNET_DC_INFO, pucDomainGUID),
LWMSG_UINT8(char),
LWMSG_ARRAY_END,
LWMSG_ATTR_LENGTH_STATIC(LWNET_GUID_SIZE),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszNetBIOSDomainName),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszFullyQualifiedDomainName),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszDnsForestName),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszDCSiteName),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszClientSiteName),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszNetBIOSHostName),
LWMSG_MEMBER_PSTR(LWNET_DC_INFO, pszUserName),
LWMSG_STRUCT_END,
LWMSG_STRUCT_END,
LWMSG_TYPE_END
};
// ISSUE-2008/07/01-dalmeida -- For now, use exlusive locking as we need to
// verify actual thread safety wrt things like error strings and such.
#define RW_LOCK_ACQUIRE_READ(Lock) \
pthread_rwlock_wrlock(Lock)
#define RW_LOCK_RELEASE_READ(Lock) \
pthread_rwlock_unlock(Lock)
#define RW_LOCK_ACQUIRE_WRITE(Lock) \
pthread_rwlock_wrlock(Lock)
#define RW_LOCK_RELEASE_WRITE(Lock) \
pthread_rwlock_unlock(Lock)
#if ENABLE_CACHEDB_DEBUG
static
VOID
DebugEntry(
IN PLWNET_CACHE_DB_ENTRY pEntry
);
#define DEBUG_ENTRY(pEntry) DebugEntry(pEntry)
#else
#define DEBUG_ENTRY(pEntry)
#endif
static
DWORD
LWNetCacheDbReadFromRegistry(
LWNET_CACHE_DB_HANDLE dbHandle
);
static
DWORD
LWNetCacheDbWriteToRegistry(
PDLINKEDLIST pCacheList
);
static
VOID
LWNetCacheDbForEachEntryDestroy(
IN PVOID pData,
IN PVOID pContext
);
static
VOID
LWNetCacheDbEntryFree(
IN OUT PLWNET_CACHE_DB_ENTRY pEntry
);
static
DWORD
LWNetCacheDbUpdate(
IN LWNET_CACHE_DB_HANDLE DbHandle,
IN PCSTR pszDnsDomainName,
IN OPTIONAL PCSTR pszSiteName,
IN DWORD dwDsFlags,
IN LWNET_UNIX_TIME_T LastDiscovered,
IN LWNET_UNIX_TIME_T LastPinged,
IN BOOLEAN IsBackoffToWritableDc,
IN OPTIONAL LWNET_UNIX_TIME_T LastBackoffToWritableDc,
IN PLWNET_DC_INFO pDcInfo
);
#if ENABLE_CACHEDB_DEBUG
static
VOID
DebugEntry(
IN PLWNET_CACHE_DB_ENTRY pEntry
)
{
struct tm ldTimeFields = { 0 };
struct tm lpTimeFields = { 0 };
struct tm lwTimeFields = { 0 };
time_t ld = (time_t) pEntry->LastDiscovered;
time_t lp = (time_t) pEntry->LastPinged;
time_t lw = (time_t) pEntry->LastBackoffToWritableDc;
localtime_r(&ld, &ldTimeFields);
localtime_r(&lp, &lpTimeFields);
localtime_r(&lw, &lwTimeFields);
LWNET_LOG_DEBUG("dns=%s, site=%s, type=%u, "
"ld=%04d%02d%02d-%02d:%02d:%02d, "
"lp=%04d%02d%02d-%02d:%02d:%02d, "
"iw=%c, "
"lw=%04d%02d%02d-%02d:%02d:%02d",
LWNET_SAFE_LOG_STRING(pEntry->pszDnsDomainName),
LWNET_SAFE_LOG_STRING(pEntry->pszSiteName),
pEntry->QueryType,
ldTimeFields.tm_year,
ldTimeFields.tm_mon + 1,
ldTimeFields.tm_mday,
ldTimeFields.tm_hour,
ldTimeFields.tm_min,
ldTimeFields.tm_sec,
lpTimeFields.tm_year,
lpTimeFields.tm_mon + 1,
lpTimeFields.tm_mday,
lpTimeFields.tm_hour,
lpTimeFields.tm_min,
lpTimeFields.tm_sec,
pEntry->IsBackoffToWritableDc ? 'Y' : 'N',
lwTimeFields.tm_year,
lwTimeFields.tm_mon + 1,
lwTimeFields.tm_mday,
lwTimeFields.tm_hour,
lwTimeFields.tm_min,
lwTimeFields.tm_sec);
LWNET_LOG_DEBUG("hnb=%s, hdns=%s, haddr=%s (%u), flags=0x%08x, "
"dcsite=%s, clisite=%s, ddns=%s, dnb=%s, forest=%s, user=%s, "
"ver=%u, lm=%u, nt=%u, ping=%u, "
"guid=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszNetBIOSHostName),
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszDomainControllerName),
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszDomainControllerAddress),
pEntry->DcInfo.dwDomainControllerAddressType,
pEntry->DcInfo.dwFlags,
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszDCSiteName),
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszClientSiteName),
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszFullyQualifiedDomainName),
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszNetBIOSDomainName),
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszDnsForestName),
LWNET_SAFE_LOG_STRING(pEntry->DcInfo.pszUserName),
pEntry->DcInfo.dwVersion,
pEntry->DcInfo.wLMToken,
pEntry->DcInfo.wNTToken,
pEntry->DcInfo.dwPingTime,
pEntry->DcInfo.pucDomainGUID[0],
pEntry->DcInfo.pucDomainGUID[1],
pEntry->DcInfo.pucDomainGUID[2],
pEntry->DcInfo.pucDomainGUID[3],
pEntry->DcInfo.pucDomainGUID[4],
pEntry->DcInfo.pucDomainGUID[5],
pEntry->DcInfo.pucDomainGUID[6],
pEntry->DcInfo.pucDomainGUID[7],
pEntry->DcInfo.pucDomainGUID[8],
pEntry->DcInfo.pucDomainGUID[9],
pEntry->DcInfo.pucDomainGUID[10],
pEntry->DcInfo.pucDomainGUID[11],
pEntry->DcInfo.pucDomainGUID[12],
pEntry->DcInfo.pucDomainGUID[13],
pEntry->DcInfo.pucDomainGUID[14],
pEntry->DcInfo.pucDomainGUID[15]);
}
#endif
DWORD
LWNetCacheDbOpen(
IN PCSTR Path,
IN BOOLEAN bIsWrite,
OUT PLWNET_CACHE_DB_HANDLE pDbHandle
)
{
DWORD dwError = 0;
LWNET_CACHE_DB_HANDLE dbHandle = NULL;
dwError = LWNetAllocateMemory(sizeof(*dbHandle), (PVOID *)&dbHandle);
BAIL_ON_LWNET_ERROR(dwError);
// TODO-dalmeida-2008/06/30 -- Convert error code
dwError = pthread_rwlock_init(&dbHandle->Lock, NULL);
BAIL_ON_LWNET_ERROR(dwError);
dbHandle->pLock = &dbHandle->Lock;
dwError = LWNetCacheDbReadFromRegistry(dbHandle);
BAIL_ON_LWNET_ERROR(dwError);
error:
if (dwError)
{
LWNetCacheDbClose(&dbHandle);
}
*pDbHandle = dbHandle;
return dwError;
}
VOID
LWNetCacheDbClose(
IN OUT PLWNET_CACHE_DB_HANDLE pDbHandle
)
{
LWNET_CACHE_DB_HANDLE dbHandle = *pDbHandle;
if (dbHandle)
{
if (dbHandle->pCacheList)
{
LWNetCacheDbWriteToRegistry(dbHandle->pCacheList);
LWNetDLinkedListForEach(
dbHandle->pCacheList,
LWNetCacheDbForEachEntryDestroy,
NULL);
LWNetDLinkedListFree(dbHandle->pCacheList);
}
if (dbHandle->pLock)
{
pthread_rwlock_destroy(dbHandle->pLock);
}
LWNET_SAFE_FREE_MEMORY(dbHandle);
*pDbHandle = NULL;
}
}
static
VOID
LWNetCacheDbForEachEntryDestroy(
IN PVOID pData,
IN PVOID pContext
)
{
PLWNET_CACHE_DB_ENTRY pEntry = (PLWNET_CACHE_DB_ENTRY)pData;
LWNetCacheDbEntryFree(pEntry);
}
static
VOID
LWNetCacheDbEntryFree(
IN OUT PLWNET_CACHE_DB_ENTRY pEntry
)
{
if (pEntry)
{
LWNET_SAFE_FREE_STRING(pEntry->pszDnsDomainName);
LWNET_SAFE_FREE_STRING(pEntry->pszSiteName);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszDomainControllerName);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszDomainControllerAddress);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszNetBIOSDomainName);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszFullyQualifiedDomainName);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszDnsForestName);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszDCSiteName);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszClientSiteName);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszNetBIOSHostName);
LWNET_SAFE_FREE_STRING(pEntry->DcInfo.pszUserName);
LWNET_SAFE_FREE_MEMORY(pEntry);
}
}
DWORD
LWNetCacheDbRegistryReadString(
HANDLE hReg,
HKEY hKey,
PSTR pszValueName,
PSTR *ppszRetString)
{
DWORD dwError = 0;
DWORD dwValueLen = 0;
PSTR pszRetString = NULL;
dwError = RegGetValueA(
hReg,
hKey,
NULL,
pszValueName,
REG_SZ,
NULL,
NULL,
&dwValueLen);
if (dwError == 0 && dwValueLen > 0)
{
dwError = LwAllocateMemory(
(dwValueLen+1) * sizeof(CHAR),
(PVOID) &pszRetString);
BAIL_ON_LWNET_ERROR(dwError);
dwError = RegGetValueA(
hReg,
hKey,
NULL,
pszValueName,
REG_SZ,
NULL,
pszRetString,
&dwValueLen);
}
*ppszRetString = pszRetString;
cleanup:
return dwError;
error:
goto cleanup;
}
DWORD
LWNetCacheDbRegistryReadValue(
HANDLE hReg,
HKEY hKey,
PSTR pszValueName,
DWORD regType,
PVOID *ppValue,
DWORD dwValueLen)
{
DWORD dwError = 0;
DWORD dwValue = 0;
DWORD dwRetValueLen = 0;
PWORD pwValue = (PWORD) ppValue;
PDWORD pdwValue = (PDWORD) ppValue;
PBYTE *ppData = (PBYTE *) ppValue;
switch (regType)
{
case REG_SZ:
dwError = LWNetCacheDbRegistryReadString(
hReg,
hKey,
pszValueName,
(PSTR *) ppValue);
BAIL_ON_LWNET_ERROR(dwError);
break;
case REG_DWORD:
dwRetValueLen = sizeof(DWORD);
dwError = RegGetValueA(
hReg,
hKey,
NULL,
pszValueName,
REG_DWORD,
NULL,
&dwValue,
&dwRetValueLen);
BAIL_ON_LWNET_ERROR(dwError);
if (dwValueLen == sizeof(WORD))
{
*pwValue = dwValue;
}
else
{
*pdwValue = dwValue;
}
break;
case REG_BINARY:
dwRetValueLen = dwValueLen;
dwError = RegGetValueA(
hReg,
hKey,
NULL,
pszValueName,
REG_BINARY,
NULL,
ppData,
&dwRetValueLen);
BAIL_ON_LWNET_ERROR(dwError);
break;
}
cleanup:
return dwError;
error:
goto cleanup;
}
DWORD
LWNetCacheDbRegistryInitValueArray(
PLWNET_CACHE_DB_ENTRY pEntry,
PLWNET_CACHE_REGISTRY_VALUE *ppValueArray,
PDWORD pdwArrayEntries)
{
DWORD dwError = 0;
PLWNET_CACHE_REGISTRY_VALUE pValueArray = NULL;
LWNET_CACHE_REGISTRY_VALUE valueArray[] =
{
{
"DnsDomainName",
REG_SZ,
&pEntry->pszDnsDomainName,
0
},
{
"SiteName",
REG_SZ,
&pEntry->pszSiteName,
0
},
{
"QueryType",
REG_DWORD,
&pEntry->QueryType,
0
},
{
"LastDiscovered",
REG_BINARY,
&pEntry->LastDiscovered,
sizeof(pEntry->LastDiscovered)
},
{
"LastPinged",
REG_BINARY,
&pEntry->LastPinged,
sizeof(pEntry->LastPinged)
},
{
"IsBackoffToWritableDc",
REG_DWORD,
&pEntry->IsBackoffToWritableDc,
0
},
{
"DcInfo-PingTime",
REG_DWORD,
&pEntry->DcInfo.dwPingTime,
0
},
{
"DcInfo-DomainControllerAddressType",
REG_DWORD,
&pEntry->DcInfo.dwDomainControllerAddressType,
0
},
{
"DcInfo-Flags",
REG_DWORD,
&pEntry->DcInfo.dwFlags,
0
},
{
"DcInfo-Version",
REG_DWORD,
&pEntry->DcInfo.dwVersion,
0
},
{
"DcInfo-LMToken",
REG_DWORD,
&pEntry->DcInfo.wLMToken,
sizeof(pEntry->DcInfo.wLMToken)
},
{
"DcInfo-NTToken",
REG_DWORD,
&pEntry->DcInfo.wNTToken,
sizeof(pEntry->DcInfo.wNTToken)
},
{
"DcInfo-DomainControllerName",
REG_SZ,
&pEntry->DcInfo.pszDomainControllerName,
0
},
{
"DcInfo-DomainControllerAddress",
REG_SZ,
&pEntry->DcInfo.pszDomainControllerAddress,
0
},
{
"DcInfo-DomainGUID",
REG_BINARY,
&pEntry->DcInfo.pucDomainGUID,
LWNET_GUID_SIZE
},
{
"DcInfo-NetBIOSDomainName",
REG_SZ,
&pEntry->DcInfo.pszNetBIOSDomainName,
0
},
{
"DcInfo-FullyQualifiedDomainName",
REG_SZ,
&pEntry->DcInfo.pszFullyQualifiedDomainName,
0
},
{
"DcInfo-DnsForestName",
REG_SZ,
&pEntry->DcInfo.pszDnsForestName,
0
},
{
"DcInfo-DCSiteName",
REG_SZ,
&pEntry->DcInfo.pszDCSiteName,
0
},
{
"DcInfo-ClientSiteName",
REG_SZ,
&pEntry->DcInfo.pszClientSiteName,
0
},
{
"DcInfo-NetBIOSHostName",
REG_SZ,
&pEntry->DcInfo.pszNetBIOSHostName,
0
},
{
"DcInfo-UserName",
REG_SZ,
&pEntry->DcInfo.pszUserName,
0
},
};
dwError = LwAllocateMemory(sizeof(valueArray), (PVOID) &pValueArray);
BAIL_ON_LWNET_ERROR(dwError);
memcpy(pValueArray, valueArray, sizeof(valueArray));
*ppValueArray = pValueArray;
*pdwArrayEntries = sizeof(valueArray)/sizeof(LWNET_CACHE_REGISTRY_VALUE);
cleanup:
return dwError;
error:
goto cleanup;
}
DWORD
LWNetCacheDbRegistryReadValues(
HANDLE hReg,
HKEY pKey,
PLWNET_CACHE_DB_ENTRY pEntry)
{
DWORD dwError = 0;
DWORD index = 0;
DWORD dwArrayEntries = 0;
PLWNET_CACHE_REGISTRY_VALUE valueArray = NULL;
dwError = LWNetCacheDbRegistryInitValueArray(
pEntry,
&valueArray,
&dwArrayEntries);
BAIL_ON_LWNET_ERROR(dwError);
for (index=0; indexpItem;
dwError = LwAllocateStringPrintf(
&pszNewCacheKey,
"%s%s%s-%d",
pCacheDbEntry->pszDnsDomainName ?
pCacheDbEntry->pszDnsDomainName : "",
pCacheDbEntry->pszSiteName ? "-" : "",
pCacheDbEntry->pszSiteName ?
pCacheDbEntry->pszSiteName : "",
(int) pCacheDbEntry->QueryType);
BAIL_ON_LWNET_ERROR(dwError);
dwError = RegUtilAddKey(
hReg,
HKEY_THIS_MACHINE,
LWNET_NETLOGON_REGISTRY_KEY "\\" LWNET_CACHE_REGISTRY_KEY,
pszNewCacheKey);
BAIL_ON_LWNET_ERROR(dwError);
/* Add to registry the current entries */
dwError = LWNetCacheDbRegistryWriteValues(
hReg,
pszNewCacheKey,
pCacheDbEntry);
BAIL_ON_LWNET_ERROR(dwError);
LWNET_SAFE_FREE_MEMORY(pszNewCacheKey);
pCacheList = pCacheList->pNext;
}
cleanup:
if (pRootKey)
{
RegCloseKey(hReg, pRootKey);
}
RegCloseServer(hReg);
LWNET_SAFE_FREE_MEMORY(pData);
LWNET_SAFE_FREE_MEMORY(pszNewCacheKey);
return dwError;
error:
LWNET_LOG_ERROR("Failed to save cache %s [%d]",
HKEY_THIS_MACHINE "\\" LWNET_CACHE_REGISTRY_KEY,
dwError);
goto cleanup;
}
static
LWNET_CACHE_DB_QUERY_TYPE
LWNetCacheDbQueryToQueryType(
IN DWORD dwDsFlags
)
{
LWNET_CACHE_DB_QUERY_TYPE result = LWNET_CACHE_DB_QUERY_TYPE_DC;
if (dwDsFlags & DS_PDC_REQUIRED)
{
result = LWNET_CACHE_DB_QUERY_TYPE_PDC;
}
if (dwDsFlags & DS_GC_SERVER_REQUIRED)
{
result = LWNET_CACHE_DB_QUERY_TYPE_GC;
}
return result;
}
DWORD
LWNetCacheDbQuery(
IN LWNET_CACHE_DB_HANDLE DbHandle,
IN PCSTR pszDnsDomainName,
IN OPTIONAL PCSTR pszSiteName,
IN DWORD dwDsFlags,
OUT PLWNET_DC_INFO* ppDcInfo,
OUT PLWNET_UNIX_TIME_T LastDiscovered,
OUT PLWNET_UNIX_TIME_T LastPinged,
OUT PBOOLEAN IsBackoffToWritableDc,
OUT PLWNET_UNIX_TIME_T LastBackoffToWritableDc
)
{
DWORD dwError = 0;
LWNET_CACHE_DB_QUERY_TYPE queryType = 0;
BOOLEAN isAcquired = FALSE;
PLWNET_DC_INFO pDcInfo = NULL;
LWNET_UNIX_TIME_T lastDiscovered = 0;
LWNET_UNIX_TIME_T lastPinged = 0;
BOOLEAN isBackoffToWritableDc = FALSE;
LWNET_UNIX_TIME_T lastBackoffToWritableDc = 0;
PSTR pszDnsDomainNameLower = NULL;
PSTR pszSiteNameLower = NULL;
PLWNET_CACHE_DB_ENTRY pEntry = NULL;
PDLINKEDLIST pListEntry = NULL;
if (pszDnsDomainName)
{
dwError = LWNetAllocateString(
pszDnsDomainName,
&pszDnsDomainNameLower);
BAIL_ON_LWNET_ERROR(dwError);
}
else
{
LWNET_LOG_DEBUG("Cached entry not found: %s, %s, %u",
"",
pszSiteNameLower ? pszSiteNameLower : "",
queryType);
goto error;
}
LwStrToLower(pszDnsDomainNameLower);
if (pszSiteName)
{
dwError = LWNetAllocateString(
pszSiteName,
&pszSiteNameLower);
BAIL_ON_LWNET_ERROR(dwError);
LwStrToLower(pszSiteNameLower);
}
queryType = LWNetCacheDbQueryToQueryType(dwDsFlags);
RW_LOCK_ACQUIRE_READ(DbHandle->pLock);
isAcquired = TRUE;
for (pListEntry = DbHandle->pCacheList ;
pListEntry ;
pListEntry = pListEntry->pNext)
{
pEntry = (PLWNET_CACHE_DB_ENTRY)pListEntry->pItem;
if ( pEntry->QueryType == queryType &&
!strcmp(pEntry->pszDnsDomainName, pszDnsDomainNameLower))
{
if (!pEntry->pszSiteName && !pszSiteNameLower)
{
break;
}
if (pEntry->pszSiteName && pszSiteNameLower &&
!strcmp(pEntry->pszSiteName, pszSiteNameLower))
{
break;
}
}
}
if (!pListEntry)
{
LWNET_LOG_DEBUG("Cached entry not found: %s, %s, %u",
pszDnsDomainNameLower,
pszSiteNameLower ? pszSiteNameLower : "",
queryType);
goto error;
}
dwError = LWNetAllocateMemory(sizeof(*pDcInfo), (PVOID*)&pDcInfo);
BAIL_ON_LWNET_ERROR(dwError);
lastDiscovered = pEntry->LastDiscovered;
lastPinged = pEntry->LastPinged;
isBackoffToWritableDc = pEntry->IsBackoffToWritableDc;
lastBackoffToWritableDc = pEntry->LastBackoffToWritableDc;
pDcInfo->dwPingTime = pEntry->DcInfo.dwPingTime;
pDcInfo->dwDomainControllerAddressType = pEntry->DcInfo.dwDomainControllerAddressType;
pDcInfo->dwFlags = pEntry->DcInfo.dwFlags;
pDcInfo->dwVersion = pEntry->DcInfo.dwVersion;
pDcInfo->wLMToken = pEntry->DcInfo.wLMToken;
pDcInfo->wNTToken = pEntry->DcInfo.wNTToken;
dwError = LWNetAllocateString(
pEntry->DcInfo.pszDomainControllerName,
&pDcInfo->pszDomainControllerName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszDomainControllerAddress,
&pDcInfo->pszDomainControllerAddress);
BAIL_ON_LWNET_ERROR(dwError);
memcpy(pDcInfo->pucDomainGUID,
pEntry->DcInfo.pucDomainGUID,
LWNET_GUID_SIZE);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszNetBIOSDomainName,
&pDcInfo->pszNetBIOSDomainName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszFullyQualifiedDomainName,
&pDcInfo->pszFullyQualifiedDomainName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszDnsForestName,
&pDcInfo->pszDnsForestName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszDCSiteName,
&pDcInfo->pszDCSiteName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszClientSiteName,
&pDcInfo->pszClientSiteName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszNetBIOSHostName,
&pDcInfo->pszNetBIOSHostName);
BAIL_ON_LWNET_ERROR(dwError);
if (pEntry->DcInfo.pszUserName)
{
dwError = LWNetAllocateString(
pEntry->DcInfo.pszUserName,
&pDcInfo->pszUserName);
BAIL_ON_LWNET_ERROR(dwError);
}
error:
if (isAcquired)
{
RW_LOCK_RELEASE_READ(DbHandle->pLock);
}
if (dwError)
{
LWNET_SAFE_FREE_DC_INFO(pDcInfo);
lastPinged = 0;
lastDiscovered = 0;
isBackoffToWritableDc = FALSE;
lastBackoffToWritableDc = 0;
}
LWNET_SAFE_FREE_STRING(pszDnsDomainNameLower);
LWNET_SAFE_FREE_STRING(pszSiteNameLower);
*ppDcInfo = pDcInfo;
*LastDiscovered = lastDiscovered;
*LastPinged = lastPinged;
*IsBackoffToWritableDc = isBackoffToWritableDc;
*LastBackoffToWritableDc = lastBackoffToWritableDc;
return dwError;
}
static
DWORD
LWNetCacheDbUpdate(
IN LWNET_CACHE_DB_HANDLE DbHandle,
IN PCSTR pszDnsDomainName,
IN OPTIONAL PCSTR pszSiteName,
IN DWORD dwDsFlags,
IN LWNET_UNIX_TIME_T LastDiscovered,
IN LWNET_UNIX_TIME_T LastPinged,
IN BOOLEAN IsBackoffToWritableDc,
IN OPTIONAL LWNET_UNIX_TIME_T LastBackoffToWritableDc,
IN PLWNET_DC_INFO pDcInfo
)
{
DWORD dwError = 0;
PLWNET_CACHE_DB_ENTRY pNewEntry = NULL;
PLWNET_CACHE_DB_ENTRY pOldEntry = NULL;
PDLINKEDLIST pOldListEntry = NULL;
BOOLEAN isAcquired = FALSE;
LWNET_CACHE_DB_QUERY_TYPE QueryType = LWNetCacheDbQueryToQueryType(dwDsFlags);
dwError = LWNetAllocateMemory(sizeof(*pNewEntry), (PVOID *)&pNewEntry);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pszDnsDomainName,
&pNewEntry->pszDnsDomainName);
BAIL_ON_LWNET_ERROR(dwError);
LwStrToLower(pNewEntry->pszDnsDomainName);
if (pszSiteName)
{
dwError = LWNetAllocateString(
pszSiteName,
&pNewEntry->pszSiteName);
BAIL_ON_LWNET_ERROR(dwError);
LwStrToLower(pNewEntry->pszSiteName);
}
pNewEntry->QueryType = QueryType;
pNewEntry->LastDiscovered = LastDiscovered;
pNewEntry->LastPinged = LastPinged;
pNewEntry->IsBackoffToWritableDc = IsBackoffToWritableDc;
pNewEntry->LastBackoffToWritableDc = IsBackoffToWritableDc ? LastBackoffToWritableDc : 0;
pNewEntry->DcInfo.dwPingTime = pDcInfo->dwPingTime;
pNewEntry->DcInfo.dwDomainControllerAddressType = pDcInfo->dwDomainControllerAddressType;
pNewEntry->DcInfo.dwFlags = pDcInfo->dwFlags;
pNewEntry->DcInfo.dwVersion = pDcInfo->dwVersion;
pNewEntry->DcInfo.wLMToken = pDcInfo->wLMToken;
pNewEntry->DcInfo.wNTToken = pDcInfo->wNTToken;
dwError = LWNetAllocateString(
pDcInfo->pszDomainControllerName,
&pNewEntry->DcInfo.pszDomainControllerName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pDcInfo->pszDomainControllerAddress,
&pNewEntry->DcInfo.pszDomainControllerAddress);
BAIL_ON_LWNET_ERROR(dwError);
memcpy(pNewEntry->DcInfo.pucDomainGUID,
pDcInfo->pucDomainGUID,
LWNET_GUID_SIZE);
dwError = LWNetAllocateString(
pDcInfo->pszNetBIOSDomainName,
&pNewEntry->DcInfo.pszNetBIOSDomainName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pDcInfo->pszFullyQualifiedDomainName,
&pNewEntry->DcInfo.pszFullyQualifiedDomainName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pDcInfo->pszDnsForestName,
&pNewEntry->DcInfo.pszDnsForestName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pDcInfo->pszDCSiteName,
&pNewEntry->DcInfo.pszDCSiteName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pDcInfo->pszClientSiteName,
&pNewEntry->DcInfo.pszClientSiteName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pDcInfo->pszNetBIOSHostName,
&pNewEntry->DcInfo.pszNetBIOSHostName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pDcInfo->pszUserName ? pDcInfo->pszUserName : "",
&pNewEntry->DcInfo.pszUserName);
BAIL_ON_LWNET_ERROR(dwError);
RW_LOCK_ACQUIRE_WRITE(DbHandle->pLock);
isAcquired = TRUE;
for (pOldListEntry = DbHandle->pCacheList ;
pOldListEntry ;
pOldListEntry = pOldListEntry->pNext)
{
pOldEntry = (PLWNET_CACHE_DB_ENTRY)pOldListEntry->pItem;
if (pOldEntry->QueryType == pNewEntry->QueryType &&
!strcmp(pOldEntry->pszDnsDomainName, pNewEntry->pszDnsDomainName))
{
if (!pOldEntry->pszSiteName && !pNewEntry->pszSiteName)
{
break;
}
if (pOldEntry->pszSiteName && pNewEntry->pszSiteName &&
!strcmp(pOldEntry->pszSiteName, pNewEntry->pszSiteName))
{
break;
}
}
}
if (pOldListEntry)
{
LWNetDLinkedListDelete(
&DbHandle->pCacheList,
pOldEntry);
LWNetCacheDbEntryFree(pOldEntry);
}
dwError = LWNetDLinkedListAppend(
&DbHandle->pCacheList,
pNewEntry);
BAIL_ON_LWNET_ERROR(dwError);
DEBUG_ENTRY(pNewEntry);
pNewEntry = NULL;
error:
if (isAcquired)
{
RW_LOCK_RELEASE_WRITE(DbHandle->pLock);
}
LWNetCacheDbEntryFree(pNewEntry);
return dwError;
}
DWORD
LWNetCacheDbScavenge(
IN LWNET_CACHE_DB_HANDLE DbHandle,
IN LWNET_UNIX_TIME_T PositiveCacheAge,
IN LWNET_UNIX_TIME_T NegativeCacheAge
)
{
DWORD dwError = 0;
LWNET_UNIX_TIME_T now = 0;
LWNET_UNIX_TIME_T positiveTimeLimit = 0;
PLWNET_CACHE_DB_ENTRY pEntry = NULL;
PDLINKEDLIST pListEntry = NULL;
PDLINKEDLIST pNextListEntry = NULL;
BOOLEAN isAcquired = FALSE;
dwError = LWNetGetSystemTime(&now);
positiveTimeLimit = now + PositiveCacheAge;
RW_LOCK_ACQUIRE_WRITE(DbHandle->pLock);
isAcquired = TRUE;
pListEntry = DbHandle->pCacheList;
while(pListEntry)
{
pNextListEntry = pListEntry->pNext;
pEntry = pListEntry->pItem;
if (pEntry->LastPinged < positiveTimeLimit)
{
LWNetDLinkedListDelete(
&DbHandle->pCacheList,
pEntry);
LWNetCacheDbEntryFree(pEntry);
}
pListEntry = pNextListEntry;
}
if (isAcquired)
{
RW_LOCK_RELEASE_WRITE(DbHandle->pLock);
}
return dwError;
}
DWORD
LWNetCacheDbExport(
IN LWNET_CACHE_DB_HANDLE DbHandle,
OUT PLWNET_CACHE_DB_ENTRY* ppEntries,
OUT PDWORD pdwCount
)
{
#if 1
return ERROR_CALL_NOT_IMPLEMENTED;
#else
DWORD dwError = 0;
PLWNET_CACHE_DB_ENTRY pEntries = NULL;
DWORD dwCount = 0;
PDLINKEDLIST pListEntry = NULL;
DWORD i = 0;
RW_LOCK_ACQUIRE_READ(DbHandle->pLock);
for (pListEntry = DbHandle->pCacheList;
pListEntry;
pListEntry = pListEntry->pNext)
{
dwCount++;
}
dwError = LWNetAllocateMemory(sizeof(*pEntries) * dwCount, OUT_PPVOID(&pEntries));
BAIL_ON_LWNET_ERROR(dwError);
i = 0;
for (pListEntry = DbHandle->pCacheList;
pListEntry;
pListEntry = pListEntry->pNext)
{
PLWNET_CACHE_DB_ENTRY pEntry = (PLWNET_CACHE_DB_ENTRY) pListEntry->pItem;
PLWNET_CACHE_DB_ENTRY pNewEntry = &pEntries[i];
dwError = LWNetAllocateString(
pEntry->pszDnsDomainName,
&pNewEntry->pszDnsDomainName);
BAIL_ON_LWNET_ERROR(dwError);
if (pEntry->pszSiteName)
{
dwError = LWNetAllocateString(
pEntry->pszSiteName,
&pNewEntry->pszSiteName);
BAIL_ON_LWNET_ERROR(dwError);
}
pNewEntry->QueryType = pEntry->QueryType;
pNewEntry->LastDiscovered = pEntry->LastDiscovered;
pNewEntry->LastPinged = pEntry->LastPinged;
pNewEntry->IsBackoffToWritableDc = pEntry->IsBackoffToWritableDc;
pNewEntry->LastBackoffToWritableDc = pEntry->LastBackoffToWritableDc;
pNewEntry->DcInfo.dwPingTime = pEntry->DcInfo.dwPingTime;
pNewEntry->DcInfo.dwDomainControllerAddressType = pEntry->DcInfo.dwDomainControllerAddressType;
pNewEntry->DcInfo.dwFlags = pEntry->DcInfo.dwFlags;
pNewEntry->DcInfo.dwVersion = pEntry->DcInfo.dwVersion;
pNewEntry->DcInfo.wLMToken = pEntry->DcInfo.wLMToken;
pNewEntry->DcInfo.wNTToken = pEntry->DcInfo.wNTToken;
dwError = LWNetAllocateString(
pEntry->DcInfo.pszDomainControllerName,
&pNewEntry->DcInfo.pszDomainControllerName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszDomainControllerAddress,
&pNewEntry->DcInfo.pszDomainControllerAddress);
BAIL_ON_LWNET_ERROR(dwError);
memcpy(pNewEntry->DcInfo.pucDomainGUID,
pEntry->DcInfo.pucDomainGUID,
LWNET_GUID_SIZE);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszNetBIOSDomainName,
&pNewEntry->DcInfo.pszNetBIOSDomainName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszFullyQualifiedDomainName,
&pNewEntry->DcInfo.pszFullyQualifiedDomainName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszDnsForestName,
&pNewEntry->DcInfo.pszDnsForestName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszDCSiteName,
&pNewEntry->DcInfo.pszDCSiteName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszClientSiteName,
&pNewEntry->DcInfo.pszClientSiteName);
BAIL_ON_LWNET_ERROR(dwError);
dwError = LWNetAllocateString(
pEntry->DcInfo.pszNetBIOSHostName,
&pNewEntry->DcInfo.pszNetBIOSHostName);
BAIL_ON_LWNET_ERROR(dwError);
if (pEntry->DcInfo.pszUserName)
{
dwError = LWNetAllocateString(
pEntry->DcInfo.pszUserName,
&pNewEntry->DcInfo.pszUserName);
BAIL_ON_LWNET_ERROR(dwError);
}
i++;
}
cleanup:
RW_LOCK_RELEASE_READ(DbHandle->pLock);
*ppEntries = pEntries;
*pdwCount = dwCount;
return dwError;
error:
// TODO: Add free function.
pEntries = NULL;
dwCount = 0;
goto cleanup;
#endif
}
DWORD
LWNetCacheInitialize(
)
{
DWORD dwError = 0;
dwError = LWNetCacheDbOpen(NETLOGON_DB, TRUE, &gDbHandle);
BAIL_ON_LWNET_ERROR(dwError);
error:
if (dwError)
{
LWNetCacheCleanup();
}
return dwError;
}
VOID
LWNetCacheCleanup(
)
{
LWNetCacheDbClose(&gDbHandle);
}
DWORD
LWNetCacheQuery(
IN PCSTR pszDnsDomainName,
IN OPTIONAL PCSTR pszSiteName,
IN DWORD dwDsFlags,
OUT PLWNET_DC_INFO* ppDcInfo,
OUT PLWNET_UNIX_TIME_T LastDiscovered,
OUT PLWNET_UNIX_TIME_T LastPinged,
OUT PBOOLEAN IsBackoffToWritableDc,
OUT PLWNET_UNIX_TIME_T LastBackoffToWritableDc
)
{
return LWNetCacheDbQuery(gDbHandle,
pszDnsDomainName, pszSiteName, dwDsFlags,
ppDcInfo, LastDiscovered, LastPinged,
IsBackoffToWritableDc, LastBackoffToWritableDc);
}
DWORD
LWNetCacheUpdate(
IN PCSTR pszDnsDomainName,
IN OPTIONAL PCSTR pszSiteName,
IN DWORD dwDsFlags,
IN LWNET_UNIX_TIME_T LastDiscovered,
IN LWNET_UNIX_TIME_T LastPinged,
IN BOOLEAN IsBackoffToWritableDc,
IN OPTIONAL LWNET_UNIX_TIME_T LastBackoffToWritableDc,
IN PLWNET_DC_INFO pDcInfo
)
{
DWORD dwError = 0;
dwError = LWNetCacheDbUpdate(
gDbHandle,
pszDnsDomainName,
pszSiteName,
dwDsFlags,
LastDiscovered,
LastPinged,
IsBackoffToWritableDc,
LastBackoffToWritableDc,
pDcInfo);
BAIL_ON_LWNET_ERROR(dwError);
if (pDcInfo->pszDCSiteName &&
(!pszSiteName || strcasecmp(pszSiteName, pDcInfo->pszDCSiteName)))
{
dwError = LWNetCacheDbUpdate(
gDbHandle,
pszDnsDomainName,
pDcInfo->pszDCSiteName,
dwDsFlags,
LastDiscovered,
LastPinged,
IsBackoffToWritableDc,
LastBackoffToWritableDc,
pDcInfo);
BAIL_ON_LWNET_ERROR(dwError);
}
error:
return dwError;
}
DWORD
LWNetCacheScavenge(
IN LWNET_UNIX_TIME_T PositiveCacheAge,
IN LWNET_UNIX_TIME_T NegativeCacheAge
)
{
return LWNetCacheDbScavenge(gDbHandle, PositiveCacheAge, NegativeCacheAge);
}