977 lines
28 KiB
C
977 lines
28 KiB
C
|
#include <nt.h> // DbgPrint prototype
|
|||
|
#include <ntrtl.h> // DbgPrint
|
|||
|
#include <nturtl.h> // Needed by winbase.h
|
|||
|
|
|||
|
#include <windef.h> // DWORD
|
|||
|
#include <winbase.h> // LocalFree
|
|||
|
|
|||
|
#include <rpcutil.h> // GENERIC_ENUM_STRUCT
|
|||
|
|
|||
|
#include <lmcons.h> // NET_API_STATUS
|
|||
|
#include <lmerr.h> // NetError codes
|
|||
|
#include <lmremutl.h> // SUPPORTS_RPC
|
|||
|
|
|||
|
#include <bowser.h> // generated by the MIDL complier
|
|||
|
#include <brnames.h> // Service and interface names
|
|||
|
|
|||
|
#include <netlib.h>
|
|||
|
#include <netdebug.h>
|
|||
|
|
|||
|
#include <winsvc.h>
|
|||
|
|
|||
|
#include <lmserver.h>
|
|||
|
#include <tstr.h>
|
|||
|
|
|||
|
#include <ntddbrow.h>
|
|||
|
#include <brcommon.h> // Routines common between client & server
|
|||
|
|
|||
|
VOID
|
|||
|
UpdateInterimServerListElement(
|
|||
|
IN PINTERIM_SERVER_LIST ServerList,
|
|||
|
IN PINTERIM_ELEMENT Element,
|
|||
|
IN ULONG Level,
|
|||
|
IN BOOLEAN NewElement
|
|||
|
);
|
|||
|
|
|||
|
PINTERIM_ELEMENT
|
|||
|
AllocateInterimServerListEntry(
|
|||
|
IN PSERVER_INFO_101 ServerInfo,
|
|||
|
IN ULONG Level
|
|||
|
);
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
MergeServerList(
|
|||
|
IN OUT PINTERIM_SERVER_LIST InterimServerList,
|
|||
|
IN ULONG Level,
|
|||
|
IN PVOID NewServerList,
|
|||
|
IN ULONG NewEntriesRead,
|
|||
|
IN ULONG NewTotalEntries
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function will merge two server lists. It will reallocate the buffer
|
|||
|
for the old list if appropriate.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN OUT PINTERIM_SERVER_LIST InterimServerList - Supplies an interim server list to merge into.
|
|||
|
|
|||
|
IN ULONG Level - Supplies the level of the list (100 or 101). Special
|
|||
|
level 1010 is really level 101 with the special semantic that no
|
|||
|
fields from this the NewServerList override existing fields in the
|
|||
|
InterimServerList.
|
|||
|
|
|||
|
IN ULONG NewServerList - Supplies the list to merge into the interim list
|
|||
|
|
|||
|
IN ULONG NewEntriesRead - Supplies the entries read in the list.
|
|||
|
|
|||
|
IN ULONG NewTotalEntries - Supplies the total entries available in the list.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG i;
|
|||
|
ULONG ServerElementSize;
|
|||
|
PSERVER_INFO_101 ServerInfo = NewServerList;
|
|||
|
PINTERIM_ELEMENT InterimEntry = NULL;
|
|||
|
PLIST_ENTRY InterimList;
|
|||
|
PINTERIM_ELEMENT NewElement = NULL;
|
|||
|
|
|||
|
|
|||
|
if (Level == 100) {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_100);
|
|||
|
} else if ( Level == 101 || Level == 1010 ) {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_101);
|
|||
|
} else {
|
|||
|
return(ERROR_INVALID_LEVEL);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Early out if no entries in list.
|
|||
|
//
|
|||
|
|
|||
|
if (NewEntriesRead == 0) {
|
|||
|
return NERR_Success;
|
|||
|
}
|
|||
|
|
|||
|
PrepareServerListForMerge(NewServerList, Level, NewEntriesRead);
|
|||
|
|
|||
|
InterimList = InterimServerList->ServerList.Flink;
|
|||
|
|
|||
|
//
|
|||
|
// Walk the existing structure, packing it into an interim element, and
|
|||
|
// sticking the element into the interim table.
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0; i < NewEntriesRead; i ++) {
|
|||
|
BOOLEAN EntryInserted = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Walk forward in the interim element and find the appropriate place
|
|||
|
// to insert this element.
|
|||
|
//
|
|||
|
|
|||
|
while (InterimList != &InterimServerList->ServerList) {
|
|||
|
|
|||
|
LONG CompareResult;
|
|||
|
|
|||
|
InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
// KdPrint(("MergeServerList: Compare %ws and %ws\n", NewElement->Name, InterimEntry->Name));
|
|||
|
|
|||
|
#if DBG
|
|||
|
//
|
|||
|
// Make sure that this entry is lexically less than the next
|
|||
|
// entry.
|
|||
|
//
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY NextList = InterimList->Flink;
|
|||
|
PINTERIM_ELEMENT NextEntry = CONTAINING_RECORD(NextList, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
if (NextList != &InterimServerList->ServerList) {
|
|||
|
ASSERT (wcscmp(InterimEntry->Name, NextEntry->Name) < 0);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now make sure that the input buffer also doesn't contain
|
|||
|
// duplicate entries.
|
|||
|
//
|
|||
|
|
|||
|
if (i < (NewEntriesRead-1)) {
|
|||
|
PSERVER_INFO_101 NextServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
|
|||
|
|
|||
|
ASSERT (wcscmp(ServerInfo->sv101_name, NextServerInfo->sv101_name) <= 0);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
CompareResult = wcscmp(ServerInfo->sv101_name, InterimEntry->Name);
|
|||
|
|
|||
|
if (CompareResult == 0) {
|
|||
|
|
|||
|
// KdPrint(("MergeServerList: Elements equal - update\n"));
|
|||
|
|
|||
|
//
|
|||
|
// If the new information should override the old information,
|
|||
|
// copy it on top of the new info.
|
|||
|
//
|
|||
|
if ( Level != 1010 ) {
|
|||
|
InterimEntry->PlatformId = ServerInfo->sv101_platform_id;
|
|||
|
|
|||
|
if (Level >= 101) {
|
|||
|
InterimEntry->MajorVersionNumber = ServerInfo->sv101_version_major;
|
|||
|
|
|||
|
InterimEntry->MinorVersionNumber = ServerInfo->sv101_version_minor;
|
|||
|
|
|||
|
InterimEntry->Type = ServerInfo->sv101_type;
|
|||
|
|
|||
|
InterimServerList->TotalBytesNeeded -= wcslen(InterimEntry->Comment) * sizeof(WCHAR) + sizeof(WCHAR);
|
|||
|
|
|||
|
wcscpy(InterimEntry->Comment, ServerInfo->sv101_comment);
|
|||
|
|
|||
|
InterimServerList->TotalBytesNeeded += wcslen(InterimEntry->Comment) * sizeof(WCHAR) + sizeof(WCHAR);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
UpdateInterimServerListElement(InterimServerList, InterimEntry, Level, FALSE);
|
|||
|
|
|||
|
EntryInserted = TRUE;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
} else if (CompareResult > 0) {
|
|||
|
|
|||
|
// KdPrint(("MergeServerList: Elements greater. Skip element\n"));
|
|||
|
|
|||
|
InterimList = InterimList->Flink;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
NewElement = AllocateInterimServerListEntry(ServerInfo, Level);
|
|||
|
|
|||
|
if (NewElement == NULL) {
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
// KdPrint(("MergeServerList: Elements less. Insert at end\n"));
|
|||
|
|
|||
|
//
|
|||
|
// The new entry is < than the previous entry. Insert it
|
|||
|
// before this entry.
|
|||
|
//
|
|||
|
|
|||
|
InsertTailList(&InterimEntry->NextElement, &NewElement->NextElement);
|
|||
|
|
|||
|
//
|
|||
|
// Skip to the next element in the list.
|
|||
|
//
|
|||
|
|
|||
|
InterimList = &NewElement->NextElement;
|
|||
|
|
|||
|
UpdateInterimServerListElement(InterimServerList, NewElement, Level, TRUE);
|
|||
|
|
|||
|
EntryInserted = TRUE;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!EntryInserted &&
|
|||
|
(InterimList == &InterimServerList->ServerList)) {
|
|||
|
|
|||
|
NewElement = AllocateInterimServerListEntry(ServerInfo, Level);
|
|||
|
|
|||
|
if (NewElement == NULL) {
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
// KdPrint(("MergeServerList: Insert %ws at end of list\n", NewElement->Name));
|
|||
|
|
|||
|
InsertTailList(&InterimServerList->ServerList, &NewElement->NextElement);
|
|||
|
|
|||
|
InterimList = &NewElement->NextElement;
|
|||
|
|
|||
|
UpdateInterimServerListElement(InterimServerList, NewElement, Level, TRUE);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
|
|||
|
}
|
|||
|
|
|||
|
#if 0
|
|||
|
{
|
|||
|
PLIST_ENTRY InterimList;
|
|||
|
ULONG TotalNeededForList = 0;
|
|||
|
|
|||
|
for (InterimList = InterimServerList->ServerList.Flink ;
|
|||
|
InterimList != &InterimServerList->ServerList ;
|
|||
|
InterimList = InterimList->Flink ) {
|
|||
|
ULONG ServerElementSize;
|
|||
|
|
|||
|
InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
if (Level == 100) {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_100);
|
|||
|
} else {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_101);
|
|||
|
}
|
|||
|
|
|||
|
ServerElementSize += wcslen(InterimEntry->Name)*sizeof(WCHAR)+sizeof(WCHAR);
|
|||
|
|
|||
|
ServerElementSize += wcslen(InterimEntry->Comment)*sizeof(WCHAR)+sizeof(WCHAR);
|
|||
|
|
|||
|
// KdPrint(("MergeInterimServerList: %ws. %ld needed\n", InterimEntry->Name, ServerElementSize));
|
|||
|
|
|||
|
TotalNeededForList += ServerElementSize;
|
|||
|
}
|
|||
|
|
|||
|
if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
|
|||
|
KdPrint(("UpdateInterimServerList: Wrong number of bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
// KdPrint(("%lx bytes needed to hold server list\n", InterimServerList->TotalBytesNeeded));
|
|||
|
|
|||
|
//
|
|||
|
// Also, we had better have the whole table locally.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (InterimServerList->EntriesRead == InterimServerList->TotalEntries);
|
|||
|
|
|||
|
return(NERR_Success);
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
__cdecl
|
|||
|
CompareServerInfo(
|
|||
|
const void * Param1,
|
|||
|
const void * Param2
|
|||
|
)
|
|||
|
{
|
|||
|
const SERVER_INFO_100 * ServerInfo1 = Param1;
|
|||
|
const SERVER_INFO_100 * ServerInfo2 = Param2;
|
|||
|
|
|||
|
return wcscmp(ServerInfo1->sv100_name, ServerInfo2->sv100_name);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PrepareServerListForMerge(
|
|||
|
IN PVOID ServerInfoList,
|
|||
|
IN ULONG Level,
|
|||
|
IN ULONG EntriesInList
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
MergeServerList requires that the inputs to the list be in a strictly
|
|||
|
sorted order. This routine guarantees that this list will be of
|
|||
|
an "appropriate" form to be merged.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PVOID ServerInfoList - Supplies the list to munge.
|
|||
|
|
|||
|
IN ULONG Level - Supplies the level of the list (100 or 101).
|
|||
|
(Or level 1010 which is identical to level 101.)
|
|||
|
|
|||
|
IN ULONG EntriesInList - Supplies the number of entries in the list.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Note:
|
|||
|
In 99% of the cases, the list passed in will already be sorted. We want to
|
|||
|
take the input list and first check to see if it is sorted. If it is,
|
|||
|
we can return immediately. If it is not, we need to sort the list.
|
|||
|
|
|||
|
We don't just unilaterally sort the list, because the input is mostly
|
|||
|
sorted anyway, and there are no good sorting algorithms that handle mostly
|
|||
|
sorted inputs. Since we will see unsorted input only rarely (basically,
|
|||
|
we will only see it from WfW machines), we just take the penalty of a worst
|
|||
|
case quicksort if the input is unsorted.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LONG i;
|
|||
|
ULONG ServerElementSize;
|
|||
|
PSERVER_INFO_101 ServerInfo = ServerInfoList;
|
|||
|
BOOLEAN MisOrderedList = FALSE;
|
|||
|
|
|||
|
ASSERT (Level == 100 || Level == 101 || Level == 1010);
|
|||
|
|
|||
|
//
|
|||
|
// Figure out the size of each element.
|
|||
|
//
|
|||
|
|
|||
|
if (Level == 100) {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_100);
|
|||
|
} else {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_101);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Next check to see if the input list is sorted.
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0 ; i < ((LONG)EntriesInList - 1) ; i += 1 ) {
|
|||
|
PSERVER_INFO_101 NextServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
|
|||
|
|
|||
|
if (wcscmp(ServerInfo->sv101_name, NextServerInfo->sv101_name) >= 0) {
|
|||
|
MisOrderedList = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
ServerInfo = NextServerInfo;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This list is sorted. Return right away, it's fine.
|
|||
|
//
|
|||
|
|
|||
|
if (!MisOrderedList) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This list isn't sorted. We need to sort it.
|
|||
|
//
|
|||
|
|
|||
|
qsort(ServerInfoList, EntriesInList, ServerElementSize, CompareServerInfo);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
PINTERIM_ELEMENT
|
|||
|
AllocateInterimServerListEntry(
|
|||
|
IN PSERVER_INFO_101 ServerInfo,
|
|||
|
IN ULONG Level
|
|||
|
)
|
|||
|
{
|
|||
|
PINTERIM_ELEMENT NewElement;
|
|||
|
|
|||
|
NewElement = MIDL_user_allocate(sizeof(INTERIM_ELEMENT));
|
|||
|
|
|||
|
if (NewElement == NULL) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize TimeLastSeen and Periodicity.
|
|||
|
//
|
|||
|
|
|||
|
NewElement->TimeLastSeen = 0;
|
|||
|
|
|||
|
NewElement->Periodicity = 0;
|
|||
|
|
|||
|
NewElement->PlatformId = ServerInfo->sv101_platform_id;
|
|||
|
|
|||
|
ASSERT (wcslen(ServerInfo->sv101_name) <= CNLEN);
|
|||
|
|
|||
|
wcscpy(NewElement->Name, ServerInfo->sv101_name);
|
|||
|
|
|||
|
if (Level == 100) {
|
|||
|
NewElement->MajorVersionNumber = 0;
|
|||
|
NewElement->MinorVersionNumber = 0;
|
|||
|
*NewElement->Comment = L'\0';
|
|||
|
NewElement->Type = SV_TYPE_ALL;
|
|||
|
} else {
|
|||
|
NewElement->MajorVersionNumber = ServerInfo->sv101_version_major;
|
|||
|
|
|||
|
NewElement->MinorVersionNumber = ServerInfo->sv101_version_minor;
|
|||
|
|
|||
|
NewElement->Type = ServerInfo->sv101_type;
|
|||
|
|
|||
|
ASSERT (wcslen(ServerInfo->sv101_comment) <= LM20_MAXCOMMENTSZ);
|
|||
|
|
|||
|
wcscpy(NewElement->Comment, ServerInfo->sv101_comment);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return NewElement;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
UpdateInterimServerListElement(
|
|||
|
IN PINTERIM_SERVER_LIST InterimServerList,
|
|||
|
IN PINTERIM_ELEMENT InterimElement,
|
|||
|
IN ULONG Level,
|
|||
|
IN BOOLEAN NewElement
|
|||
|
)
|
|||
|
{
|
|||
|
#if 0
|
|||
|
PINTERIM_ELEMENT InterimEntry;
|
|||
|
ULONG TotalNeededForList = 0;
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// If this is a new element, update the size of the table to match.
|
|||
|
//
|
|||
|
|
|||
|
if (NewElement) {
|
|||
|
ULONG ServerElementSize;
|
|||
|
|
|||
|
if (Level == 100) {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_100);
|
|||
|
} else {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_101);
|
|||
|
}
|
|||
|
|
|||
|
InterimServerList->EntriesRead += 1;
|
|||
|
|
|||
|
ServerElementSize += wcslen(InterimElement->Name)*sizeof(WCHAR)+sizeof(WCHAR);
|
|||
|
|
|||
|
if (Level == 100) {
|
|||
|
ServerElementSize += sizeof(WCHAR);
|
|||
|
} else {
|
|||
|
ServerElementSize += wcslen(InterimElement->Comment)*sizeof(WCHAR)+sizeof(WCHAR);
|
|||
|
}
|
|||
|
|
|||
|
InterimServerList->TotalBytesNeeded += ServerElementSize;
|
|||
|
|
|||
|
InterimServerList->TotalEntries += 1;
|
|||
|
|
|||
|
if (InterimServerList->NewElementCallback != NULL) {
|
|||
|
InterimServerList->NewElementCallback(InterimServerList,
|
|||
|
InterimElement);
|
|||
|
} else {
|
|||
|
InterimElement->Periodicity = 0xffffffff;
|
|||
|
InterimElement->TimeLastSeen = 0xffffffff;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
if (InterimServerList->ExistingElementCallback != NULL) {
|
|||
|
InterimServerList->ExistingElementCallback(InterimServerList,
|
|||
|
InterimElement);
|
|||
|
} else {
|
|||
|
InterimElement->Periodicity = 0xffffffff;
|
|||
|
InterimElement->TimeLastSeen = 0xffffffff;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#if 0
|
|||
|
{
|
|||
|
PLIST_ENTRY InterimList;
|
|||
|
ULONG TotalNeededForList = 0;
|
|||
|
|
|||
|
for (InterimList = InterimServerList->ServerList.Flink ;
|
|||
|
InterimList != &InterimServerList->ServerList ;
|
|||
|
InterimList = InterimList->Flink ) {
|
|||
|
ULONG ServerElementSize;
|
|||
|
|
|||
|
InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
if (Level == 100) {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_100);
|
|||
|
} else {
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_101);
|
|||
|
}
|
|||
|
|
|||
|
ServerElementSize += wcslen(InterimEntry->Name)*sizeof(WCHAR)+sizeof(WCHAR);
|
|||
|
|
|||
|
ServerElementSize += wcslen(InterimEntry->Comment)*sizeof(WCHAR)+sizeof(WCHAR);
|
|||
|
|
|||
|
TotalNeededForList += ServerElementSize;
|
|||
|
}
|
|||
|
|
|||
|
if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
|
|||
|
KdPrint(("UpdateInterimServerList: Wrong number of bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
PackServerList(
|
|||
|
IN PINTERIM_SERVER_LIST InterimServerList,
|
|||
|
IN ULONG Level,
|
|||
|
IN ULONG ServerType,
|
|||
|
IN ULONG PreferedMaximumLength,
|
|||
|
OUT PVOID *ServerList,
|
|||
|
OUT PULONG EntriesRead,
|
|||
|
OUT PULONG TotalEntries,
|
|||
|
IN LPCWSTR FirstNameToReturn
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function will take an interim server list and "pack" it into an array
|
|||
|
of server_info_xxx structures.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PINTERIM_SERVER_LIST InterimServerList - Supplies an interim server list to merge into.
|
|||
|
|
|||
|
IN ULONG Level - Supplies the level of the list (100 or 101).
|
|||
|
|
|||
|
IN ULONG ServerType - Supplies the type to filter on the list.
|
|||
|
|
|||
|
IN ULONG PreferedMaximumLength - Supplies the prefered size of the list.
|
|||
|
|
|||
|
OUT PVOID *ServerList - Where to put the destination list.
|
|||
|
|
|||
|
OUT PULONG EntriesEntries - Receives the entries packed in the list.
|
|||
|
|
|||
|
OUT PULONG TotalEntries - Receives the total entries available in the list.
|
|||
|
|
|||
|
FirstNameToReturn - Supplies the name of the first domain or server entry to return.
|
|||
|
The caller can use this parameter to implement a resume handle of sorts by passing
|
|||
|
the name of the last entry returned on a previous call. (Notice that the specified
|
|||
|
entry will, also, be returned on this call unless it has since been deleted.)
|
|||
|
Pass NULL to start with the first entry available.
|
|||
|
|
|||
|
Passed name must be the canonical form of the name.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG EntriesPacked = 0;
|
|||
|
PLIST_ENTRY InterimList;
|
|||
|
PSERVER_INFO_101 ServerEntry;
|
|||
|
ULONG EntrySize = 0;
|
|||
|
LPWSTR BufferEnd;
|
|||
|
BOOLEAN ReturnWholeList = FALSE;
|
|||
|
BOOLEAN TrimmingNames;
|
|||
|
BOOLEAN BufferFull = FALSE;
|
|||
|
|
|||
|
if (Level == 100) {
|
|||
|
EntrySize = sizeof(SERVER_INFO_100);
|
|||
|
} else if (Level == 101) {
|
|||
|
EntrySize = sizeof(SERVER_INFO_101);
|
|||
|
} else {
|
|||
|
return(ERROR_INVALID_LEVEL);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the entries read based on the information we collected before.
|
|||
|
//
|
|||
|
|
|||
|
*TotalEntries = 0;
|
|||
|
|
|||
|
if (PreferedMaximumLength == 0xffffffff) {
|
|||
|
*ServerList = MIDL_user_allocate(InterimServerList->TotalBytesNeeded);
|
|||
|
|
|||
|
BufferEnd = (LPWSTR)((ULONG_PTR)(*ServerList)+InterimServerList->TotalBytesNeeded);
|
|||
|
|
|||
|
} else {
|
|||
|
*ServerList = MIDL_user_allocate(PreferedMaximumLength);
|
|||
|
|
|||
|
BufferEnd = (LPWSTR)((ULONG_PTR)(*ServerList)+PreferedMaximumLength);
|
|||
|
}
|
|||
|
|
|||
|
if (ServerType == SV_TYPE_ALL || ServerType == SV_TYPE_DOMAIN_ENUM) {
|
|||
|
ReturnWholeList = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if ( *ServerList == NULL ) {
|
|||
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
TrimmingNames = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0');
|
|||
|
ServerEntry = *ServerList;
|
|||
|
|
|||
|
for (InterimList = InterimServerList->ServerList.Flink ;
|
|||
|
InterimList != &InterimServerList->ServerList ;
|
|||
|
InterimList = InterimList->Flink ) {
|
|||
|
|
|||
|
PINTERIM_ELEMENT InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
#if DBG
|
|||
|
//
|
|||
|
// Make sure that this entry is lexically less than the next
|
|||
|
// entry.
|
|||
|
//
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY NextList = InterimList->Flink;
|
|||
|
PINTERIM_ELEMENT NextEntry = CONTAINING_RECORD(NextList, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
if (NextList != &InterimServerList->ServerList) {
|
|||
|
ASSERT (wcscmp(InterimEntry->Name, NextEntry->Name) < 0);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Trim the first several names from the list.
|
|||
|
//
|
|||
|
|
|||
|
if ( TrimmingNames ) {
|
|||
|
if ( wcscmp( InterimEntry->Name, FirstNameToReturn ) < 0 ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
TrimmingNames = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the server's type matches the type filter, pack it into the buffer.
|
|||
|
//
|
|||
|
|
|||
|
if (InterimEntry->Type & ServerType) {
|
|||
|
|
|||
|
(*TotalEntries) += 1;
|
|||
|
|
|||
|
//
|
|||
|
// If this entry will fit into the buffer, pack it in.
|
|||
|
//
|
|||
|
// Please note that we only count an entry if the entire entry
|
|||
|
// (server and comment) fits in the buffer. This is NOT
|
|||
|
// strictly Lan Manager compatible.
|
|||
|
//
|
|||
|
|
|||
|
if ( !BufferFull &&
|
|||
|
((ULONG_PTR)ServerEntry+EntrySize <= (ULONG_PTR)BufferEnd)) {
|
|||
|
|
|||
|
ServerEntry->sv101_platform_id = InterimEntry->PlatformId;
|
|||
|
|
|||
|
ServerEntry->sv101_name = InterimEntry->Name;
|
|||
|
|
|||
|
if (NetpPackString(&ServerEntry->sv101_name,
|
|||
|
(LPBYTE)((PCHAR)ServerEntry)+EntrySize,
|
|||
|
&BufferEnd)) {
|
|||
|
|
|||
|
if (Level == 101) {
|
|||
|
|
|||
|
ServerEntry->sv101_version_major = InterimEntry->MajorVersionNumber;;
|
|||
|
|
|||
|
ServerEntry->sv101_version_minor = InterimEntry->MinorVersionNumber;;
|
|||
|
|
|||
|
ServerEntry->sv101_type = InterimEntry->Type;
|
|||
|
|
|||
|
ServerEntry->sv101_comment = InterimEntry->Comment;
|
|||
|
|
|||
|
if (NetpPackString(&ServerEntry->sv101_comment,
|
|||
|
(LPBYTE)((PCHAR)ServerEntry)+EntrySize,
|
|||
|
&BufferEnd)) {
|
|||
|
EntriesPacked += 1;
|
|||
|
} else {
|
|||
|
BufferFull = TRUE;
|
|||
|
}
|
|||
|
} else {
|
|||
|
EntriesPacked += 1;
|
|||
|
}
|
|||
|
#if DBG
|
|||
|
{
|
|||
|
PSERVER_INFO_101 PreviousServerInfo = (PSERVER_INFO_101)((PCHAR)ServerEntry-EntrySize);
|
|||
|
if (PreviousServerInfo >= (PSERVER_INFO_101)*ServerList) {
|
|||
|
ASSERT (wcscmp(ServerEntry->sv101_name, PreviousServerInfo->sv101_name) > 0);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
#endif
|
|||
|
} else {
|
|||
|
BufferFull = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If we're returning the entire list and we have exceeded
|
|||
|
// the amount that fits in the list, we can early out
|
|||
|
// now.
|
|||
|
//
|
|||
|
|
|||
|
if (ReturnWholeList) {
|
|||
|
|
|||
|
*TotalEntries = InterimServerList->TotalEntries;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
BufferFull = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Step to the next server entry.
|
|||
|
//
|
|||
|
|
|||
|
ServerEntry = (PSERVER_INFO_101)((PCHAR)ServerEntry+EntrySize);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ASSERT (InterimServerList->EntriesRead >= EntriesPacked);
|
|||
|
|
|||
|
*EntriesRead = EntriesPacked;
|
|||
|
|
|||
|
if (EntriesPacked != *TotalEntries) {
|
|||
|
return ERROR_MORE_DATA;
|
|||
|
} else {
|
|||
|
return NERR_Success;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
InitializeInterimServerList(
|
|||
|
IN PINTERIM_SERVER_LIST InterimServerList,
|
|||
|
IN PINTERIM_NEW_CALLBACK NewCallback,
|
|||
|
IN PINTERIM_EXISTING_CALLBACK ExistingCallback,
|
|||
|
IN PINTERIM_DELETE_CALLBACK DeleteElementCallback,
|
|||
|
IN PINTERIM_AGE_CALLBACK AgeElementCallback
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
InitializeListHead(&InterimServerList->ServerList);
|
|||
|
|
|||
|
InterimServerList->TotalBytesNeeded = 0;
|
|||
|
InterimServerList->TotalEntries = 0;
|
|||
|
InterimServerList->EntriesRead = 0;
|
|||
|
|
|||
|
InterimServerList->NewElementCallback = NewCallback;
|
|||
|
InterimServerList->ExistingElementCallback = ExistingCallback;
|
|||
|
InterimServerList->DeleteElementCallback = DeleteElementCallback;
|
|||
|
InterimServerList->AgeElementCallback = AgeElementCallback;
|
|||
|
return(NERR_Success);
|
|||
|
}
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
UninitializeInterimServerList(
|
|||
|
IN PINTERIM_SERVER_LIST InterimServerList
|
|||
|
)
|
|||
|
{
|
|||
|
PINTERIM_ELEMENT InterimElement;
|
|||
|
|
|||
|
|
|||
|
// KdPrint(("BROWSER: Uninitialize Interim Server List %lx\n", InterimServerList));
|
|||
|
|
|||
|
//
|
|||
|
// Enumerate the elements in the table, deleting them as we go.
|
|||
|
//
|
|||
|
|
|||
|
while (!IsListEmpty(&InterimServerList->ServerList)) {
|
|||
|
PLIST_ENTRY Entry;
|
|||
|
|
|||
|
Entry = RemoveHeadList(&InterimServerList->ServerList);
|
|||
|
|
|||
|
InterimElement = CONTAINING_RECORD(Entry, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
if (InterimServerList->DeleteElementCallback != NULL) {
|
|||
|
InterimServerList->DeleteElementCallback(InterimServerList, InterimElement);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// There is one less element in the list.
|
|||
|
//
|
|||
|
|
|||
|
InterimServerList->EntriesRead -= 1;
|
|||
|
|
|||
|
InterimServerList->TotalEntries -= 1;
|
|||
|
|
|||
|
MIDL_user_free(InterimElement);
|
|||
|
}
|
|||
|
|
|||
|
ASSERT (InterimServerList->EntriesRead == 0);
|
|||
|
|
|||
|
return(NERR_Success);
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
NumberInterimServerListElements(
|
|||
|
IN PINTERIM_SERVER_LIST InterimServerList
|
|||
|
)
|
|||
|
{
|
|||
|
PLIST_ENTRY InterimList;
|
|||
|
ULONG NumberOfEntries = 0;
|
|||
|
|
|||
|
for (InterimList = InterimServerList->ServerList.Flink ;
|
|||
|
InterimList != &InterimServerList->ServerList ;
|
|||
|
InterimList = InterimList->Flink ) {
|
|||
|
NumberOfEntries += 1;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return NumberOfEntries;
|
|||
|
}
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
AgeInterimServerList(
|
|||
|
IN PINTERIM_SERVER_LIST InterimServerList
|
|||
|
)
|
|||
|
{
|
|||
|
PLIST_ENTRY InterimList, NextElement;
|
|||
|
PINTERIM_ELEMENT InterimElement;
|
|||
|
|
|||
|
if (InterimServerList->AgeElementCallback != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Enumerate the elements in the table, aging them as we go.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
for (InterimList = InterimServerList->ServerList.Flink ;
|
|||
|
InterimList != &InterimServerList->ServerList ;
|
|||
|
InterimList = NextElement) {
|
|||
|
InterimElement = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
//
|
|||
|
// Call into the aging routine and if this entry is too old,
|
|||
|
// remove it from the interim list.
|
|||
|
//
|
|||
|
|
|||
|
if (InterimServerList->AgeElementCallback(InterimServerList, InterimElement)) {
|
|||
|
ULONG ElementSize = sizeof(SERVER_INFO_101) + ((wcslen(InterimElement->Comment) + 1) * sizeof(WCHAR)) + ((wcslen(InterimElement->Name) + 1) * sizeof(WCHAR));
|
|||
|
|
|||
|
ASSERT (ElementSize <= InterimServerList->TotalBytesNeeded);
|
|||
|
|
|||
|
NextElement = InterimList->Flink;
|
|||
|
|
|||
|
//
|
|||
|
// Remove this entry from the list.
|
|||
|
//
|
|||
|
|
|||
|
RemoveEntryList(&InterimElement->NextElement);
|
|||
|
|
|||
|
if (InterimServerList->DeleteElementCallback != NULL) {
|
|||
|
InterimServerList->DeleteElementCallback(InterimServerList, InterimElement);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// There is one less element in the list.
|
|||
|
//
|
|||
|
|
|||
|
InterimServerList->EntriesRead -= 1;
|
|||
|
|
|||
|
InterimServerList->TotalEntries -= 1;
|
|||
|
|
|||
|
//
|
|||
|
// Since this element isn't in the table any more, we don't
|
|||
|
// need to allocate memory for it.
|
|||
|
//
|
|||
|
|
|||
|
InterimServerList->TotalBytesNeeded -= ElementSize;
|
|||
|
|
|||
|
MIDL_user_free(InterimElement);
|
|||
|
|
|||
|
} else {
|
|||
|
NextElement = InterimList->Flink;
|
|||
|
}
|
|||
|
}
|
|||
|
#if 0
|
|||
|
{
|
|||
|
PINTERIM_ELEMENT InterimEntry;
|
|||
|
ULONG TotalNeededForList = 0;
|
|||
|
|
|||
|
for (InterimList = InterimServerList->ServerList.Flink ;
|
|||
|
InterimList != &InterimServerList->ServerList ;
|
|||
|
InterimList = InterimList->Flink ) {
|
|||
|
ULONG ServerElementSize;
|
|||
|
|
|||
|
InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
|
|||
|
|
|||
|
ServerElementSize = sizeof(SERVER_INFO_101);
|
|||
|
|
|||
|
ServerElementSize += wcslen(InterimEntry->Name)*sizeof(WCHAR)+sizeof(WCHAR);
|
|||
|
|
|||
|
ServerElementSize += wcslen(InterimEntry->Comment)*sizeof(WCHAR)+sizeof(WCHAR);
|
|||
|
|
|||
|
TotalNeededForList += ServerElementSize;
|
|||
|
}
|
|||
|
|
|||
|
if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
|
|||
|
KdPrint(("AgeInterimServerList: Too few bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return(NERR_Success);
|
|||
|
}
|
|||
|
|
|||
|
PINTERIM_ELEMENT
|
|||
|
LookupInterimServerList(
|
|||
|
IN PINTERIM_SERVER_LIST InterimServerList,
|
|||
|
IN LPWSTR ServerNameToLookUp
|
|||
|
)
|
|||
|
{
|
|||
|
PLIST_ENTRY InterimList;
|
|||
|
|
|||
|
for (InterimList = InterimServerList->ServerList.Flink ;
|
|||
|
InterimList != &InterimServerList->ServerList ;
|
|||
|
InterimList = InterimList->Flink ) {
|
|||
|
|
|||
|
PINTERIM_ELEMENT InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
|
|||
|
LONG CompareResult;
|
|||
|
|
|||
|
if ((CompareResult = _wcsicmp(InterimEntry->Name, ServerNameToLookUp) == 0)) {
|
|||
|
return InterimEntry;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we went past this guy, return an error.
|
|||
|
//
|
|||
|
|
|||
|
if (CompareResult > 0) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|