windows-nt/Source/XPSP1/NT/net/mmc/wins/winsdb.cpp

1666 lines
46 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/**********************************************************************/
/** Microsoft Windows/NT **/
/** Copyright(c) Microsoft Corporation, 1997 - 1999 **/
/**********************************************************************/
/*
winsdb.cpp
Wins database enumerator
FILE HISTORY:
Oct 13 1997 EricDav Modified
*/
#include "stdafx.h"
#include "wins.h"
#include "search.h"
#include "winsdb.h"
#include "tfschar.h"
IMPLEMENT_ADDREF_RELEASE(CWinsDatabase);
IMPLEMENT_SIMPLE_QUERYINTERFACE(CWinsDatabase, IWinsDatabase)
DEBUG_DECLARE_INSTANCE_COUNTER(CWinsDatabase)
CWinsDatabase::CWinsDatabase()
: m_cRef(1), m_fFiltered(FALSE), m_fInitialized(FALSE), m_bShutdown(FALSE), m_hrLastError(hrOK)
{
DEBUG_INCREMENT_INSTANCE_COUNTER(CWinsDatabase);
SetCurrentState(WINSDB_NORMAL);
m_hBinding = NULL;
m_hThread = NULL;
m_hStart = NULL;
m_hAbort = NULL;
m_dwOwner = (DWORD)-1;
m_strPrefix = NULL;
m_dwRecsCount = 0;
m_bEnableCache = FALSE;
}
CWinsDatabase::~CWinsDatabase()
{
DEBUG_DECREMENT_INSTANCE_COUNTER(CWinsDatabase);
m_bShutdown = TRUE;
if (m_strPrefix != NULL)
delete m_strPrefix;
SetEvent(m_hAbort);
SetEvent(m_hStart);
if (WaitForSingleObject(m_hThread, 30000) != WAIT_OBJECT_0)
{
Trace0("WinsDatabase destructor thread never died!\n");
// TerminateThread
}
CloseHandle(m_hAbort);
CloseHandle(m_hStart);
CloseHandle(m_hThread);
}
/*!--------------------------------------------------------------------------
CWinsDatabase::Init
Implementation of IWinsDatabase::Init
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::Init()
{
HRESULT hr = hrOK;
WINSDB_STATE uCurrentState;
m_dwRecsCount = 0;
COM_PROTECT_TRY
{
CORg (GetCurrentState(&uCurrentState));
if (uCurrentState != WINSDB_NORMAL)
{
Trace1("WinsDatabase::Init - called when database busy - state %d\n", uCurrentState);
return E_FAIL;
}
CORg (m_cMemMan.Initialize());
CORg (m_IndexMgr.Initialize());
m_hrLastError = hrOK;
CORg (SetCurrentState(WINSDB_LOADING));
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::Start
Implementation of IWinsDatabase::Start
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::Start()
{
// signal the thread to start loading
SetEvent(m_hStart);
return hrOK;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::Initialize
Implementation of IWinsDatabase::Initialize
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::Initialize(LPCOLESTR pszName, LPCOLESTR pszIP)
{
HRESULT hr = hrOK;
DWORD dwError;
DWORD dwThreadId;
COM_PROTECT_TRY
{
m_strName = pszName;
m_strIp = pszIP;
CORg (m_cMemMan.Initialize());
CORg (m_IndexMgr.Initialize());
m_hStart = ::CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hStart == NULL)
{
dwError = ::GetLastError();
Trace1("WinsDatabase::Initialize - CreateEvent Failed m_hStart %d\n", dwError);
return HRESULT_FROM_WIN32(dwError);
}
m_hAbort = ::CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hAbort == NULL)
{
dwError = ::GetLastError();
Trace1("WinsDatabase::Initialize - CreateEvent Failed m_hAbort %d\n", dwError);
return HRESULT_FROM_WIN32(dwError);
}
m_hThread = ::CreateThread(NULL, 0, ThreadProc, this, 0, &dwThreadId);
if (m_hThread == NULL)
{
dwError = ::GetLastError();
Trace1("WinsDatabase::Init - CreateThread Failed %d\n", dwError);
return HRESULT_FROM_WIN32(dwError);
}
m_fInitialized = TRUE;
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetName
Implementation of IWinsDatabase::GetName
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetName(LPOLESTR pszName, UINT cchMax)
{
HRESULT hr = hrOK;
LPCTSTR pBuf;
COM_PROTECT_TRY
{
if (cchMax < (UINT) (m_strName.GetLength() / sizeof(TCHAR)))
return E_FAIL;
StrnCpy(pszName, (LPCTSTR) m_strName, cchMax);
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetIP
Implementation of IWinsDatabase::GetIP
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetIP(LPOLESTR pszIP, UINT cchMax)
{
HRESULT hr = hrOK;
LPCTSTR pBuf;
COM_PROTECT_TRY
{
if (cchMax < (UINT) (m_strIp.GetLength() / sizeof(TCHAR)))
return E_FAIL;
StrnCpy(pszIP, (LPCTSTR) m_strIp, cchMax);
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::Stop
Implementation of IWinsDatabase::Stop
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::Stop()
{
HRESULT hr = hrOK;
WINSDB_STATE uState;
COM_PROTECT_TRY
{
CORg (GetCurrentState(&uState));
if (uState != WINSDB_LOADING)
return hr;
SetEvent(m_hAbort);
CORg (SetCurrentState(WINSDB_NORMAL));
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::Clear
Clears the wins DB of all records
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::Clear()
{
HRESULT hr = hrOK;
WINSDB_STATE uState;
COM_PROTECT_TRY
{
CORg (GetCurrentState(&uState));
if (uState == WINSDB_SORTING ||
uState == WINSDB_FILTERING)
return E_FAIL;
if (uState == WINSDB_LOADING)
{
SetEvent(m_hAbort);
CORg (SetCurrentState(WINSDB_NORMAL));
}
CORg (m_cMemMan.Initialize());
CORg (m_IndexMgr.Initialize());
m_dwOwner = (DWORD)-1;
if (m_strPrefix != NULL)
delete m_strPrefix;
m_strPrefix = NULL;
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetLastError
Returns the last error for async calls
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetLastError(HRESULT * pLastError)
{
HRESULT hr = hrOK;
WINSDB_STATE uState;
COM_PROTECT_TRY
{
if (pLastError)
*pLastError = m_hrLastError;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::Sort
Implementation of IWinsDatabase::Sort
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::Sort(WINSDB_SORT_TYPE SortType, DWORD dwSortOptions)
{
HRESULT hr = hrOK;
WINSDB_STATE uState;
COM_PROTECT_TRY
{
CORg (GetCurrentState(&uState));
if (uState != WINSDB_NORMAL)
return E_FAIL;
CORg (SetCurrentState(WINSDB_SORTING));
m_IndexMgr.Sort(SortType, dwSortOptions);
CORg (SetCurrentState(WINSDB_NORMAL));
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetHRow
Implementation of IWinsDatabase::GetHRow
returns the HRow in the current sorted index
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetHRow(UINT uIndex,
LPHROW hRow)
{
Assert(uIndex >= 0);
HRESULT hr = hrOK;
WINSDB_STATE uState;
int nCurrentCount;
COM_PROTECT_TRY
{
CORg (GetCurrentCount(&nCurrentCount));
if (uIndex > (UINT) nCurrentCount)
return E_FAIL;
CORg (GetCurrentState(&uState));
if (uState == WINSDB_SORTING || uState == WINSDB_FILTERING)
return E_FAIL;
m_IndexMgr.GetHRow(uIndex, hRow);
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetRows
Implementation of IWinsDatabase::GetRows
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT CWinsDatabase::GetRows( ULONG uNumberOfRows,
ULONG uStartPos,
HROW* pHRow,
int* nNumberOfRowsReturned)
{
int nCurrentCount;
WINSDB_STATE uState;
HRESULT hr = hrOK;
int nReturnedRows = 0;
int i;
HROW hrowCur;
Assert (uStartPos >= 0);
COM_PROTECT_TRY
{
CORg (GetCurrentState(&uState));
if (uState == WINSDB_SORTING || uState == WINSDB_FILTERING)
return E_FAIL;
CORg (GetCurrentCount(&nCurrentCount));
Assert ((int) uStartPos <= nCurrentCount);
if (uStartPos > (UINT) nCurrentCount)
return E_FAIL;
for (i = (int) uStartPos; i < (int) (uStartPos + uNumberOfRows); i++)
{
if( i > nCurrentCount )
{
break;
}
CORg (m_IndexMgr.GetHRow(i, &hrowCur));
// if the row is marked deleted, don't add it to the array
// REVIEW: directly accessing memory here.. we may want to change this
// to go through the memory manager
if ( ((LPWINSDBRECORD) hrowCur)->szRecordName[17] & WINSDB_INTERNAL_DELETED )
{
continue;
}
// fill in the data
pHRow[i-uStartPos] = hrowCur;
nReturnedRows++;
}
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
if (nNumberOfRowsReturned)
*nNumberOfRowsReturned = nReturnedRows;
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetData
Implementation of IWinsDatabase::GetData
returns the HRow in the current sorted index
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT CWinsDatabase::GetData(HROW hRow,
LPWINSRECORD pRecordData)
{
HRESULT hr = E_FAIL;
COM_PROTECT_TRY
{
CORg (m_cMemMan.GetData(hRow, pRecordData));
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::FindRow
Implementation of IWinsDatabase::FindRow
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::FindRow(LPCOLESTR pszName,
HROW hrowStart,
HROW * phRow)
{
HRESULT hr = E_FAIL;
WinsRecord ws;
int nIndex, nPos, nCurrentCount;
HROW hrowCur;
HROW hrowFound = NULL;
char szName[MAX_PATH];
CString strTemp(pszName);
// this should be OEM
WideToMBCS(strTemp, szName, WINS_NAME_CODE_PAGE);
COM_PROTECT_TRY
{
CORg (m_IndexMgr.GetIndex(hrowStart, &nIndex));
/////
CORg(GetHRow(nIndex, &hrowCur));
CORg (m_IndexMgr.GetIndex(hrowCur, &nIndex));
CORg (GetCurrentCount(&nCurrentCount));
if(nIndex != -1)
{
CORg(GetHRow(nIndex, &hrowCur));
for (nPos = nIndex + 1; nPos < nCurrentCount; nPos++)
{
CORg(GetHRow(nPos, &hrowCur));
CORg(GetData(hrowCur, &ws));
if(!_strnicmp(ws.szRecordName, szName, strlen(szName) ))
{
hrowFound = hrowCur;
hr = hrOK;
break;
}
}
}
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
if (phRow)
*phRow = hrowFound;
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetTotalCount
Implementation of IWinsDatabase::GetTotalCount
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetTotalCount(int * nTotalCount)
{
HRESULT hr = hrOK;
COM_PROTECT_TRY
{
*nTotalCount = m_IndexMgr.GetTotalCount();
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetCurrentCount
Implementation of IWinsDatabase::GetCurrentCount
returns the HRow in the current sorted index
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetCurrentCount(int * nCurrentCount)
{
HRESULT hr = hrOK;
COM_PROTECT_TRY
{
if (m_DBState == WINSDB_SORTING)
*nCurrentCount = 0;
else
*nCurrentCount = m_IndexMgr.GetCurrentCount();
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetCurrentScanned(int * nCurrentScanned)
Implementation of IWinsDatabase::GetCurrentScanned
returns the total number of records that were read from the server
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetCurrentScanned(int * nCurrentCount)
{
HRESULT hr = hrOK;
COM_PROTECT_TRY
{
*nCurrentCount = m_dwRecsCount;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::AddRecord
Implementation of IWinsDatabase::AddRecord
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::AddRecord(const LPWINSRECORD pRecordData)
{
HRESULT hr = hrOK;
COM_PROTECT_TRY
{
// critical sections taken care by the memory manager
HROW hrow = NULL;
CORg (m_cMemMan.AddData(*pRecordData, &hrow));
CORg (m_IndexMgr.AddHRow(hrow));
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hrOK;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::DeleteRecord
Implementation of IWinsDatabase::DeleteRecord
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::DeleteRecord(HROW hrowRecord)
{
HRESULT hr = hrOK;
WINSDB_STATE uState;
COM_PROTECT_TRY
{
CORg (GetCurrentState(&uState));
if (uState != WINSDB_NORMAL)
return E_FAIL;
// make sure the hrow is a valid one
if (!m_cMemMan.IsValidHRow(hrowRecord))
return E_FAIL;
// Tell the memmgr to delete this record
CORg (m_cMemMan.Delete(hrowRecord));
// now tell the index manager to remove this hrow
CORg (m_IndexMgr.RemoveHRow(hrowRecord));
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetCurrentState
Implementation of IWinsDatabase::GetCurrentState
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetCurrentState(WINSDB_STATE * pState)
{
CSingleLock cl(&m_csState);
cl.Lock();
*pState = m_DBState;
return hrOK;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::SetCurrentState
Helper function to set the current state, protected
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::SetCurrentState(WINSDB_STATE winsdbState)
{
CSingleLock cl(&m_csState);
cl.Lock();
m_DBState = winsdbState;
return hrOK;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::FilterRecords
Implementation of IWinsDatabase::FilterRecords
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::FilterRecords
(
WINSDB_FILTER_TYPE FilterType,
DWORD dwParam1,
DWORD dwParam2)
{
HRESULT hr = E_NOTIMPL;
WINSDB_STATE uState ;
COM_PROTECT_TRY
{
// fail if the state is other then WINSDB_NORMAL
CORg (GetCurrentState(&uState));
if (uState == WINSDB_SORTING || uState == WINSDB_FILTERING)
return E_FAIL;
// if in the loading state the readrecords function takes care
if(uState != WINSDB_LOADING)
CORg (SetCurrentState(WINSDB_FILTERING));
// do the filtering here, rebuild the filtered name Index
m_IndexMgr.Filter(FilterType, dwParam1, dwParam2);
if(uState != WINSDB_LOADING)
CORg (SetCurrentState(WINSDB_NORMAL));
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::AddFilter
Adds the filters specified to the list
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::AddFilter(WINSDB_FILTER_TYPE FilterType, DWORD dwParam1, DWORD dwParam2, LPCOLESTR strParam3)
{
HRESULT hr = hrOK;
COM_PROTECT_TRY
{
// for filter by type, dwParam1 is the type, dwParam2 is show/not show
m_IndexMgr.AddFilter(FilterType, dwParam1, dwParam2, strParam3);
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::ClearFilter
CLears all the filters
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::ClearFilter(WINSDB_FILTER_TYPE FilterType)
{
HRESULT hr = hrOK;
COM_PROTECT_TRY
{
//CFilteredIndexName *pFilterName = (CFilteredIndexName *)m_IndexMgr.GetFilteredNameIndex();
//pFilterName->ClearFilter();
m_IndexMgr.ClearFilter(FilterType);
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::SetActiveView
Implementation of IWinsDatabase::SetActiveView
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::SetActiveView(WINSDB_VIEW_TYPE ViewType)
{
HRESULT hr = hrOK;
COM_PROTECT_TRY
{
m_IndexMgr.SetActiveView(ViewType);
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::Execute()
The background thread calls into this to execute
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
DWORD
CWinsDatabase::Execute()
{
DWORD dwStatus = 0;
// wait for the other thread to signal us to start doing something
while (::WaitForSingleObject(m_hStart, INFINITE) == WAIT_OBJECT_0)
{
if (m_bShutdown)
break;
Trace0("WinsDatabase::Execute - start event signaled\n");
WINSINTF_BIND_DATA_T wbdBindData;
handle_t hBinding = NULL;
do
{
// enumerate leases here
SetCurrentState(WINSDB_LOADING);
// now that the server name and ip are valid, call
// WINSBind function directly.
WINSINTF_ADD_T waWinsAddress;
DWORD dwStatus;
CString strNetBIOSName;
// call WinsBind function with the IP address
wbdBindData.fTcpIp = 1;
wbdBindData.pPipeName = NULL;
// convert wbdBindData.pServerAdd to wide char again as one of the internal
// functions expects a wide char string, this is done in WinsABind which is bypassed for
// unicode compatible apps
wbdBindData.pServerAdd = (LPSTR) (LPCTSTR) m_strIp;
if ((hBinding = ::WinsBind(&wbdBindData)) == NULL)
{
dwStatus = ::GetLastError();
Trace1("WinsDatabase::Execute - WinsBind failed %lx\n", dwStatus);
break;
}
#ifdef WINS_CLIENT_APIS
dwStatus = ::WinsGetNameAndAdd(
hBinding,
&waWinsAddress,
(BYTE *)strNetBIOSName.GetBuffer(128));
#else
dwStatus = ::WinsGetNameAndAdd(
&waWinsAddress,
(BYTE *)strNetBIOSName.GetBuffer(128));
#endif WINS_CLIENT_APIS
strNetBIOSName.ReleaseBuffer();
if (dwStatus == ERROR_SUCCESS)
{
if(m_dwOwner == (DWORD)-1)
dwStatus = ReadRecords(hBinding);
else
dwStatus = ReadRecordsByOwner(hBinding);
break;
}
else
{
Trace1("WinsDatabase::Execute - WinsGetNameAndAdd failed %lx\n", dwStatus);
break;
}
} while (FALSE);
SetCurrentState(WINSDB_NORMAL);
if(hBinding)
{
// call winsunbind here, the handle is invalid after this and that's fine
WinsUnbind(&wbdBindData, hBinding);
hBinding = NULL;
}
Trace0("WinsDatabase::Execute - all done, going to sleep now...\n");
} // while !Start
Trace0("WinsDatabase::Execute - exiting\n");
return dwStatus;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::ReadRecords
Reads records from the WINS server
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
DWORD
CWinsDatabase::ReadRecords(handle_t hBinding)
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD err = ERROR_SUCCESS;
CWinsResults winsResults;
err = winsResults.Update(hBinding);
WINSINTF_RECS_T Recs;
Recs.pRow = NULL;
DWORD NoOfRecsDesired = 500;
DWORD TypeOfRecs = 4;
BOOL fReadAllRecords ;
PWINSINTF_RECORD_ACTION_T pRow;
enum {ST_SCAN_1B_NAME, ST_SCAN_NORM_NAME} State;
LPBYTE pLastName;
UINT nLastNameLen, nLastBuffLen;
pLastName = NULL;
nLastNameLen = 0;
nLastBuffLen = 0;
#ifdef DEBUG
CTime timeStart, timeFinish;
timeStart = CTime::GetCurrentTime();
#endif
m_dwRecsCount = 0;
// initialize the state machine. If we have a name prefix filter we
// start in ST_INIT_1B since we look first for the 1B names. These are
// particular in a sense their type byte - i.e. 0x1B - has been swapped
// with the first byte from the name. Consequently we need to do the same
// to allow WINS to look first for these names. Once we get over the 1B zone
// of our names, we restore the first byte and initiate another cycle for
// the rest of the name.
if (m_strPrefix != NULL)
{
nLastNameLen = nLastBuffLen = strlen(m_strPrefix) + 1;
pLastName = (LPBYTE) new CHAR[nLastBuffLen];
strcpy((LPSTR)pLastName, m_strPrefix);
pLastName[0] = 0x1B;
State = ST_SCAN_1B_NAME;
}
else
{
State = ST_SCAN_NORM_NAME;
}
do
{
#ifdef WINS_CLIENT_APIS
err = ::WinsGetDbRecsByName(
hBinding,
NULL,
WINSINTF_BEGINNING,
pLastName,
nLastNameLen,
NoOfRecsDesired,
TypeOfRecs,
&Recs);
#else
err = ::WinsGetDbRecsByName(
NULL,
WINSINTF_BEGINNING,
pFromName,
LastNameLen,
NoOfRecsDesired,
TypeOfRecs,
&Recs);
#endif WINS_CLIENT_APIS
// check to see if we need to abort
if (WaitForSingleObject(m_hAbort, 0) == WAIT_OBJECT_0)
{
Trace0("CWinsDatabase::ReadRecords - abort detected\n");
dwStatus = ERROR_OPERATION_ABORTED;
break;
}
if (err == ERROR_REC_NON_EXISTENT)
{
//
// Not a problem, there simply
// are no records in the database
//
Trace0("WinsDatabase::ReadRecords - no records in the Datbase\n");
fReadAllRecords = TRUE;
err = ERROR_SUCCESS;
break;
}
if (err == ERROR_SUCCESS)
{
fReadAllRecords = Recs.NoOfRecs < NoOfRecsDesired;
if (fReadAllRecords)
Trace0("WinsDatabase::ReadRecords - Recs.NoOfRecs < NoOfRecsDesired, will exit\n");
TRY
{
DWORD i;
pRow = Recs.pRow;
for (i = 0; i < Recs.NoOfRecs; ++i, ++pRow)
{
PWINSINTF_RECORD_ACTION_T pRow1 = Recs.pRow;
WinsRecord wRecord;
HROW hrow = NULL;
WinsIntfToWinsRecord(pRow, wRecord);
if (pRow->OwnerId < (UINT) winsResults.AddVersMaps.GetSize())
{
wRecord.dwOwner = winsResults.AddVersMaps[pRow->OwnerId].Add.IPAdd;
}
else
{
// having a record owned by a server which is not in the version map
// we got just earlier from WINS is not something that usually happens.
// It might happen only if the new owner was added right in between.
// Unlikely since this is a very small window - but if this happens
// just skip the record. From our point of view this owner doesn't exist
// hence the record doesn't belong to the view. It will show up with the
// first refresh.
continue;
}
m_dwRecsCount++;
if (!m_bEnableCache && !m_IndexMgr.AcceptWinsRecord(&wRecord))
continue;
// add the data to our memory store and
// to the sorted index
m_cMemMan.AddData(wRecord, &hrow);
// if m_bEnableCache is 0 the the filter was checked
m_IndexMgr.AddHRow(hrow, TRUE, !m_bEnableCache);
//Trace1("%d records added to DB\n", m_dwRecsCount);
}
// if we reached the end of the DB there is no need to do
// anything from below. Is just pLastName that needs to be
// freed up - this is done outside the loop, before exiting
// the call.
if (!fReadAllRecords)
{
BOOL fRangeOver = FALSE;
// get to the last record that was retrieved.
--pRow;
// check if the last name retrieved from the server is still
// mathing the pattern prefix (if any) or the range has been
// passed over (fRangeOver)
if (m_strPrefix != NULL)
{
for (UINT i = 0; i < pRow->NameLen && m_strPrefix[i] != 0; i++)
{
if (m_strPrefix[i] != pRow->pName[i])
{
fRangeOver = TRUE;
break;
}
}
}
// here fRangeOver is either TRUE if the name doesn't match the pattern
// prefix or FALSE if the range is not passed yet. This latter thing means
// either the name is included in the prefix or the prefix isn't included in the name
// !!! We might want to invalidate the "name included in the prefix" case.
if (fRangeOver)
{
switch(State)
{
case ST_SCAN_1B_NAME:
// in this state pLastName is definitely not NULL and even more,
// it once copied m_strPrefix. Since pLastName can only grow, it is
// certain it is large enough to cotain m_strPrefix one more time.
strcpy((LPSTR)pLastName, m_strPrefix);
nLastNameLen = strlen((LPCSTR)pLastName);
State = ST_SCAN_NORM_NAME;
break;
case ST_SCAN_NORM_NAME:
// we were scanning normal names and we passed
// over the range of names we are looking for
// so just get out of the loop.
fReadAllRecords = TRUE;
break;
}
}
else
{
// enlarge the pLastName if needed
if (nLastBuffLen < pRow->NameLen+2)
{
if (pLastName != NULL)
delete pLastName;
nLastBuffLen = pRow->NameLen+2;
pLastName = (LPBYTE)new CHAR[nLastBuffLen];
}
// copy in pLastName the name of the last record
strcpy((LPSTR)pLastName, (LPCSTR)(pRow->pName));
if (pRow->NameLen >= 16 && pLastName[15] == 0x1B)
{
CHAR ch = pLastName[15];
pLastName[15] = pLastName[0];
pLastName[0] = ch;
}
strcat((LPSTR)pLastName, "\x01");
nLastNameLen = strlen((LPCSTR)pLastName);
}
}
}
CATCH_ALL(e)
{
err = ::GetLastError();
Trace1("WinsDatabase::ReadRecords - Exception! %d \n", err);
m_hrLastError = HRESULT_FROM_WIN32(err);
}
END_CATCH_ALL
}
else
{
Trace1("WinsDatabase::ReadRecords - GetRecsByName failed! %d \n", err);
m_hrLastError = HRESULT_FROM_WIN32(err);
break;
}
if (Recs.pRow != NULL)
{
::WinsFreeMem(Recs.pRow);
}
} while(!fReadAllRecords );
if (pLastName != NULL)
delete pLastName;
#ifdef DEBUG
timeFinish = CTime::GetCurrentTime();
CTimeSpan timeDelta = timeFinish - timeStart;
CString strTempTime = timeDelta.Format(_T("%H:%M:%S"));
Trace2("WINS DB - ReadRecords: %d records read, total time %s\n", m_dwRecsCount, strTempTime);
#endif
return dwStatus;
}
/*!--------------------------------------------------------------------------
ThreadProc
-
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
DWORD WINAPI
ThreadProc(LPVOID pParam)
{
DWORD dwReturn;
HRESULT hr = hrOK;
COM_PROTECT_TRY
{
CWinsDatabase *pWinsDB = (CWinsDatabase *) pParam;
Trace0("WinsDatabase Background Thread started.\n");
dwReturn = pWinsDB->Execute();
}
COM_PROTECT_CATCH
return dwReturn;
}
/*!--------------------------------------------------------------------------
WinsIntfToWinsRecord
Converts a wins record from the server into the WinsRecord struct
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
void
WinsIntfToWinsRecord(PWINSINTF_RECORD_ACTION_T pRecord, WinsRecord & wRecord)
{
ZeroMemory(&wRecord, sizeof(WinsRecord));
//::strcpy(wRecord.szRecordName, (LPCSTR)pRecord->pName);
::memcpy(wRecord.szRecordName, (LPCSTR)pRecord->pName, pRecord->NameLen);
wRecord.dwExpiration = (ULONG) pRecord->TimeStamp;
wRecord.liVersion = pRecord->VersNo;
wRecord.dwOwner = pRecord->OwnerId;
wRecord.dwNameLen = WINSINTF_NAME_LEN_M(pRecord->NameLen);
wRecord.dwType |= (BYTE) wRecord.szRecordName[15];
// translate the state and types to our own definitions
switch (pRecord->State_e)
{
case WINSINTF_E_TOMBSTONE:
wRecord.dwState |= WINSDB_REC_TOMBSTONE;
break;
case WINSINTF_E_DELETED:
//Trace0("WinsIntfToWinsRecord - deleted record.\n");
wRecord.dwState |= WINSDB_REC_DELETED;
break;
case WINSINTF_E_RELEASED:
//Trace0("WinsIntfToWinsRecord - released record.\n");
wRecord.dwState |= WINSDB_REC_RELEASED;
break;
default: // WINSINTF_E_ACTIVE:
wRecord.dwState |= WINSDB_REC_ACTIVE;
break;
}
switch (pRecord->TypOfRec_e)
{
case WINSINTF_E_NORM_GROUP:
wRecord.dwState |= WINSDB_REC_NORM_GROUP;
break;
case WINSINTF_E_SPEC_GROUP:
wRecord.dwState |= WINSDB_REC_SPEC_GROUP;
break;
case WINSINTF_E_MULTIHOMED:
wRecord.dwState |= WINSDB_REC_MULTIHOMED;
break;
default: // WINSINTF_E_UNIQUE:
wRecord.dwState |= WINSDB_REC_UNIQUE;
break;
}
// now do the type -- move the value into the high word
DWORD dwTemp = (pRecord->TypOfRec_e << 16);
wRecord.dwType |= dwTemp;
// now set the static flag
if (pRecord->fStatic)
wRecord.dwState |= WINSDB_REC_STATIC;
// store all of the IP addrs
wRecord.dwNoOfAddrs = pRecord->NoOfAdds;
if (pRecord->NoOfAdds > 1)
{
Assert(pRecord->NoOfAdds <= WINSDB_MAX_NO_IPADDRS);
//if (wRecord.dwNoOfAddrs > 4)
// Trace1("WinsIntfToWinsRecord - record with multiple (>4) IP addrs: %d\n", wRecord.dwNoOfAddrs);
wRecord.dwState |= WINSDB_REC_MULT_ADDRS;
for (UINT i = 0; i < pRecord->NoOfAdds; i++)
wRecord.dwIpAdd[i] = pRecord->pAdd[i].IPAdd;
}
else
{
if (pRecord->NoOfAdds == 0)
{
//Trace2("WinsIntfToWinsRecord - record with NoOfAdds == 0; IP: %lx State: %lx \n", pRecord->Add.IPAdd, wRecord.dwState);
}
if (pRecord->Add.IPAdd == 0)
{
Trace1("WinsIntfToWinsRecord - record with 0 IP Address! State: %lx \n", wRecord.dwState);
}
wRecord.dwIpAdd[0] = pRecord->Add.IPAdd;
}
}
/*!--------------------------------------------------------------------------
CreateWinsDatabase
-
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
HRESULT
CreateWinsDatabase(CString& strName, CString& strIP, IWinsDatabase **ppWinsDB)
{
AFX_MANAGE_STATE(AfxGetModuleState());
CWinsDatabase * pWinsDB = NULL;
HRESULT hr = hrOK;
SPIWinsDatabase spWinsDB;
COM_PROTECT_TRY
{
pWinsDB = new CWinsDatabase();
Assert(pWinsDB);
spWinsDB = pWinsDB;
CORg(pWinsDB->Initialize(strName, strIP));
*ppWinsDB = spWinsDB.Transfer();
COM_PROTECT_ERROR_LABEL;
}
COM_PROTECT_CATCH
return hr;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::SetApiInfo
Implementation of SetApiInfo of IWinsDatabase
Author: FlorinT
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::SetApiInfo(DWORD dwOwner, LPCOLESTR strPrefix, BOOL bCache)
{
// first cleanup the old prefix
if (m_strPrefix != NULL)
{
delete m_strPrefix;
m_strPrefix = NULL;
}
if (strPrefix != NULL)
{
UINT nPxLen = 0;
LPSTR pPrefix;
nPxLen = (_tcslen(strPrefix)+1)*sizeof(TCHAR);
m_strPrefix = new char[nPxLen];
if (m_strPrefix != NULL)
{
#ifdef _UNICODE
if (WideCharToMultiByte(CP_OEMCP,
0,
strPrefix,
-1,
m_strPrefix,
nPxLen,
NULL,
NULL) == 0)
{
delete m_strPrefix;
m_strPrefix = NULL;
}
#else
CharToOem(strPrefix, m_strPrefix);
#endif
m_strPrefix = _strupr(m_strPrefix);
for (pPrefix = m_strPrefix;
*pPrefix != '\0' && *pPrefix != '*' && *pPrefix != '?';
pPrefix++);
*pPrefix = '\0';
}
}
m_dwOwner = dwOwner;
m_bEnableCache = bCache;
return hrOK;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::GetCachingFlag
Implementation of GetCachingFlag of IWinsDatabase
Author: FlorinT
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::GetCachingFlag(LPBOOL pbCache)
{
*pbCache = m_bEnableCache;
return hrOK;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::ReloadSuggested
Implementation of ReloadSuggested of IWinsDatabase
Author: FlorinT
---------------------------------------------------------------------------*/
HRESULT
CWinsDatabase::ReloadSuggested(DWORD dwOwner, LPCOLESTR strPrefix, LPBOOL pbReload)
{
// check whether we filtered on a particular owner.
if (m_dwOwner != 0xFFFFFFFF)
{
// we did filter on owner previously, suggest RELOAD if we now
// don't want to filter on any owner (dwOwner == 0xffffffff)
// or the owner we want to filter is different from the original one
*pbReload = (m_dwOwner != dwOwner);
}
else
{
// we didn't filter on any owner previously so we either loaded
// all the records (if no name prefix was specified) or loaded
// all the records matching the given prefix
if (m_strPrefix != NULL)
{
// we did have a previous prefix to match so we need to see
// if the new prefix is not by any chance more specific than
// the original one. In which case there is no need to reload
LPSTR pPrefix;
UINT nPxLen;
UINT i;
if (strPrefix == NULL)
{
// if now we're not filtering by name, since we did previously
// we definitely need to reload the database
*pbReload = TRUE;
return hrOK;
}
nPxLen = (_tcslen(strPrefix)+1)*sizeof(TCHAR);
pPrefix = new char[nPxLen];
if (pPrefix != NULL)
{
#ifdef _UNICODE
if (WideCharToMultiByte(CP_OEMCP,
0,
strPrefix,
-1,
pPrefix,
nPxLen,
NULL,
NULL) == 0)
{
delete pPrefix;
*pbReload = TRUE;
return hrOK;
}
#else
CharToOem(strPrefix, pPrefix);
#endif
pPrefix = _strupr(pPrefix);
for (i = 0;
pPrefix[i] != '\0' && pPrefix[i] != '*' && pPrefix[i] != '?';
i++);
pPrefix[i] = '\0';
// we don't suggest database reloading only if the current prefix
// is a prefix for the new one to be applied. This way, whatever
// was retrieved previously already contains the names having the
// new prefix.
*pbReload = (strncmp(m_strPrefix, pPrefix, strlen(m_strPrefix)) != 0);
delete pPrefix;
}
else
{
// couldn't allocate memory -> serious enough to ask a full reload
*pbReload = TRUE;
}
}
else
{
// well, there was no prefix specified last time the db was loaded so
// we should have the whole database in hand. No need to reload.
*pbReload = FALSE;
}
}
return hrOK;
}
/*!--------------------------------------------------------------------------
CWinsDatabase::ReadRecordsByOwner
Reads records from the WINS server for a particular owner
Author: EricDav, v-shubk
---------------------------------------------------------------------------*/
#define MAX_DESIRED_RECORDS 400
#define LARGE_GAP_DETECT_COUNT 32
DWORD
CWinsDatabase::ReadRecordsByOwner(handle_t hBinding)
{
DWORD err;
CWinsResults winsResults;
WINSINTF_RECS_T Recs;
DWORD dwIP;
LARGE_INTEGER MinVersNo, MaxVersNo;
LARGE_INTEGER LowestVersNo;
DWORD dwDesired;
DWORD dwLargeGapCount;
WINSINTF_ADD_T OwnerAdd;
DWORD i;
err = winsResults.Update(hBinding);
if (err != ERROR_SUCCESS)
{
m_hrLastError = HRESULT_FROM_WIN32(err);
return err;
}
MinVersNo.QuadPart= 0;
for (i = 0; i < (int)winsResults.NoOfOwners; i++)
{
if (m_dwOwner == winsResults.AddVersMaps[i].Add.IPAdd)
{
MaxVersNo = winsResults.AddVersMaps[i].VersNo;
break;
}
}
// if we couldn't find the owner (highly unlikely) get out
// with error INVALID_PARAMETER.
if (i == winsResults.NoOfOwners)
{
err = ERROR_INVALID_PARAMETER;
m_hrLastError = HRESULT_FROM_WIN32(err);
return err;
}
m_dwRecsCount = 0;
OwnerAdd.Type = 0;
OwnerAdd.Len = 4;
OwnerAdd.IPAdd = m_dwOwner;
// This is what the server does to retrieve the records:
// 1. sets an ascending index on owner & version number.
// 2. goes to the first record owned by the given owner,
// having a version number larger or equal to MinVersNo.
// 3. stop if the record's vers num is higher than the range specified
// 4. stop if more than 1000 recs have been already received
// 5. add the new record to the set to return and go to 3.
//
dwDesired = MAX_DESIRED_RECORDS;
dwLargeGapCount = LARGE_GAP_DETECT_COUNT;
LowestVersNo.QuadPart = 0;
if (MaxVersNo.QuadPart > dwDesired)
MinVersNo.QuadPart = MaxVersNo.QuadPart-dwDesired;
else
MinVersNo.QuadPart = 0;
Recs.pRow = NULL;
while(MaxVersNo.QuadPart >= MinVersNo.QuadPart)
{
// clear up the previous array - if any
if (Recs.pRow != NULL)
{
::WinsFreeMem(Recs.pRow);
Recs.pRow = NULL;
}
// go to WINS to get the data for the given Owner
#ifdef WINS_CLIENT_APIS
err = ::WinsGetDbRecs(hBinding, &OwnerAdd, MinVersNo,
MaxVersNo, &Recs);
#else
err = ::WinsGetDbRecs(&OwnerAdd, MinVersNo,
MaxVersNo, &Recs);
#endif WINS_CLIENT_APIS
// if abort was requested, break out with "ABORTED"
if (WaitForSingleObject(m_hAbort, 0) == WAIT_OBJECT_0)
{
err = ERROR_OPERATION_ABORTED;
break;
}
// if there is any kind of error break out
if (err != ERROR_SUCCESS)
{
if (err == ERROR_REC_NON_EXISTENT)
{
// I'm not sure this happens after all. The server side (WINS) has
// not code path returning such an error code.
err = ERROR_SUCCESS;
}
else
{
// if this happens, just get out with the error, and save the
// meaning of the error
m_hrLastError = HRESULT_FROM_WIN32(err);
}
break;
}
// if got less than 1/4 of the size of the range, expand the range
// to double of what it was + 1. (+1 is important to avoid the effect
// of dramatic drop down because of DWORD roll-over
if (Recs.NoOfRecs <= (dwDesired >> 2))
{
dwDesired <<= 1;
dwDesired |= 1;
}
// else if got more than 3/4 of the size of the range, split the range in 2
// but not less than MAX_DESIRED_RECORDS
else if (Recs.NoOfRecs >= (dwDesired - (dwDesired >> 2)))
{
dwDesired = max (MAX_DESIRED_RECORDS, dwDesired >> 1);
}
TRY
{
DWORD j;
PWINSINTF_RECORD_ACTION_T pRow;
for (j = 0, pRow = Recs.pRow; j < Recs.NoOfRecs; j++, ++pRow)
{
WinsRecord wRecord;
HROW hrow = NULL;
pRow->OwnerId = m_dwOwner;
WinsIntfToWinsRecord(pRow, wRecord);
m_dwRecsCount++;
if (!m_bEnableCache && !m_IndexMgr.AcceptWinsRecord(&wRecord))
continue;
// add the data to our memory store and
// to the sorted index
m_cMemMan.AddData(wRecord, &hrow);
m_IndexMgr.AddHRow(hrow, FALSE, !m_bEnableCache);
}
// now setup the new range to search..
//
// if this is not the gap boundary detection cycle, the next MaxVersNo
// needs to go right below the current MinVersNo. Otherwise, MaxVersNo
// needs to remain untouched!
if (dwLargeGapCount != 0)
MaxVersNo.QuadPart = MinVersNo.QuadPart - 1;
// if no records were found..
if (Recs.NoOfRecs == 0)
{
// ..and we were already in the gap boundary detection cycle..
if (dwLargeGapCount == 0)
// ..just break the loop - there are simply no more records
// for this owner in the database
break;
// ..otherwise just decrease the gap boundary detection counter.
// If it reaches 0, then next cycle we will attempt to see if
// there is any record closer to the lowest edge of the range by
// expanding for one time only the whole space.
dwLargeGapCount--;
}
else
{
// if we just exited the gap boundary detection cycle by finding some
// records, set the LowestVersNo to one more than the largest VersNo
// we found during this cycle.
if (dwLargeGapCount == 0)
{
pRow--;
LowestVersNo.QuadPart = pRow->VersNo.QuadPart+1;
}
// if there were any records found, just reset the gap boundary detection counter.
dwLargeGapCount = LARGE_GAP_DETECT_COUNT;
}
// if the dwLargeGapCount counter is zero, it means the next cycle is a gap boundary detection one
// which means the range should be set for the whole unexplored space.
if (dwLargeGapCount != 0 && MaxVersNo.QuadPart > LowestVersNo.QuadPart + dwDesired)
MinVersNo.QuadPart = MaxVersNo.QuadPart - dwDesired;
else
MinVersNo.QuadPart = LowestVersNo.QuadPart;
}
CATCH_ALL(e)
{
err = ::GetLastError();
m_hrLastError = HRESULT_FROM_WIN32(err);
}
END_CATCH_ALL
}
if (Recs.pRow != NULL)
::WinsFreeMem(Recs.pRow);
return err;
}