You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3033 lines
92 KiB
3033 lines
92 KiB
/*++
|
|
|
|
Copyright (c) 1991-1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
srvenum.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the worker routine for the NetServerEnum API
|
|
implemented by the Workstation service.
|
|
|
|
Author:
|
|
|
|
Rita Wong (ritaw) 25-Mar-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
BrNetServerEnum(
|
|
IN PNETWORK Network OPTIONAL,
|
|
IN LPCWSTR ClientName OPTIONAL,
|
|
IN ULONG Level,
|
|
IN DWORD PreferedMaximumLength,
|
|
OUT PVOID *Buffer,
|
|
OUT LPDWORD EntriesRead,
|
|
OUT LPDWORD TotalEntries,
|
|
IN DWORD ServerType,
|
|
IN LPCWSTR Domain,
|
|
IN LPCWSTR FirstNameToReturn OPTIONAL
|
|
);
|
|
|
|
NET_API_STATUS
|
|
BrRetrieveServerListForMaster(
|
|
IN PNETWORK Network,
|
|
IN OUT PVOID *Buffer,
|
|
OUT PDWORD EntriesRead,
|
|
OUT PDWORD TotalEntries,
|
|
IN DWORD Level,
|
|
IN DWORD ServerType,
|
|
IN DWORD PreferedMaximumLength,
|
|
IN BOOLEAN LocalListOnly,
|
|
IN LPTSTR ClientName,
|
|
IN LPTSTR DomainName,
|
|
IN LPCWSTR FirstNameToReturn
|
|
);
|
|
|
|
NET_API_STATUS
|
|
BrRetrieveServerListForBackup(
|
|
IN PNETWORK Network,
|
|
IN OUT PVOID *Buffer,
|
|
OUT PDWORD EntriesRead,
|
|
OUT PDWORD TotalEntries,
|
|
IN DWORD Level,
|
|
IN DWORD ServerType,
|
|
IN DWORD PreferedMaximumLength,
|
|
IN LPCWSTR FirstNameToReturn
|
|
);
|
|
|
|
//
|
|
// This points to XsConvertServerEnumBuffer, which is dynamically loaded from
|
|
// xactsrv.dll when required
|
|
//
|
|
XS_CONVERT_SERVER_ENUM_BUFFER_FUNCTION *pXsConvertServerEnumBuffer = NULL;
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_BrowserrServerEnum(
|
|
IN LPTSTR ServerName OPTIONAL,
|
|
IN LPTSTR TransportName OPTIONAL,
|
|
IN LPTSTR ClientName OPTIONAL,
|
|
IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
|
|
IN DWORD PreferedMaximumLength,
|
|
OUT LPDWORD TotalEntries,
|
|
IN DWORD ServerType,
|
|
IN LPTSTR Domain,
|
|
IN OUT LPDWORD ResumeHandle OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the NetServerEnum entry point in the Workstation service.
|
|
|
|
Arguments:
|
|
|
|
ServerName - Supplies the name of server to execute this function
|
|
|
|
TransportName - Supplies the name of xport on which to enumerate servers
|
|
|
|
InfoStruct - This structure supplies the level of information requested,
|
|
returns a pointer to the buffer allocated by the Workstation service
|
|
which contains a sequence of information structure of the specified
|
|
information level, and returns the number of entries read. The buffer
|
|
pointer is set to NULL if return code is not NERR_Success or
|
|
ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
|
|
value is only valid if the return code is NERR_Success or
|
|
ERROR_MORE_DATA.
|
|
|
|
PreferedMaximumLength - Supplies the number of bytes of information
|
|
to return in the buffer. If this value is MAXULONG, we will try
|
|
to return all available information if there is enough memory
|
|
resource.
|
|
|
|
TotalEntries - Returns the total number of entries available. This value
|
|
is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
|
|
|
|
ServerType - Supplies the type of server to enumerate.
|
|
|
|
Domain - Supplies the name of one of the active domains to enumerate the
|
|
servers from. If NULL, servers from the primary domain, logon domain
|
|
and other domains are enumerated.
|
|
|
|
ResumeHandle - Supplies and returns the point to continue with enumeration.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
|
|
NetStatus = I_BrowserrServerEnumEx(
|
|
ServerName,
|
|
TransportName,
|
|
ClientName,
|
|
InfoStruct,
|
|
PreferedMaximumLength,
|
|
TotalEntries,
|
|
ServerType,
|
|
Domain,
|
|
NULL ); // NULL FirstNameToReturn
|
|
|
|
if (ARGUMENT_PRESENT(ResumeHandle)) {
|
|
*ResumeHandle = 0;
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_BrowserrServerEnumEx(
|
|
IN LPTSTR ServerName OPTIONAL,
|
|
IN LPTSTR TransportName OPTIONAL,
|
|
IN LPTSTR ClientName OPTIONAL,
|
|
IN OUT LPSERVER_ENUM_STRUCT InfoStruct,
|
|
IN DWORD PreferedMaximumLength,
|
|
OUT LPDWORD TotalEntries,
|
|
IN DWORD ServerType,
|
|
IN LPTSTR Domain,
|
|
IN LPTSTR FirstNameToReturnArg
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the NetServerEnum entry point in the Workstation service.
|
|
|
|
Arguments:
|
|
|
|
ServerName - Supplies the name of server to execute this function
|
|
|
|
TransportName - Supplies the name of xport on which to enumerate servers
|
|
|
|
InfoStruct - This structure supplies the level of information requested,
|
|
returns a pointer to the buffer allocated by the Workstation service
|
|
which contains a sequence of information structure of the specified
|
|
information level, and returns the number of entries read. The buffer
|
|
pointer is set to NULL if return code is not NERR_Success or
|
|
ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
|
|
value is only valid if the return code is NERR_Success or
|
|
ERROR_MORE_DATA.
|
|
|
|
PreferedMaximumLength - Supplies the number of bytes of information
|
|
to return in the buffer. If this value is MAXULONG, we will try
|
|
to return all available information if there is enough memory
|
|
resource.
|
|
|
|
TotalEntries - Returns the total number of entries available. This value
|
|
is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
|
|
|
|
ServerType - Supplies the type of server to enumerate.
|
|
|
|
Domain - Supplies the name of one of the active domains to enumerate the
|
|
servers from. If NULL, servers from the primary domain, logon domain
|
|
and other domains are enumerated.
|
|
|
|
FirstNameToReturnArg - 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.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS status;
|
|
PVOID Buffer = NULL;
|
|
ULONG EntriesRead;
|
|
BOOLEAN NetworkLocked = FALSE;
|
|
PNETWORK Network = NULL;
|
|
UNICODE_STRING NetworkName;
|
|
WCHAR FirstNameToReturn[DNLEN+1];
|
|
PDOMAIN_INFO DomainInfo = NULL;
|
|
#if DBG
|
|
DWORD StartTickCount, EndTickCount;
|
|
#endif
|
|
|
|
UNREFERENCED_PARAMETER(ServerName);
|
|
|
|
#if DBG
|
|
StartTickCount = GetTickCount();
|
|
#endif
|
|
|
|
BrPrint( (BR_CLIENT_OP,
|
|
"I_BrowserrServerEnumEx: called by client <%ws>, for domain <%ws>, type <%lx>, firstNameToReturn <%ws>, on Transport <%ws>\n",
|
|
ClientName, Domain, ServerType, FirstNameToReturnArg, TransportName ));
|
|
if (!ARGUMENT_PRESENT(TransportName)) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
if (!ARGUMENT_PRESENT(InfoStruct)) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( InfoStruct->Level == 101 &&
|
|
!InfoStruct->ServerInfo.Level101 ) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
} else if ( InfoStruct->Level == 100 &&
|
|
!InfoStruct->ServerInfo.Level100 ) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#ifdef ENABLE_PSEUDO_BROWSER
|
|
//
|
|
// Pseudo Server returns nothing
|
|
//
|
|
if (BrInfo.PseudoServerLevel == BROWSER_PSEUDO) {
|
|
if (InfoStruct->Level == 101) {
|
|
InfoStruct->ServerInfo.Level101->Buffer = NULL;
|
|
InfoStruct->ServerInfo.Level101->EntriesRead = 0;
|
|
} else {
|
|
InfoStruct->ServerInfo.Level100->Buffer = NULL;
|
|
InfoStruct->ServerInfo.Level100->EntriesRead = 0;
|
|
}
|
|
status = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// Find the requested domain.
|
|
//
|
|
|
|
DomainInfo = BrFindDomain( Domain, TRUE );
|
|
|
|
if ( DomainInfo == NULL) {
|
|
status = ERROR_NO_SUCH_DOMAIN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Find the requested network
|
|
//
|
|
|
|
RtlInitUnicodeString(&NetworkName, TransportName);
|
|
|
|
Network = BrFindNetwork( DomainInfo, &NetworkName);
|
|
|
|
if (Network == NULL) {
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: BrowserrServerEnum: Network not found.\n",
|
|
Domain,
|
|
TransportName));
|
|
status = ERROR_FILE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the caller has asked us to use the alternate transport,
|
|
// do so.
|
|
//
|
|
|
|
if ((ServerType != SV_TYPE_ALL) &&
|
|
(ServerType & SV_TYPE_ALTERNATE_XPORT) ) {
|
|
|
|
//
|
|
// If this transport has an alternate network, then actually
|
|
// query the alternate name, not the real one.
|
|
//
|
|
|
|
if (Network->AlternateNetwork != NULL) {
|
|
PNETWORK TempNetwork = Network;
|
|
Network = Network->AlternateNetwork;
|
|
if ( !BrReferenceNetwork( Network )) {
|
|
Network = TempNetwork;
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
BrDereferenceNetwork( TempNetwork );
|
|
BrPrint((BR_CLIENT_OP,
|
|
"I_BrowserrServerEnumEx: for client <%ws>, querying alternate network <%ws> instead of <%ws>\n",
|
|
ClientName, Network->NetworkName.Buffer, Network->AlternateNetwork->NetworkName.Buffer ));
|
|
} else {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ServerType &= ~SV_TYPE_ALTERNATE_XPORT;
|
|
|
|
if (ServerType == 0) {
|
|
ServerType = SV_TYPE_ALL;
|
|
}
|
|
|
|
}
|
|
|
|
if (!LOCK_NETWORK_SHARED(Network)) {
|
|
status = NERR_InternalError;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetworkLocked = TRUE;
|
|
|
|
if (!(Network->Role & (ROLE_BACKUP | ROLE_MASTER))) {
|
|
|
|
//
|
|
// If this is a wannish transport,
|
|
// and the caller is asking for "local list",
|
|
// try to find another wannish transport that is a master browser.
|
|
//
|
|
// The domain master browser doesn't have any control over which
|
|
// transport he comes in on. If he picks a disabled transport,
|
|
// this code will find the enabled transport.
|
|
//
|
|
// There are cases where there is more than one wannish master browser
|
|
// transport on this machine. In that case, BrNetServerEnum merges the
|
|
// local lists for all wannish transports to ensure that all list are returned
|
|
// to the domain master browser
|
|
//
|
|
|
|
if ( (Network->Flags & NETWORK_WANNISH) != 0 &&
|
|
(ServerType == SV_TYPE_LOCAL_LIST_ONLY ||
|
|
ServerType == (SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM) ) ) {
|
|
|
|
PNETWORK TempNetwork;
|
|
|
|
TempNetwork = BrFindWannishMasterBrowserNetwork( DomainInfo );
|
|
|
|
if ( TempNetwork == NULL ) {
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: Browse request received from %ws, but not backup or master\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
ClientName));
|
|
|
|
status = ERROR_REQ_NOT_ACCEP;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Ditch the old network.
|
|
//
|
|
UNLOCK_NETWORK(Network);
|
|
NetworkLocked = FALSE;
|
|
|
|
BrDereferenceNetwork( Network );
|
|
|
|
Network = TempNetwork;
|
|
|
|
//
|
|
// Use the new network
|
|
//
|
|
if (!LOCK_NETWORK_SHARED(Network)) {
|
|
status = NERR_InternalError;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetworkLocked = TRUE;
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Is wannish IP Network found for %ws\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
ClientName));
|
|
|
|
} else {
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: Browse request received from %ws, but not backup or master\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
ClientName));
|
|
|
|
status = ERROR_REQ_NOT_ACCEP;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Canonicalize the FirstNameToReturn.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
|
|
|
|
if ( I_NetNameCanonicalize(
|
|
NULL,
|
|
(LPWSTR) FirstNameToReturnArg,
|
|
FirstNameToReturn,
|
|
sizeof(FirstNameToReturn),
|
|
NAMETYPE_WORKGROUP,
|
|
LM2X_COMPATIBLE
|
|
) != NERR_Success) {
|
|
status = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else {
|
|
FirstNameToReturn[0] = L'\0';
|
|
}
|
|
|
|
status = BrNetServerEnum(Network,
|
|
ClientName,
|
|
InfoStruct->Level,
|
|
PreferedMaximumLength,
|
|
&Buffer,
|
|
&EntriesRead,
|
|
TotalEntries,
|
|
ServerType,
|
|
Domain,
|
|
FirstNameToReturn );
|
|
|
|
//
|
|
// Return output parameters other than output buffer from request packet.
|
|
//
|
|
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA) {
|
|
|
|
if (InfoStruct->Level == 101) {
|
|
InfoStruct->ServerInfo.Level101->Buffer = (PSERVER_INFO_101) Buffer;
|
|
InfoStruct->ServerInfo.Level101->EntriesRead = EntriesRead;
|
|
} else {
|
|
InfoStruct->ServerInfo.Level100->Buffer = (PSERVER_INFO_100) Buffer;
|
|
InfoStruct->ServerInfo.Level100->EntriesRead = EntriesRead;
|
|
}
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
if (NetworkLocked) {
|
|
UNLOCK_NETWORK(Network);
|
|
}
|
|
|
|
if ( Network != NULL ) {
|
|
|
|
#if DBG
|
|
EndTickCount = GetTickCount();
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Browse request for %ws took %ld milliseconds\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
ClientName,
|
|
EndTickCount - StartTickCount));
|
|
#endif
|
|
BrDereferenceNetwork( Network );
|
|
}
|
|
|
|
if ( DomainInfo != NULL ) {
|
|
BrDereferenceDomain( DomainInfo );
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
WORD
|
|
I_BrowserServerEnumForXactsrv(
|
|
IN LPCWSTR TransportName OPTIONAL,
|
|
IN LPCWSTR ClientName OPTIONAL,
|
|
|
|
IN ULONG Level,
|
|
IN USHORT ClientLevel,
|
|
|
|
IN PVOID ClientBuffer,
|
|
IN WORD BufferLength,
|
|
IN DWORD PreferedMaximumLength,
|
|
|
|
OUT LPDWORD EntriesFilled,
|
|
OUT LPDWORD TotalEntries,
|
|
|
|
IN DWORD ServerType,
|
|
IN LPCWSTR Domain,
|
|
IN LPCWSTR FirstNameToReturnArg OPTIONAL,
|
|
|
|
OUT PWORD Converter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is a private entrypoint for Xactsrv that bypasses RPC
|
|
entirely.
|
|
|
|
Arguments:
|
|
|
|
TransportName - Supplies the name of xport on which to enumerate servers
|
|
|
|
ClientName - Supplies the name of the client that requested the data
|
|
|
|
Level - Level of data requested.
|
|
ClientLevel - Level requested by the client.
|
|
|
|
ClientBuffer - Output buffer allocated to hold the buffer.
|
|
BufferLength - Size of ClientBuffer
|
|
PreferedMaximumLength - Prefered maximum size of Client buffer if we are
|
|
going to use the NT form of the buffer
|
|
|
|
OUT LPDWORD EntriesFilled - The entries packed into ClientBuffer
|
|
OUT LPDWORD TotalEntries - The total # of entries available.
|
|
|
|
IN DWORD ServerType - Server type mask.
|
|
IN LPTSTR Domain - Domain to query
|
|
|
|
OUT PWORD Converter - Magic constant from Xactsrv that allows the client
|
|
to convert the response buffer.
|
|
|
|
Return Value:
|
|
|
|
WORD - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS status;
|
|
BOOLEAN networkLocked = FALSE;
|
|
PNETWORK network = NULL;
|
|
UNICODE_STRING networkName;
|
|
PDOMAIN_INFO DomainInfo = NULL;
|
|
PVOID buffer = NULL;
|
|
DWORD entriesRead;
|
|
PCACHED_BROWSE_RESPONSE response = NULL;
|
|
WCHAR FirstNameToReturn[DNLEN+1];
|
|
|
|
#if DBG
|
|
DWORD startTickCount, endTickCount;
|
|
|
|
startTickCount = GetTickCount();
|
|
|
|
#endif
|
|
|
|
|
|
try {
|
|
|
|
|
|
BrPrint( (BR_CLIENT_OP,
|
|
"I_BrowserServerEnunForXactsrv: called by client <%ws>, domain <%ws>, type <%lx>, firstNameToReturn <%ws>, on Transport <%ws>\n",
|
|
ClientName, Domain, ServerType, FirstNameToReturnArg, TransportName ));
|
|
//
|
|
// If the browser isn't up and we get one of these calls, fail the API
|
|
// immediately.
|
|
//
|
|
|
|
if (BrGlobalData.Status.dwCurrentState != SERVICE_RUNNING) {
|
|
BrPrint(( BR_CRITICAL,
|
|
"Browse request from %ws received, but browser not running\n", ClientName));
|
|
try_return(status = NERR_ServiceNotInstalled);
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(TransportName)) {
|
|
try_return(status = ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Find the requested domain.
|
|
//
|
|
|
|
DomainInfo = BrFindDomain( (LPWSTR) Domain, TRUE );
|
|
|
|
if ( DomainInfo == NULL) {
|
|
try_return(status = ERROR_NO_SUCH_DOMAIN);
|
|
}
|
|
|
|
//
|
|
// Look up the transport.
|
|
//
|
|
RtlInitUnicodeString(&networkName, TransportName);
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: NetServerEnum: Look up network for %ws\n",
|
|
Domain,
|
|
TransportName,
|
|
ClientName));
|
|
network = BrFindNetwork( DomainInfo, &networkName);
|
|
|
|
//
|
|
// If it returns NULL, the network was not found.
|
|
// In that case just pick a network randomly and enumerate for that network
|
|
// This is a workaround for RAID bug 614688.
|
|
// The situation is that the request comes over NetbiosSMB and so
|
|
// networkName points to that, and browser is not registered on that transport.
|
|
//
|
|
if ( network == NULL) {
|
|
EnterCriticalSection(&NetworkCritSect);
|
|
|
|
if ( ServicedNetworks.Flink != &ServicedNetworks ) {
|
|
// If the list is not empty, just pick the first network
|
|
network = CONTAINING_RECORD(ServicedNetworks.Flink, NETWORK, NextNet);
|
|
network->ReferenceCount ++;
|
|
}
|
|
LeaveCriticalSection(&NetworkCritSect);
|
|
}
|
|
|
|
if (network == NULL) {
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: Network not found.\n",
|
|
Domain,
|
|
TransportName));
|
|
try_return(status = ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
|
|
//
|
|
// If the caller has asked us to use the alternate transport,
|
|
// do so.
|
|
//
|
|
|
|
if ((ServerType != SV_TYPE_ALL) &&
|
|
(ServerType & SV_TYPE_ALTERNATE_XPORT) ) {
|
|
|
|
//
|
|
// If this transport has an alternate network, then actually
|
|
// query the alternate name, not the real one.
|
|
//
|
|
|
|
if (network->AlternateNetwork != NULL) {
|
|
PNETWORK TempNetwork = network;
|
|
network = network->AlternateNetwork;
|
|
if ( !BrReferenceNetwork( network )) {
|
|
network = TempNetwork;
|
|
try_return(status = ERROR_INVALID_PARAMETER);
|
|
}
|
|
BrDereferenceNetwork( TempNetwork );
|
|
} else {
|
|
try_return(status = ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
ServerType &= ~SV_TYPE_ALTERNATE_XPORT;
|
|
|
|
if (ServerType == 0) {
|
|
ServerType = SV_TYPE_ALL;
|
|
}
|
|
|
|
}
|
|
|
|
if (!LOCK_NETWORK_SHARED(network)) {
|
|
try_return(status = NERR_InternalError);
|
|
}
|
|
|
|
networkLocked = TRUE;
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Network found for %ws\n",
|
|
network->DomainInfo->DomUnicodeDomainName,
|
|
network->NetworkName.Buffer,
|
|
ClientName));
|
|
|
|
if (!(network->Role & (ROLE_BACKUP | ROLE_MASTER))) {
|
|
|
|
//
|
|
// If this is a wannish transport,
|
|
// and the caller is asking for "local list",
|
|
// try to find another wannish transport that is a master browser.
|
|
//
|
|
// The domain master browser doesn't have any control over which
|
|
// transport he comes in on. If he picks a disabled transport,
|
|
// this code will find the enabled transport.
|
|
//
|
|
// There are cases where there is more than one wannish master browser
|
|
// transport on this machine. In that case, BrNetServerEnum merges the
|
|
// local lists for all wannish transports to ensure that all list are returned
|
|
// to the domain master browser
|
|
//
|
|
|
|
if ( (network->Flags & NETWORK_WANNISH) != 0 &&
|
|
(ServerType == SV_TYPE_LOCAL_LIST_ONLY ||
|
|
ServerType == (SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM) ) ) {
|
|
|
|
PNETWORK TempNetwork;
|
|
|
|
TempNetwork = BrFindWannishMasterBrowserNetwork( DomainInfo );
|
|
|
|
if ( TempNetwork == NULL ) {
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: Browse request received from %ws, but not backup or master\n",
|
|
network->DomainInfo->DomUnicodeDomainName,
|
|
network->NetworkName.Buffer,
|
|
ClientName));
|
|
|
|
try_return(status = ERROR_REQ_NOT_ACCEP);
|
|
}
|
|
|
|
|
|
//
|
|
// Ditch the old network.
|
|
//
|
|
UNLOCK_NETWORK(network);
|
|
networkLocked = FALSE;
|
|
|
|
BrDereferenceNetwork( network );
|
|
|
|
network = TempNetwork;
|
|
|
|
//
|
|
// Use the new network
|
|
//
|
|
if (!LOCK_NETWORK_SHARED(network)) {
|
|
try_return(status = NERR_InternalError);
|
|
}
|
|
|
|
networkLocked = TRUE;
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Is wannish IP Network found for %ws\n",
|
|
network->DomainInfo->DomUnicodeDomainName,
|
|
network->NetworkName.Buffer,
|
|
ClientName));
|
|
|
|
} else {
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: Browse request received from %ws, but not backup or master\n",
|
|
network->DomainInfo->DomUnicodeDomainName,
|
|
network->NetworkName.Buffer,
|
|
ClientName));
|
|
|
|
try_return(status = ERROR_REQ_NOT_ACCEP);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we weren't able to find a cached response buffer, or
|
|
// if the cached response didn't have a buffer allocated for it,
|
|
// we need to process the browse request.
|
|
//
|
|
|
|
if (network->Role & ROLE_MASTER) {
|
|
|
|
//
|
|
// Check to see if we should flush the cache at this time. If
|
|
// we should, we need to release the network and re-acquire it
|
|
// exclusively, because we use the network lock to protect
|
|
// enumerations from flushes.
|
|
//
|
|
|
|
|
|
if ((BrCurrentSystemTime() - network->TimeCacheFlushed) > BrInfo.DriverQueryFrequency) {
|
|
|
|
UNLOCK_NETWORK(network);
|
|
|
|
networkLocked = FALSE;
|
|
|
|
if (!LOCK_NETWORK(network)) {
|
|
try_return(status = NERR_InternalError);
|
|
}
|
|
|
|
networkLocked = TRUE;
|
|
|
|
//
|
|
// We're running on a master browser, and we found a cached browse
|
|
// request. See if we can safely use this cached value, or if we
|
|
// should go to the driver for it.
|
|
//
|
|
|
|
EnterCriticalSection(&network->ResponseCacheLock);
|
|
|
|
if ((BrCurrentSystemTime() - network->TimeCacheFlushed) > BrInfo.DriverQueryFrequency) {
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Flushing cache\n",
|
|
network->DomainInfo->DomUnicodeDomainName,
|
|
network->NetworkName.Buffer ));
|
|
|
|
network->TimeCacheFlushed = BrCurrentSystemTime();
|
|
|
|
BrAgeResponseCache( network );
|
|
}
|
|
|
|
LeaveCriticalSection(&network->ResponseCacheLock);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Canonicalize the FirstNameToReturn.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(FirstNameToReturnArg) && *FirstNameToReturnArg != L'\0') {
|
|
|
|
if ( I_NetNameCanonicalize(
|
|
NULL,
|
|
(LPWSTR) FirstNameToReturnArg,
|
|
FirstNameToReturn,
|
|
sizeof(FirstNameToReturn),
|
|
NAMETYPE_WORKGROUP,
|
|
LM2X_COMPATIBLE
|
|
) != NERR_Success) {
|
|
try_return(status = ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
} else {
|
|
FirstNameToReturn[0] = L'\0';
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(Domain) ||
|
|
!STRICMP(Domain, network->DomainInfo->DomUnicodeDomainName)) {
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Look up 0x%x/%d/%x.\n",
|
|
Domain, TransportName, ServerType, ClientLevel, BufferLength));
|
|
|
|
//
|
|
// This request is for our primary domain. Look up a cached response
|
|
// entry. If none is found for this request, allocate a new one and
|
|
// return it.
|
|
//
|
|
|
|
BrPrint( (BR_CLIENT_OP,
|
|
"I_BrowserServerEnunForXactsrv: try to look up a cached response for our primary domain <%ws> on network <%ws>\n",
|
|
Domain, network->NetworkName.Buffer ));
|
|
|
|
response = BrLookupAndAllocateCachedEntry(
|
|
network,
|
|
ServerType,
|
|
BufferLength,
|
|
ClientLevel,
|
|
FirstNameToReturn
|
|
);
|
|
|
|
|
|
}
|
|
|
|
EnterCriticalSection(&network->ResponseCacheLock);
|
|
if ((response == NULL)
|
|
|
|
||
|
|
|
|
(response->Buffer == NULL)) {
|
|
LeaveCriticalSection(&network->ResponseCacheLock);
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Cached entry not found, or hit count too low. Retrieve actual list for %ws\n",
|
|
Domain, TransportName, ClientName));
|
|
|
|
BrPrint( (BR_CLIENT_OP,
|
|
"I_BrowserServerEnunForXactsrv: retrieving actual list for client <%ws>, domain <%ws> on network <%ws>\n",
|
|
ClientName, Domain, network->NetworkName.Buffer ));
|
|
|
|
status = BrNetServerEnum(network,
|
|
ClientName,
|
|
Level,
|
|
PreferedMaximumLength,
|
|
&buffer,
|
|
&entriesRead,
|
|
TotalEntries,
|
|
ServerType,
|
|
Domain,
|
|
FirstNameToReturn
|
|
);
|
|
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA) {
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Convert NT buffer to Xactsrv buffer for %ws\n",
|
|
Domain,
|
|
TransportName,
|
|
ClientName ));
|
|
|
|
if( pXsConvertServerEnumBuffer == NULL ) {
|
|
//
|
|
// It doesn't really matter if several threads do this simultaneously.
|
|
// We are never going to unload this library anyway. But we are delaying
|
|
// the load of the library until now to speed up system boot and init.
|
|
//
|
|
|
|
HMODULE hLibrary = LoadLibrary( L"xactsrv.dll" );
|
|
if( hLibrary != NULL ) {
|
|
pXsConvertServerEnumBuffer = (XS_CONVERT_SERVER_ENUM_BUFFER_FUNCTION *)GetProcAddress(
|
|
hLibrary, "XsConvertServerEnumBuffer" );
|
|
}
|
|
}
|
|
|
|
if( pXsConvertServerEnumBuffer != NULL ) {
|
|
status = pXsConvertServerEnumBuffer(
|
|
buffer,
|
|
entriesRead,
|
|
TotalEntries,
|
|
ClientLevel,
|
|
ClientBuffer,
|
|
BufferLength,
|
|
EntriesFilled,
|
|
Converter);
|
|
} else {
|
|
status = GetLastError();
|
|
}
|
|
|
|
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA) {
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Conversion done for %ws\n",
|
|
Domain,
|
|
TransportName,
|
|
ClientName ));
|
|
|
|
EnterCriticalSection(&network->ResponseCacheLock);
|
|
|
|
if ((response != NULL) &&
|
|
|
|
(response->Buffer == NULL) &&
|
|
|
|
(response->TotalHitCount >= BrInfo.CacheHitLimit)) {
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Save contents of server list for 0x%x/%d/%x.\n",
|
|
Domain,
|
|
TransportName,
|
|
ServerType, ClientLevel, BufferLength));
|
|
|
|
response->Buffer = MIDL_user_allocate(BufferLength);
|
|
|
|
if ( response->Buffer != NULL ) {
|
|
|
|
//
|
|
// We successfully allocated the buffer, now copy
|
|
// our response into the buffer and save away the
|
|
// other useful stuff about the request.
|
|
//
|
|
|
|
RtlCopyMemory(response->Buffer, ClientBuffer, BufferLength);
|
|
|
|
response->EntriesRead = *EntriesFilled;
|
|
response->TotalEntries = *TotalEntries;
|
|
response->Converter = *Converter;
|
|
response->Status = (WORD)status;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&network->ResponseCacheLock);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
ASSERT (response);
|
|
|
|
ASSERT (response->Buffer);
|
|
|
|
ASSERT (response->Size == BufferLength);
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"Cache hit. Use contents of server list for 0x%x/%d/%x.\n",
|
|
Domain,
|
|
TransportName,
|
|
ServerType, ClientLevel, BufferLength));
|
|
|
|
//
|
|
// Copy over the cached response from the response cache into the
|
|
// users response buffer.
|
|
//
|
|
|
|
RtlCopyMemory(ClientBuffer, response->Buffer, BufferLength);
|
|
|
|
//
|
|
// Also copy over the other useful stuff that the client will
|
|
// want to know about.
|
|
//
|
|
|
|
*EntriesFilled = response->EntriesRead;
|
|
|
|
*TotalEntries = response->TotalEntries;
|
|
|
|
*Converter = response->Converter;
|
|
|
|
status = response->Status;
|
|
LeaveCriticalSection(&network->ResponseCacheLock);
|
|
|
|
}
|
|
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
if (networkLocked) {
|
|
UNLOCK_NETWORK(network);
|
|
}
|
|
|
|
//
|
|
// If a buffer was allocated, free it.
|
|
//
|
|
|
|
if (buffer != NULL) {
|
|
MIDL_user_free(buffer);
|
|
}
|
|
|
|
|
|
if ( network != NULL ) {
|
|
#if DBG
|
|
endTickCount = GetTickCount();
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Browse request for %ws took %ld milliseconds\n",
|
|
network->DomainInfo->DomUnicodeDomainName,
|
|
network->NetworkName.Buffer,
|
|
ClientName,
|
|
endTickCount - startTickCount));
|
|
#endif
|
|
BrDereferenceNetwork( network );
|
|
}
|
|
|
|
if ( DomainInfo != NULL ) {
|
|
BrDereferenceDomain( DomainInfo );
|
|
}
|
|
}
|
|
|
|
return (WORD)status;
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
BrNetServerEnum(
|
|
IN PNETWORK Network OPTIONAL,
|
|
IN LPCWSTR ClientName OPTIONAL,
|
|
IN ULONG Level,
|
|
IN DWORD PreferedMaximumLength,
|
|
OUT PVOID *Buffer,
|
|
OUT LPDWORD EntriesRead,
|
|
OUT LPDWORD TotalEntries,
|
|
IN DWORD ServerType,
|
|
IN LPCWSTR Domain,
|
|
IN LPCWSTR FirstNameToReturn OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the real worker for the NetServerEnum entry point in the
|
|
Workstation service.
|
|
|
|
Arguments:
|
|
|
|
ServerName - Supplies the name of server to execute this function
|
|
|
|
TransportName - Supplies the name of xport on which to enumerate servers
|
|
|
|
InfoStruct - This structure supplies the level of information requested,
|
|
returns a pointer to the buffer allocated by the Workstation service
|
|
which contains a sequence of information structure of the specified
|
|
information level, and returns the number of entries read. The buffer
|
|
pointer is set to NULL if return code is not NERR_Success or
|
|
ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
|
|
value is only valid if the return code is NERR_Success or
|
|
ERROR_MORE_DATA.
|
|
|
|
PreferedMaximumLength - Supplies the number of bytes of information
|
|
to return in the buffer. If this value is MAXULONG, we will try
|
|
to return all available information if there is enough memory
|
|
resource.
|
|
|
|
TotalEntries - Returns the total number of entries available. This value
|
|
is returned only if the return code is NERR_Success or ERROR_MORE_DATA.
|
|
|
|
ServerType - Supplies the type of server to enumerate.
|
|
|
|
Domain - Supplies the name of one of the active domains to enumerate the
|
|
servers from. If NULL, servers from the primary domain, logon domain
|
|
and other domains are enumerated.
|
|
|
|
FirstNameToReturn - Supplies the name of the first server or domain to return
|
|
to the caller. If NULL, enumeration begins with the very first entry.
|
|
|
|
Passed name must be the canonical form of the name.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS status;
|
|
DWORD DomainNameSize = 0;
|
|
TCHAR DomainName[DNLEN + 1];
|
|
BOOLEAN NetworkLocked = TRUE;
|
|
BOOLEAN LocalListOnly = FALSE;
|
|
PVOID DoubleHopLocalList = NULL;
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Retrieve browse list for %lx for client %ws\n",
|
|
Domain,
|
|
Network->NetworkName.Buffer,
|
|
ServerType,
|
|
ClientName));
|
|
|
|
EnterCriticalSection(&BrowserStatisticsLock);
|
|
|
|
if (ServerType == SV_TYPE_DOMAIN_ENUM) {
|
|
NumberOfDomainEnumerations += 1;
|
|
} else if (ServerType == SV_TYPE_ALL) {
|
|
NumberOfServerEnumerations += 1;
|
|
} else {
|
|
NumberOfOtherEnumerations += 1;
|
|
}
|
|
|
|
LeaveCriticalSection(&BrowserStatisticsLock);
|
|
|
|
//
|
|
// Only levels 100 and 101 are valid
|
|
//
|
|
|
|
if ((Level != 100) && (Level != 101)) {
|
|
return ERROR_INVALID_LEVEL;
|
|
}
|
|
|
|
#ifdef ENABLE_PSEUDO_BROWSER
|
|
//
|
|
// Pseudo Server shortcut
|
|
//
|
|
if ( BrInfo.PseudoServerLevel == BROWSER_PSEUDO ) {
|
|
*Buffer = NULL;
|
|
*EntriesRead = 0;
|
|
*TotalEntries = 0;
|
|
return NERR_Success;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
if (ARGUMENT_PRESENT(Domain)) {
|
|
|
|
//
|
|
// NAMETYPE_WORKGROUP allows spaces.
|
|
//
|
|
|
|
if ( I_NetNameCanonicalize(
|
|
NULL,
|
|
(LPWSTR) Domain,
|
|
DomainName,
|
|
(DNLEN + 1) * sizeof(TCHAR),
|
|
NAMETYPE_WORKGROUP,
|
|
0
|
|
) != NERR_Success) {
|
|
return ERROR_INVALID_DOMAINNAME;
|
|
}
|
|
|
|
DomainNameSize = STRLEN(DomainName) * sizeof(WCHAR);
|
|
}
|
|
|
|
try {
|
|
|
|
if (ServerType != SV_TYPE_ALL) {
|
|
|
|
//
|
|
// If the user has specified SV_TYPE_LOCAL_LIST_ONLY, we
|
|
// will only accept SV_TYPE_DOMAIN_ENUM or 0 as legal bits
|
|
// otherwise, we return an error.
|
|
//
|
|
|
|
if (ServerType & SV_TYPE_LOCAL_LIST_ONLY) {
|
|
|
|
LocalListOnly = TRUE;
|
|
|
|
// BrPrint(( BR_SERVER_ENUM, "Retrieve local list for %ws\n", ClientName));
|
|
|
|
|
|
//
|
|
// Turn off the LOCAL_LIST_ONLY bit.
|
|
//
|
|
|
|
ServerType &= ~SV_TYPE_LOCAL_LIST_ONLY;
|
|
|
|
//
|
|
// Check the remaining bits. The only two legal values
|
|
// are SV_TYPE_DOMAIN_ENUM and 0
|
|
//
|
|
|
|
if (ServerType != SV_TYPE_DOMAIN_ENUM) {
|
|
|
|
if (ServerType == 0) {
|
|
ServerType = SV_TYPE_ALL;
|
|
} else {
|
|
try_return(status = ERROR_INVALID_FUNCTION);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we aren't a master browser, blow this request
|
|
// off, since we don't have a local list.
|
|
//
|
|
|
|
if (!(Network->Role & ROLE_MASTER)) {
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: Local list request received from %ws, but not master\n",
|
|
Domain,
|
|
Network->NetworkName.Buffer,
|
|
ClientName));
|
|
try_return(status = ERROR_REQ_NOT_ACCEP);
|
|
}
|
|
|
|
} else if (ServerType & SV_TYPE_DOMAIN_ENUM) {
|
|
if (ServerType != SV_TYPE_DOMAIN_ENUM) {
|
|
try_return(status = ERROR_INVALID_FUNCTION);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(Domain) &&
|
|
STRICMP(Domain, Network->DomainInfo->DomUnicodeDomainName)) {
|
|
PINTERIM_ELEMENT DomainEntry;
|
|
LPWSTR MasterName = NULL;
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"Non local domain %ws - Check for double hop\n",
|
|
Domain));
|
|
|
|
if ( Network->Role & ROLE_MASTER ) {
|
|
if ( Network->DomainList.EntriesRead != 0 ) {
|
|
|
|
DomainEntry = LookupInterimServerList(&Network->DomainList, (LPWSTR) Domain);
|
|
|
|
if (DomainEntry != NULL) {
|
|
MasterName = DomainEntry->Comment;
|
|
}
|
|
} else {
|
|
ULONG i;
|
|
PSERVER_INFO_101 DomainInfo;
|
|
DWORD DoubleHopEntriesRead = 0;
|
|
DWORD DoubleHopTotalEntries = 0;
|
|
|
|
status = BrGetLocalBrowseList(Network,
|
|
NULL,
|
|
101,
|
|
SV_TYPE_DOMAIN_ENUM,
|
|
&DoubleHopLocalList,
|
|
&DoubleHopEntriesRead,
|
|
&DoubleHopTotalEntries);
|
|
|
|
for (i = 0 , DomainInfo = DoubleHopLocalList ;
|
|
|
|
i < DoubleHopEntriesRead ;
|
|
|
|
i += 1) {
|
|
|
|
if (!_wcsicmp(Domain, DomainInfo->sv101_name)) {
|
|
MasterName = DomainInfo->sv101_comment;
|
|
break;
|
|
}
|
|
|
|
DomainInfo += 1;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
ULONG i;
|
|
PSERVER_INFO_101 DomainInfo;
|
|
|
|
//
|
|
// We're running on a backup browser. We want to find the
|
|
// name of the master browser by looking in the backup
|
|
// server list for this network.
|
|
//
|
|
|
|
for (i = 0 , DomainInfo = Network->BackupDomainList ;
|
|
|
|
i < Network->TotalBackupDomainListEntries ;
|
|
|
|
i += 1) {
|
|
|
|
if (!_wcsicmp(Domain, DomainInfo->sv101_name)) {
|
|
MasterName = DomainInfo->sv101_comment;
|
|
break;
|
|
}
|
|
|
|
DomainInfo += 1;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we couldn't find a master name, bail out right now.
|
|
//
|
|
|
|
if (MasterName == NULL || *MasterName == UNICODE_NULL) {
|
|
try_return(status = ERROR_NO_BROWSER_SERVERS_FOUND);
|
|
}
|
|
|
|
// We also check if the MasterName is actually our current domain name
|
|
// There is a deadlock scenario that is being hit that is causing this
|
|
// to be the case which results in stalled SRV threads
|
|
if ( 0 == STRICMP(MasterName, Network->DomainInfo->DomUnicodeDomainName)) {
|
|
Domain = NULL;
|
|
goto get_local_list;
|
|
}
|
|
|
|
//
|
|
// If the master for this domain isn't listed as our
|
|
// current machine, remote the API to that server.
|
|
//
|
|
if (STRICMP(MasterName, Network->DomainInfo->DomUnicodeComputerName))
|
|
{
|
|
WCHAR RemoteComputerName[UNLEN+1];
|
|
|
|
//
|
|
// Build the name of the remote computer.
|
|
//
|
|
|
|
STRCPY(RemoteComputerName, TEXT("\\\\"));
|
|
|
|
STRCAT(RemoteComputerName, MasterName);
|
|
|
|
ASSERT (NetworkLocked);
|
|
|
|
UNLOCK_NETWORK(Network);
|
|
|
|
NetworkLocked = FALSE;
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"Double hop to %ws on %ws\n",
|
|
RemoteComputerName,
|
|
Network->NetworkName.Buffer));
|
|
BrPrint(( BR_CLIENT_OP,
|
|
"BrNetServerEnum: for client <%ws>, remoting the call to computer <%ws> on network <%ws>\n",
|
|
ClientName, RemoteComputerName, Network->NetworkName.Buffer ));
|
|
|
|
status = RxNetServerEnum(RemoteComputerName,
|
|
Network->NetworkName.Buffer,
|
|
Level,
|
|
(LPBYTE *)Buffer,
|
|
PreferedMaximumLength,
|
|
EntriesRead,
|
|
TotalEntries,
|
|
ServerType,
|
|
Domain,
|
|
FirstNameToReturn );
|
|
BrPrint(( BR_SERVER_ENUM, "Double hop done\n"));
|
|
|
|
if (!LOCK_NETWORK_SHARED (Network)) {
|
|
try_return(status = NERR_InternalError);
|
|
}
|
|
|
|
NetworkLocked = TRUE;
|
|
|
|
try_return(status);
|
|
}
|
|
}
|
|
|
|
get_local_list:
|
|
ASSERT (NetworkLocked);
|
|
|
|
if (!ARGUMENT_PRESENT(Domain)) {
|
|
STRCPY(DomainName, Network->DomainInfo->DomUnicodeDomainName);
|
|
}
|
|
|
|
//
|
|
// If we are running on the master browser, we want to retrieve the
|
|
// local list, merge it with our interim server list, and
|
|
// return that to the user.
|
|
//
|
|
|
|
if (Network->Role & ROLE_MASTER) {
|
|
BrPrint(( BR_CLIENT_OP,
|
|
"BrNetServerEnum: retrieving local server list as master browser for client <%ws> on network <%ws>\n",
|
|
ClientName, Network->NetworkName.Buffer ));
|
|
status = BrRetrieveServerListForMaster(Network,
|
|
Buffer,
|
|
EntriesRead,
|
|
TotalEntries,
|
|
Level,
|
|
ServerType,
|
|
PreferedMaximumLength,
|
|
LocalListOnly,
|
|
(LPWSTR) ClientName,
|
|
DomainName,
|
|
FirstNameToReturn );
|
|
|
|
|
|
|
|
} else {
|
|
|
|
BrPrint(( BR_CLIENT_OP,
|
|
"BrNetServerEnum: retrieving local server list as backup browser for client <%ws> on network <%ws>\n",
|
|
ClientName, Network->NetworkName.Buffer ));
|
|
status = BrRetrieveServerListForBackup(Network,
|
|
Buffer,
|
|
EntriesRead,
|
|
TotalEntries,
|
|
Level,
|
|
ServerType,
|
|
PreferedMaximumLength,
|
|
FirstNameToReturn );
|
|
|
|
|
|
}
|
|
|
|
try_return(status);
|
|
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
|
|
#if DBG
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA) {
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Returning Browse list for %lx with %ld entries for %ws\n",
|
|
Domain,
|
|
Network->NetworkName.Buffer,
|
|
ServerType,
|
|
*EntriesRead,
|
|
ClientName));
|
|
} else {
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: Failing I_BrowserServerEnum: %ld for client %ws\n",
|
|
Domain,
|
|
Network->NetworkName.Buffer,
|
|
status, ClientName));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If we used the local driver's list to retrieve the list of
|
|
// domains, free up the buffer.
|
|
//
|
|
|
|
if (DoubleHopLocalList != NULL) {
|
|
MIDL_user_free(DoubleHopLocalList);
|
|
|
|
}
|
|
|
|
ASSERT (NetworkLocked);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
TrimServerList(
|
|
IN DWORD Level,
|
|
IN OUT LPBYTE *Buffer,
|
|
IN OUT LPDWORD EntriesRead,
|
|
IN OUT LPDWORD TotalEntries,
|
|
IN LPCWSTR FirstNameToReturn
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine trims any name from Buffer that's less than FirstNameToReturn.
|
|
|
|
The routine is implemented by moving the fixed part of the kept entries to the beginning
|
|
of the allocated buffer. Thay way, the pointer contained in the entries need not be
|
|
relocated and the original buffer can still be used.
|
|
|
|
Arguments:
|
|
|
|
Level - Level of information in the buffer.
|
|
|
|
Buffer - Pointer to address of buffer to trim.
|
|
Returns null (and deallocates the buffer) if all the entries are trimmed.
|
|
|
|
EntriesRead - Pointer to number of entries in the buffer.
|
|
Returns the number of entries remaining in the buffer after triming.
|
|
|
|
TotalEntries - Pointer to number of entries available from server.
|
|
Returns a number reduced by the number of trimmed entries.
|
|
|
|
FirstNameToReturn - Supplies the name of the first server or domain to return
|
|
to the caller. If NULL, enumeration begins with the very first entry.
|
|
|
|
Passed name must be the canonical form of the name.
|
|
|
|
Return Value:
|
|
|
|
Returns the error code for the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD EntryCount;
|
|
DWORD EntryNumber;
|
|
DWORD FixedSize;
|
|
|
|
LPBYTE CurrentEntry;
|
|
|
|
//
|
|
// Early out if there's nothing to do.
|
|
//
|
|
|
|
if ( FirstNameToReturn == NULL || *FirstNameToReturn == L'\0' || *EntriesRead == 0 ) {
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Compute the size of each entry.
|
|
//
|
|
|
|
switch (Level) {
|
|
case 100:
|
|
FixedSize = sizeof(SERVER_INFO_100);
|
|
break;
|
|
case 101:
|
|
FixedSize = sizeof(SERVER_INFO_101);
|
|
break;
|
|
default:
|
|
NetpAssert( FALSE );
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Finding the first entry to return
|
|
//
|
|
|
|
EntryCount = *EntriesRead;
|
|
|
|
for ( EntryNumber=0; EntryNumber< EntryCount; EntryNumber++ ) {
|
|
|
|
LPSERVER_INFO_100 ServerEntry =
|
|
(LPSERVER_INFO_100)( *Buffer + FixedSize * EntryNumber);
|
|
|
|
//
|
|
// Found the first entry to return.
|
|
//
|
|
|
|
if ( STRCMP( ServerEntry->sv100_name, FirstNameToReturn ) >= 0 ) {
|
|
|
|
//
|
|
// If we're returning the whole list,
|
|
// simply return.
|
|
//
|
|
|
|
if ( ServerEntry == (LPSERVER_INFO_100)(*Buffer) ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Copy the remaining entries to the beginning of the buffer.
|
|
// (Yes, this is an overlapping copy)
|
|
//
|
|
|
|
RtlMoveMemory( *Buffer, ServerEntry, (*EntriesRead) * FixedSize );
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Account for skipped entries.
|
|
//
|
|
|
|
*EntriesRead -= 1;
|
|
*TotalEntries -= 1;
|
|
}
|
|
|
|
//
|
|
// If no entries should be returned,
|
|
// deallocate the buffer.
|
|
//
|
|
|
|
NetApiBufferFree( *Buffer );
|
|
*Buffer = NULL;
|
|
|
|
ASSERT ( *EntriesRead == 0 );
|
|
|
|
return;
|
|
|
|
} // TrimServerList
|
|
|
|
//
|
|
// Context for building a local list of all the Wannish transports.
|
|
//
|
|
|
|
typedef struct _BR_LOCAL_LIST_CONTEXT {
|
|
LPWSTR DomainName;
|
|
DWORD Level;
|
|
DWORD ServerType;
|
|
PVOID Buffer;
|
|
NET_API_STATUS NetStatus;
|
|
DWORD EntriesRead;
|
|
DWORD TotalEntries;
|
|
BOOLEAN AtLeastOneWorked;
|
|
|
|
INTERIM_SERVER_LIST InterimServerList;
|
|
} BR_LOCAL_LIST_CONTEXT, *PBR_LOCAL_LIST_CONTEXT;
|
|
|
|
|
|
NET_API_STATUS
|
|
BrGetWannishLocalList(
|
|
IN PNETWORK Network,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker function to get the local list for all WANNISH networks and
|
|
merge them together.
|
|
|
|
Arguments:
|
|
|
|
Network - The current network to do.
|
|
|
|
Context - Context that the accumulated list is built into.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
PBR_LOCAL_LIST_CONTEXT LocalListContext = (PBR_LOCAL_LIST_CONTEXT) Context;
|
|
|
|
PVOID Buffer = NULL;
|
|
DWORD EntriesRead;
|
|
DWORD TotalEntries;
|
|
|
|
//
|
|
// If this isn't a wannish network,
|
|
// ignore it.
|
|
|
|
if ((Network->Flags & NETWORK_WANNISH) == 0 ) {
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: isn't a wannish network. (ignored)\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer ));
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Get the local list from this transport.
|
|
//
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Get local list from wannish network.\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer ));
|
|
|
|
NetStatus = BrGetLocalBrowseList(
|
|
Network,
|
|
LocalListContext->DomainName,
|
|
LocalListContext->Level,
|
|
LocalListContext->ServerType,
|
|
&Buffer,
|
|
&EntriesRead,
|
|
&TotalEntries);
|
|
|
|
if ( NetStatus != NO_ERROR && NetStatus != ERROR_MORE_DATA ) {
|
|
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: Get local list from wannish network failed: %ld.\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
NetStatus ));
|
|
//
|
|
// If no transport has worked yet, save this as the new default status.
|
|
// (But keep on going)
|
|
//
|
|
if ( !LocalListContext->AtLeastOneWorked ) {
|
|
LocalListContext->NetStatus = NetStatus;
|
|
}
|
|
NetStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalListContext->AtLeastOneWorked = TRUE;
|
|
LocalListContext->NetStatus = NetStatus;
|
|
|
|
//
|
|
// If no data was returned,
|
|
// we're done.
|
|
//
|
|
|
|
if ( EntriesRead == 0 ) {
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Get local list from wannish network got zero entries.\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer ));
|
|
NetStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If a prior network already returned some entries,
|
|
// build an interim list to merge both lists into.
|
|
//
|
|
|
|
if ( LocalListContext->Buffer != NULL ) {
|
|
NetStatus = MergeServerList(
|
|
&LocalListContext->InterimServerList,
|
|
LocalListContext->Level,
|
|
LocalListContext->Buffer,
|
|
LocalListContext->EntriesRead,
|
|
LocalListContext->TotalEntries );
|
|
|
|
MIDL_user_free(LocalListContext->Buffer);
|
|
LocalListContext->Buffer = NULL;
|
|
LocalListContext->EntriesRead = 0;
|
|
LocalListContext->TotalEntries = 0;;
|
|
|
|
if ( NetStatus != NO_ERROR ) {
|
|
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: merge local list from wannish network failed: %ld.\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
NetStatus ));
|
|
goto Cleanup;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is an interim server list,
|
|
// add the new entries to it.
|
|
//
|
|
|
|
if ( LocalListContext->InterimServerList.TotalEntries != 0 ) {
|
|
NetStatus = MergeServerList(
|
|
&LocalListContext->InterimServerList,
|
|
LocalListContext->Level,
|
|
Buffer,
|
|
EntriesRead,
|
|
TotalEntries );
|
|
|
|
if ( NetStatus != NO_ERROR ) {
|
|
|
|
BrPrint(( BR_CRITICAL,
|
|
"%ws: %ws: merge local list from wannish network failed: %ld.\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
NetStatus ));
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is the first network to return entries,
|
|
// simply save the list.
|
|
//
|
|
// This saves us from having to convert a list to an interim list just
|
|
// to have to convert it back in those cases where we have a single wannish
|
|
// network.
|
|
//
|
|
} else {
|
|
LocalListContext->Buffer = Buffer;
|
|
LocalListContext->EntriesRead = EntriesRead;
|
|
LocalListContext->TotalEntries = TotalEntries;
|
|
Buffer = NULL;
|
|
}
|
|
|
|
NetStatus = NO_ERROR;
|
|
|
|
|
|
|
|
Cleanup:
|
|
if ( Buffer != NULL ) {
|
|
MIDL_user_free(Buffer);
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
BrRetrieveServerListForMaster(
|
|
IN PNETWORK Network,
|
|
IN OUT PVOID *Buffer,
|
|
OUT PDWORD EntriesRead,
|
|
OUT PDWORD TotalEntries,
|
|
IN DWORD Level,
|
|
IN DWORD ServerType,
|
|
IN DWORD PreferedMaximumLength,
|
|
IN BOOLEAN LocalListOnly,
|
|
IN LPTSTR ClientName,
|
|
IN LPTSTR DomainName,
|
|
IN LPCWSTR FirstNameToReturn
|
|
)
|
|
{
|
|
BOOLEAN GetLocalList = FALSE;
|
|
NET_API_STATUS status;
|
|
BOOLEAN EarlyOut = FALSE;
|
|
|
|
//
|
|
// Determine if it is appropriate that we should early out after retrieving
|
|
// the list of servers (or domains) from the bowser.
|
|
//
|
|
// We will early out on the following critieria:
|
|
//
|
|
// 1) If we are requested to retrieve the local list on a Wannish xport
|
|
// 2) If the transport isn't a wannish transport, or
|
|
// 3) If this domain isn't our primary domain (in which case it's an
|
|
// "otherdomain" request).
|
|
//
|
|
|
|
if (LocalListOnly || STRICMP(DomainName, Network->DomainInfo->DomUnicodeDomainName)) {
|
|
EarlyOut = TRUE;
|
|
} else if (!(Network->Flags & NETWORK_WANNISH)) {
|
|
|
|
//
|
|
// This isn't a wannish transport. We need to see if we've been
|
|
// master long enough for the driver to have retrieved a reasonable
|
|
// list.
|
|
//
|
|
// The server and domain table will be emptied when the first master
|
|
// browser timer is run. This will happen 15 minutes after we
|
|
// become the master, so we will use our services list for the
|
|
// first 15 minutes the browser is master.
|
|
//
|
|
|
|
if (ServerType == SV_TYPE_DOMAIN_ENUM) {
|
|
if (Network->DomainList.EntriesRead == 0) {
|
|
EarlyOut = TRUE;
|
|
}
|
|
} else {
|
|
if (Network->BrowseTable.EntriesRead == 0) {
|
|
EarlyOut = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (EarlyOut) {
|
|
|
|
GetLocalList = TRUE;
|
|
|
|
} else if (ServerType == SV_TYPE_ALL) {
|
|
|
|
if ((BrCurrentSystemTime() - Network->LastBowserServerQueried) > BrInfo.DriverQueryFrequency ) {
|
|
|
|
GetLocalList = TRUE;
|
|
}
|
|
|
|
} else if (ServerType == SV_TYPE_DOMAIN_ENUM) {
|
|
|
|
if ((BrCurrentSystemTime() - Network->LastBowserDomainQueried) > BrInfo.DriverQueryFrequency ) {
|
|
|
|
GetLocalList = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
GetLocalList = TRUE;
|
|
}
|
|
|
|
if (GetLocalList && !EarlyOut) {
|
|
|
|
//
|
|
// If we're retriving the list from the driver, and can't early out
|
|
// this request, this means that we will be merging this server list
|
|
// with an interim server list. This means that we will be modifying
|
|
// the network structure, and thus that we need to re-lock the network
|
|
// structure.
|
|
//
|
|
|
|
UNLOCK_NETWORK(Network);
|
|
|
|
if (!LOCK_NETWORK(Network)) {
|
|
return NERR_InternalError;
|
|
}
|
|
|
|
//
|
|
// Now re-test to see if another thread came in and retrieved the
|
|
// list from the driver while we had the network unlocked. If so,
|
|
// we don't want to get the local list any more.
|
|
//
|
|
// Otherwise, we want to update the last query time.
|
|
//
|
|
|
|
if (ServerType == SV_TYPE_ALL) {
|
|
|
|
if ((BrCurrentSystemTime() - Network->LastBowserServerQueried) > BrInfo.DriverQueryFrequency ) {
|
|
Network->LastBowserServerQueried = BrCurrentSystemTime();
|
|
} else {
|
|
GetLocalList = FALSE;
|
|
}
|
|
|
|
} else if (ServerType == SV_TYPE_DOMAIN_ENUM) {
|
|
|
|
if ((BrCurrentSystemTime() - Network->LastBowserDomainQueried) > BrInfo.DriverQueryFrequency ) {
|
|
Network->LastBowserDomainQueried = BrCurrentSystemTime();
|
|
} else {
|
|
GetLocalList = FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
Network->LastBowserServerQueried = BrCurrentSystemTime();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we're supposed to retrieve the local server list, retrieve it.
|
|
//
|
|
|
|
if (GetLocalList) {
|
|
DWORD ServerTypeForLocalList;
|
|
|
|
//
|
|
// Calculate the server type to use when retrieving the list from
|
|
// the driver.
|
|
//
|
|
|
|
if (LocalListOnly ||
|
|
|
|
(ServerType == SV_TYPE_DOMAIN_ENUM) ||
|
|
|
|
!(Network->Flags & NETWORK_WANNISH)) {
|
|
|
|
//
|
|
// If we are retrieving the local list, or this is a non-wannish
|
|
// transport, or we are asking for domains, we want to
|
|
// keep the callers server type.
|
|
//
|
|
|
|
ServerTypeForLocalList = ServerType;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This must be a wannish transport, ask for all servers (we know
|
|
// that we're not asking for domains, since we checked that
|
|
// above).
|
|
//
|
|
// We ask for all the servers because it's possible that a servers
|
|
// role has changed.
|
|
//
|
|
|
|
ASSERT (Network->Flags & NETWORK_WANNISH);
|
|
|
|
ServerTypeForLocalList = SV_TYPE_ALL;
|
|
}
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Get local browse list for %ws\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
ClientName));
|
|
|
|
//
|
|
// If this is a call from the Domain Master Browser to get the local list,
|
|
// and this is a wannish transport,
|
|
// get the local list on all wannish transports.
|
|
//
|
|
|
|
if ( LocalListOnly &&
|
|
(Network->Flags & NETWORK_WANNISH) != 0 ) {
|
|
BR_LOCAL_LIST_CONTEXT LocalListContext;
|
|
//
|
|
// Build a context to use to gather the server lists from all
|
|
// wannish transports
|
|
//
|
|
LocalListContext.DomainName = DomainName;
|
|
LocalListContext.Level = Level;
|
|
LocalListContext.ServerType = ServerTypeForLocalList;
|
|
LocalListContext.Buffer = NULL;
|
|
LocalListContext.NetStatus = NO_ERROR;
|
|
LocalListContext.EntriesRead = 0;
|
|
LocalListContext.TotalEntries = 0;
|
|
LocalListContext.AtLeastOneWorked = FALSE;
|
|
|
|
(VOID) InitializeInterimServerList(&LocalListContext.InterimServerList, NULL, NULL, NULL, NULL);
|
|
|
|
//
|
|
// Gather the lists
|
|
//
|
|
|
|
status = BrEnumerateNetworks( BrGetWannishLocalList, &LocalListContext );
|
|
|
|
if ( status == NO_ERROR ) {
|
|
|
|
//
|
|
// If multiple networks were found,
|
|
// pack the interim list.
|
|
//
|
|
|
|
if ( LocalListContext.InterimServerList.TotalEntries != 0 ) {
|
|
status = PackServerList( &LocalListContext.InterimServerList,
|
|
LocalListContext.Level,
|
|
LocalListContext.ServerType,
|
|
PreferedMaximumLength,
|
|
Buffer,
|
|
EntriesRead,
|
|
TotalEntries,
|
|
(LPWSTR)FirstNameToReturn );
|
|
|
|
//
|
|
// If just a single network was found,
|
|
// just use it.
|
|
//
|
|
} else {
|
|
status = LocalListContext.NetStatus;
|
|
*Buffer = LocalListContext.Buffer;
|
|
*EntriesRead = LocalListContext.EntriesRead;
|
|
*TotalEntries = LocalListContext.TotalEntries;
|
|
}
|
|
|
|
}
|
|
|
|
(VOID) UninitializeInterimServerList( &LocalListContext.InterimServerList );
|
|
|
|
|
|
} else {
|
|
status = BrGetLocalBrowseList(
|
|
Network,
|
|
DomainName,
|
|
( EarlyOut ? Level : 101 ),
|
|
ServerTypeForLocalList,
|
|
Buffer,
|
|
EntriesRead,
|
|
TotalEntries);
|
|
}
|
|
|
|
// BrPrint(( BR_SERVER_ENUM, "List retrieved. %ld entries, %ld total\n", *EntriesRead, *TotalEntries));
|
|
|
|
|
|
//
|
|
// If we're supposed to early-out this request (or if there was
|
|
// an error), do so now.
|
|
//
|
|
|
|
if (EarlyOut ||
|
|
(status != NERR_Success && status != ERROR_MORE_DATA)) {
|
|
|
|
//
|
|
// If we're returning an early out list,
|
|
// truncate the complete list returned from the kernel.
|
|
//
|
|
// This saves us from having to modify the kernel interface and untangle
|
|
// the code above.
|
|
//
|
|
|
|
if ( status == NERR_Success || status == ERROR_MORE_DATA ) {
|
|
|
|
TrimServerList( Level,
|
|
(LPBYTE *)Buffer,
|
|
EntriesRead,
|
|
TotalEntries,
|
|
FirstNameToReturn );
|
|
|
|
}
|
|
|
|
BrPrint(( BR_SERVER_ENUM, "Early out for %ws with %ld servers. Don't merge server list.\n", ClientName, *EntriesRead));
|
|
|
|
return status;
|
|
}
|
|
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA) {
|
|
|
|
if (*EntriesRead != 0) {
|
|
|
|
//
|
|
// Merge the local list with the list we got from the
|
|
// master or from the domain master.
|
|
//
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Merge %d entries in server list for %ws \n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
*EntriesRead,
|
|
ClientName));
|
|
|
|
status = MergeServerList((ServerType == SV_TYPE_DOMAIN_ENUM ?
|
|
&Network->DomainList :
|
|
&Network->BrowseTable),
|
|
101,
|
|
*Buffer,
|
|
*EntriesRead,
|
|
*TotalEntries
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We've merged the local list into the appropriate interim table,
|
|
// now free it up.
|
|
//
|
|
|
|
if (*EntriesRead != 0) {
|
|
MIDL_user_free(*Buffer);
|
|
}
|
|
|
|
status = PackServerList((ServerType == SV_TYPE_DOMAIN_ENUM ?
|
|
&Network->DomainList :
|
|
&Network->BrowseTable),
|
|
Level,
|
|
ServerType,
|
|
PreferedMaximumLength,
|
|
Buffer,
|
|
EntriesRead,
|
|
TotalEntries,
|
|
FirstNameToReturn
|
|
);
|
|
return status;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
BrRetrieveServerListForBackup(
|
|
IN PNETWORK Network,
|
|
IN OUT PVOID *Buffer,
|
|
OUT PDWORD EntriesRead,
|
|
OUT PDWORD TotalEntries,
|
|
IN DWORD Level,
|
|
IN DWORD ServerType,
|
|
IN DWORD PreferedMaximumLength,
|
|
IN LPCWSTR FirstNameToReturn
|
|
)
|
|
{
|
|
PSERVER_INFO_101 ServerList, ClientServerInfo;
|
|
ULONG EntriesInList;
|
|
ULONG TotalEntriesInList;
|
|
ULONG EntrySize;
|
|
ULONG BufferSize;
|
|
LPTSTR BufferEnd;
|
|
BOOLEAN ReturnWholeList = FALSE;
|
|
BOOLEAN TrimmingNames;
|
|
BOOLEAN BufferFull = FALSE; // see bug 427656
|
|
|
|
//
|
|
// If we are not running as a master, we want to use our stored
|
|
// server list to figure out what the client gets.
|
|
//
|
|
|
|
if (ServerType == SV_TYPE_DOMAIN_ENUM) {
|
|
|
|
ServerList = Network->BackupDomainList;
|
|
|
|
TotalEntriesInList = EntriesInList = Network->TotalBackupDomainListEntries;
|
|
|
|
ReturnWholeList = TRUE;
|
|
|
|
} else {
|
|
ServerList = Network->BackupServerList;
|
|
|
|
TotalEntriesInList = EntriesInList = Network->TotalBackupServerListEntries;
|
|
|
|
if (ServerType == SV_TYPE_ALL) {
|
|
ReturnWholeList = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Figure out the largest buffer we have to allocate to hold this
|
|
// server info.
|
|
//
|
|
|
|
if (Level == 101) {
|
|
if (PreferedMaximumLength == MAXULONG) {
|
|
|
|
if (ServerType == SV_TYPE_DOMAIN_ENUM) {
|
|
BufferSize = (sizeof(SERVER_INFO_101) + (CNLEN+1 + CNLEN+1)*sizeof(TCHAR)) * EntriesInList;
|
|
} else {
|
|
BufferSize = (sizeof(SERVER_INFO_101) + (CNLEN+1 + LM20_MAXCOMMENTSZ+1)*sizeof(TCHAR)) * EntriesInList;
|
|
}
|
|
} else {
|
|
BufferSize = PreferedMaximumLength;
|
|
}
|
|
|
|
EntrySize = sizeof(SERVER_INFO_101);
|
|
} else {
|
|
if (PreferedMaximumLength == MAXULONG) {
|
|
BufferSize = (sizeof(SERVER_INFO_100) + (CNLEN+1)*sizeof(TCHAR)) * EntriesInList;
|
|
} else {
|
|
BufferSize = PreferedMaximumLength;
|
|
}
|
|
|
|
EntrySize = sizeof(SERVER_INFO_100);
|
|
}
|
|
|
|
*Buffer = MIDL_user_allocate(BufferSize);
|
|
|
|
if (*Buffer == NULL) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
BufferEnd = (LPTSTR)((ULONG_PTR)*Buffer+BufferSize);
|
|
|
|
ClientServerInfo = *Buffer;
|
|
|
|
*TotalEntries = 0;
|
|
|
|
*EntriesRead = 0;
|
|
|
|
//
|
|
// While there are still entries to process....
|
|
//
|
|
|
|
TrimmingNames = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0');
|
|
while (EntriesInList) {
|
|
|
|
EntriesInList -= 1;
|
|
|
|
//
|
|
// If this entry is appropriate to be packed,
|
|
//
|
|
|
|
if ( (ServerList->sv101_type & ServerType) &&
|
|
(!TrimmingNames ||
|
|
STRCMP( ServerList->sv101_name, FirstNameToReturn ) >= 0 ) ) {
|
|
|
|
TrimmingNames = FALSE;
|
|
|
|
//
|
|
// Indicate one more entry in the list.
|
|
//
|
|
|
|
*TotalEntries += 1;
|
|
|
|
//
|
|
// If we can fit this entry before the buffer end,
|
|
// pack in the information into the buffer.
|
|
//
|
|
|
|
if ( !BufferFull &&
|
|
(ULONG_PTR)ClientServerInfo+EntrySize <= (ULONG_PTR)BufferEnd) {
|
|
|
|
//
|
|
// Copy over the platform ID and computer name.
|
|
//
|
|
|
|
ClientServerInfo->sv101_platform_id = ServerList->sv101_platform_id;
|
|
|
|
ClientServerInfo->sv101_name = ServerList->sv101_name;
|
|
|
|
if (NetpPackString(&ClientServerInfo->sv101_name,
|
|
(LPBYTE)((PCHAR)ClientServerInfo)+EntrySize,
|
|
&BufferEnd)) {
|
|
|
|
if (Level == 101) {
|
|
|
|
ClientServerInfo->sv101_version_major = ServerList->sv101_version_major;
|
|
|
|
ClientServerInfo->sv101_version_minor = ServerList->sv101_version_minor;
|
|
|
|
ClientServerInfo->sv101_type = ServerList->sv101_type;
|
|
|
|
ClientServerInfo->sv101_comment = ServerList->sv101_comment;
|
|
|
|
if (NetpPackString(&ClientServerInfo->sv101_comment,
|
|
(LPBYTE)((PCHAR)ClientServerInfo)+EntrySize,
|
|
&BufferEnd)) {
|
|
*EntriesRead += 1;
|
|
}
|
|
else {
|
|
BufferFull = TRUE;
|
|
}
|
|
} else {
|
|
*EntriesRead += 1;
|
|
}
|
|
}
|
|
else {
|
|
BufferFull = TRUE;
|
|
}
|
|
|
|
ClientServerInfo = (PSERVER_INFO_101)((PCHAR)ClientServerInfo+EntrySize);
|
|
} else {
|
|
//
|
|
// If we're returning the entire list, we can
|
|
// early out now, since there's no point in continuing.
|
|
//
|
|
|
|
if (ReturnWholeList) {
|
|
|
|
*TotalEntries = TotalEntriesInList;
|
|
|
|
break;
|
|
}
|
|
BufferFull = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
ServerList += 1;
|
|
}
|
|
|
|
//
|
|
// If we weren't able to pack all the entries into the list,
|
|
// return ERROR_MORE_DATA
|
|
//
|
|
|
|
BrPrint( (BR_CLIENT_OP,
|
|
"BrRetrieveServerListForBackup: returning from the stored server list for network <%ws>\n",
|
|
Network->NetworkName.Buffer ));
|
|
if (*EntriesRead != *TotalEntries) {
|
|
return ERROR_MORE_DATA;
|
|
} else {
|
|
return NERR_Success;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
I_BrowserrResetStatistics (
|
|
IN LPTSTR servername OPTIONAL
|
|
)
|
|
{
|
|
NET_API_STATUS Status = NERR_Success;
|
|
ULONG BufferSize;
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Perform access validation on the caller.
|
|
//
|
|
|
|
Status = NetpAccessCheck(
|
|
BrGlobalBrowserSecurityDescriptor, // Security descriptor
|
|
BROWSER_CONTROL_ACCESS, // Desired access
|
|
&BrGlobalBrowserInfoMapping ); // Generic mapping
|
|
|
|
if ( Status != NERR_Success) {
|
|
|
|
BrPrint((BR_CRITICAL,
|
|
"I_BrowserrResetStatistics failed NetpAccessCheck\n" ));
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
EnterCriticalSection(&BrowserStatisticsLock);
|
|
|
|
NumberOfServerEnumerations = 0;
|
|
|
|
NumberOfDomainEnumerations = 0;
|
|
|
|
NumberOfOtherEnumerations = 0;
|
|
|
|
NumberOfMissedGetBrowserListRequests = 0;
|
|
|
|
//
|
|
// Reset the driver's statistics as well.
|
|
//
|
|
|
|
if (!DeviceIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_RESET_STATISTICS, NULL, 0, NULL, 0, &BufferSize, NULL)) {
|
|
|
|
//
|
|
// The API failed, return the error.
|
|
//
|
|
|
|
Status = GetLastError();
|
|
}
|
|
|
|
LeaveCriticalSection(&BrowserStatisticsLock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
I_BrowserrQueryStatistics (
|
|
IN LPTSTR servername OPTIONAL,
|
|
OUT LPBROWSER_STATISTICS *Statistics
|
|
)
|
|
{
|
|
NET_API_STATUS Status = NERR_Success;
|
|
BOWSER_STATISTICS BowserStatistics;
|
|
ULONG BufferSize;
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Perform access validation on the caller.
|
|
//
|
|
|
|
Status = NetpAccessCheck(
|
|
BrGlobalBrowserSecurityDescriptor, // Security descriptor
|
|
BROWSER_QUERY_ACCESS, // Desired access
|
|
&BrGlobalBrowserInfoMapping ); // Generic mapping
|
|
|
|
if ( Status != NERR_Success) {
|
|
|
|
BrPrint((BR_CRITICAL,
|
|
"I_BrowserrQueryStatistics failed NetpAccessCheck\n" ));
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
|
|
*Statistics = MIDL_user_allocate(sizeof(BROWSER_STATISTICS));
|
|
|
|
if (*Statistics == NULL) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
EnterCriticalSection(&BrowserStatisticsLock);
|
|
|
|
if (!DeviceIoControl(BrDgReceiverDeviceHandle, IOCTL_LMDR_QUERY_STATISTICS, NULL, 0, &BowserStatistics, sizeof(BowserStatistics), &BufferSize, NULL)) {
|
|
|
|
//
|
|
// The API failed, return the error.
|
|
//
|
|
|
|
Status = GetLastError();
|
|
} else {
|
|
|
|
if (BufferSize != sizeof(BOWSER_STATISTICS)) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
} else {
|
|
(*Statistics)->StatisticsStartTime = BowserStatistics.StartTime;
|
|
|
|
(*Statistics)->NumberOfServerAnnouncements = BowserStatistics.NumberOfServerAnnouncements;
|
|
(*Statistics)->NumberOfDomainAnnouncements = BowserStatistics.NumberOfDomainAnnouncements;
|
|
(*Statistics)->NumberOfElectionPackets = BowserStatistics.NumberOfElectionPackets;
|
|
(*Statistics)->NumberOfMailslotWrites = BowserStatistics.NumberOfMailslotWrites;
|
|
(*Statistics)->NumberOfGetBrowserServerListRequests = BowserStatistics.NumberOfGetBrowserServerListRequests;
|
|
(*Statistics)->NumberOfMissedServerAnnouncements = BowserStatistics.NumberOfMissedServerAnnouncements;
|
|
(*Statistics)->NumberOfMissedMailslotDatagrams = BowserStatistics.NumberOfMissedMailslotDatagrams;
|
|
(*Statistics)->NumberOfMissedGetBrowserServerListRequests = BowserStatistics.NumberOfMissedGetBrowserServerListRequests +
|
|
NumberOfMissedGetBrowserListRequests;
|
|
(*Statistics)->NumberOfFailedServerAnnounceAllocations = BowserStatistics.NumberOfFailedServerAnnounceAllocations;
|
|
(*Statistics)->NumberOfFailedMailslotAllocations = BowserStatistics.NumberOfFailedMailslotAllocations;
|
|
(*Statistics)->NumberOfFailedMailslotReceives = BowserStatistics.NumberOfFailedMailslotReceives;
|
|
(*Statistics)->NumberOfFailedMailslotWrites = BowserStatistics.NumberOfFailedMailslotWrites;
|
|
(*Statistics)->NumberOfFailedMailslotOpens = BowserStatistics.NumberOfFailedMailslotOpens;
|
|
(*Statistics)->NumberOfDuplicateMasterAnnouncements = BowserStatistics.NumberOfDuplicateMasterAnnouncements;
|
|
(*Statistics)->NumberOfIllegalDatagrams = BowserStatistics.NumberOfIllegalDatagrams;
|
|
|
|
//
|
|
// Now fill in the local statistics.
|
|
//
|
|
|
|
(*Statistics)->NumberOfServerEnumerations = NumberOfServerEnumerations;
|
|
(*Statistics)->NumberOfDomainEnumerations = NumberOfDomainEnumerations;
|
|
(*Statistics)->NumberOfOtherEnumerations = NumberOfOtherEnumerations;
|
|
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&BrowserStatisticsLock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Browser request response cache management logic.
|
|
//
|
|
|
|
|
|
|
|
PCACHED_BROWSE_RESPONSE
|
|
BrLookupAndAllocateCachedEntry(
|
|
IN PNETWORK Network,
|
|
IN DWORD ServerType,
|
|
IN WORD Size,
|
|
IN DWORD Level,
|
|
IN LPCWSTR FirstNameToReturn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will look up (and allocate if appropriate) a cached
|
|
browse response for this browse.
|
|
|
|
Enter with the Network Locked shared or exclusive.
|
|
|
|
Arguments:
|
|
IN PNETWORK Network - Network to allocate entry on.
|
|
IN DWORD ServerType - Server type bits for request.
|
|
IN WORD Size, - Users buffer size for request.
|
|
IN WORD Level - Level of request.
|
|
|
|
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.)
|
|
|
|
Passed name must be the canonical form of the name.
|
|
|
|
This entry is never NULL. It may be a pointer to an empty string to indicate
|
|
the enumeration starts at the beginning of the list.
|
|
|
|
|
|
Return Value:
|
|
|
|
PCACHED_BROWSE_RESPONSE - NULL or a cached response for the request.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PCACHED_BROWSE_RESPONSE response;
|
|
|
|
//
|
|
// If we have more cached responses than we are allowed,
|
|
// remove the last entry from the list and free it.
|
|
//
|
|
|
|
if (Network->NumberOfCachedResponses > BrInfo.NumberOfCachedResponses) {
|
|
|
|
//
|
|
// We need to release the network and re-acquire it
|
|
// exclusively, because we use the network lock to protect
|
|
// enumerations from deletions.
|
|
//
|
|
|
|
UNLOCK_NETWORK(Network);
|
|
|
|
if (LOCK_NETWORK(Network)) {
|
|
|
|
EnterCriticalSection(&Network->ResponseCacheLock);
|
|
if (Network->NumberOfCachedResponses > BrInfo.NumberOfCachedResponses) {
|
|
|
|
PLIST_ENTRY LastEntry = RemoveTailList(&Network->ResponseCache);
|
|
|
|
response = CONTAINING_RECORD(LastEntry, CACHED_BROWSE_RESPONSE, Next);
|
|
|
|
Network->NumberOfCachedResponses -= 1;
|
|
|
|
response->Next.Flink = NULL;
|
|
response->Next.Blink = NULL;
|
|
|
|
//
|
|
// Free the last cached entry.
|
|
//
|
|
|
|
BrDestroyCacheEntry( response );
|
|
|
|
response = NULL;
|
|
}
|
|
LeaveCriticalSection(&Network->ResponseCacheLock);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Search the list of responses for this one.
|
|
//
|
|
|
|
EnterCriticalSection(&Network->ResponseCacheLock);
|
|
|
|
for (entry = Network->ResponseCache.Flink ;
|
|
entry != &Network->ResponseCache ;
|
|
entry = entry->Flink ) {
|
|
|
|
response = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
|
|
|
|
//
|
|
// If this response cache entry matches the incoming request,
|
|
// we can increment the hit count for this entry and return it.
|
|
//
|
|
|
|
if (response->Level == Level
|
|
&&
|
|
response->ServerType == ServerType
|
|
&&
|
|
response->Size == Size
|
|
&&
|
|
wcscmp( response->FirstNameToReturn, FirstNameToReturn ) == 0) {
|
|
|
|
//
|
|
// This response exactly matches the request.
|
|
//
|
|
// Bump its hit count and move it to the head of the cache.
|
|
//
|
|
|
|
response->HitCount += 1;
|
|
|
|
response->TotalHitCount += 1;
|
|
|
|
//
|
|
// Remove this entry from its current location in the list and
|
|
// move it to the head of the list.
|
|
//
|
|
|
|
RemoveEntryList(&response->Next);
|
|
|
|
InsertHeadList(&Network->ResponseCache, &response->Next);
|
|
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Found cache entry 0x%x/%d/%x H:%d T:%d\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
response->ServerType,
|
|
response->Level,
|
|
response->Size,
|
|
response->HitCount,
|
|
response->TotalHitCount ));
|
|
|
|
BrPrint( (BR_CLIENT_OP,
|
|
"BrLookupAndAllocateCachedEntry: returning cached entry for domain <%ws>, network <%ws>\n",
|
|
Network->DomainInfo->DomUnicodeDomainName, Network->NetworkName.Buffer ));
|
|
|
|
LeaveCriticalSection(&Network->ResponseCacheLock);
|
|
|
|
return response;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We've walked our entire cache and have been unable to find
|
|
// a response that matches our request.
|
|
//
|
|
// Allocate a new response cache entry and hook it into the cache.
|
|
//
|
|
|
|
response = BrAllocateResponseCacheEntry(Network, ServerType, Size, Level, FirstNameToReturn );
|
|
|
|
LeaveCriticalSection(&Network->ResponseCacheLock);
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
VOID
|
|
BrAgeResponseCache(
|
|
IN PNETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will age response cache entries for a network.
|
|
|
|
We scan the response cache, and every entry that has a cached response
|
|
will be tossed. In addition, any entry that has had less than the
|
|
cache hit limit number of hits since the past scan will also be removed.
|
|
|
|
Arguments:
|
|
IN PNETWORK Network - Network to age entries on.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY entry;
|
|
|
|
EnterCriticalSection(&Network->ResponseCacheLock);
|
|
|
|
try {
|
|
|
|
for (entry = Network->ResponseCache.Flink ;
|
|
entry != &Network->ResponseCache ;
|
|
entry = entry->Flink ) {
|
|
PCACHED_BROWSE_RESPONSE response = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
|
|
|
|
//
|
|
// If this response didn't have a hit count high enough during
|
|
// the previous run to justify keeping it around, blow it away.
|
|
//
|
|
|
|
if (response->HitCount < BrInfo.CacheHitLimit) {
|
|
response->LowHitCount += 1;
|
|
}
|
|
|
|
//
|
|
// If we have CacheHitLimit iterations of low hits, then
|
|
// flush the entry from the cache.
|
|
//
|
|
|
|
if (response->LowHitCount > BrInfo.CacheHitLimit) {
|
|
PLIST_ENTRY nextentry = entry->Blink;
|
|
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Flush cache entry for 0x%x/%d/%x H:%d T:%d\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
response->ServerType,
|
|
response->Level,
|
|
response->Size,
|
|
response->HitCount,
|
|
response->TotalHitCount ));
|
|
|
|
Network->NumberOfCachedResponses -= 1;
|
|
|
|
RemoveEntryList(entry);
|
|
|
|
entry->Flink = NULL;
|
|
entry->Blink = NULL;
|
|
|
|
BrDestroyCacheEntry(response);
|
|
|
|
entry = nextentry;
|
|
|
|
//
|
|
// Null out the pointer to make sure we don't use it again.
|
|
//
|
|
|
|
response = NULL;
|
|
|
|
} else {
|
|
BrPrint(( BR_SERVER_ENUM,
|
|
"%ws: %ws: Retain cache entry 0x%x/%d/%x H:%d T:%d\n",
|
|
Network->DomainInfo->DomUnicodeDomainName,
|
|
Network->NetworkName.Buffer,
|
|
response->ServerType, response->Level, response->Size, response->HitCount, response->TotalHitCount ));
|
|
|
|
//
|
|
// We ALWAYS blow away the response buffer during an age pass.
|
|
//
|
|
|
|
MIDL_user_free( response->Buffer );
|
|
|
|
response->Buffer = NULL;
|
|
|
|
//
|
|
// Reset the hit count for this entry for this pass.
|
|
//
|
|
|
|
response->HitCount = 0;
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
LeaveCriticalSection(&Network->ResponseCacheLock);
|
|
}
|
|
}
|
|
|
|
|
|
PCACHED_BROWSE_RESPONSE
|
|
BrAllocateResponseCacheEntry(
|
|
IN PNETWORK Network,
|
|
IN DWORD ServerType,
|
|
IN WORD Size,
|
|
IN DWORD Level,
|
|
IN LPCWSTR FirstNameToReturn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will allocate a new browse response cache entry.
|
|
|
|
Arguments:
|
|
IN PNETWORK Network - Network to allocate entry on.
|
|
IN DWORD ServerType - Server type bits for request.
|
|
IN WORD Size, - Users buffer size for request.
|
|
IN WORD Level - Level of request.
|
|
|
|
FirstNameToReturn - FirstNameCached
|
|
|
|
Return Value:
|
|
|
|
PCACHED_BROWSE_RESPONSE - NULL or a cached response for the request.
|
|
|
|
NOTE: This is called with the network response cache locked.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCACHED_BROWSE_RESPONSE response;
|
|
|
|
response = MIDL_user_allocate( sizeof( CACHED_BROWSE_RESPONSE ) );
|
|
|
|
if ( response == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Flag the information for this response.
|
|
//
|
|
|
|
response->ServerType = ServerType;
|
|
response->Size = Size;
|
|
response->Level = Level;
|
|
|
|
//
|
|
// Initialize the other fields in the response.
|
|
//
|
|
|
|
response->Buffer = NULL;
|
|
response->HitCount = 0;
|
|
response->TotalHitCount = 0;
|
|
response->LowHitCount = 0;
|
|
response->Status = NERR_Success;
|
|
wcscpy( response->FirstNameToReturn, FirstNameToReturn );
|
|
|
|
Network->NumberOfCachedResponses += 1;
|
|
|
|
//
|
|
// We hook this response into the tail of the cache. We do this
|
|
// because we assume that this request won't be used frequently. If
|
|
// it is, it will move to the head of the cache naturally.
|
|
//
|
|
|
|
InsertTailList(&Network->ResponseCache, &response->Next);
|
|
|
|
return response;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
BrDestroyCacheEntry(
|
|
IN PCACHED_BROWSE_RESPONSE CacheEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys an individual response cache entry.
|
|
|
|
Arguments:
|
|
IN PCACHED_BROWSE_RESPONSE CacheEntry - Entry to destroy.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success
|
|
|
|
--*/
|
|
{
|
|
ASSERT (CacheEntry->Next.Flink == NULL);
|
|
ASSERT (CacheEntry->Next.Blink == NULL);
|
|
|
|
if (CacheEntry->Buffer != NULL) {
|
|
MIDL_user_free(CacheEntry->Buffer);
|
|
}
|
|
|
|
MIDL_user_free(CacheEntry);
|
|
|
|
return NERR_Success;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
BrDestroyResponseCache(
|
|
IN PNETWORK Network
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys the entire response cache for a supplied network.
|
|
|
|
Arguments:
|
|
IN PNETWORK Network - Network to allocate entry on.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success
|
|
|
|
--*/
|
|
|
|
{
|
|
while (!IsListEmpty(&Network->ResponseCache)) {
|
|
PCACHED_BROWSE_RESPONSE cacheEntry;
|
|
PLIST_ENTRY entry = RemoveHeadList(&Network->ResponseCache);
|
|
|
|
entry->Flink = NULL;
|
|
entry->Blink = NULL;
|
|
|
|
cacheEntry = CONTAINING_RECORD(entry, CACHED_BROWSE_RESPONSE, Next);
|
|
|
|
Network->NumberOfCachedResponses -= 1;
|
|
|
|
BrDestroyCacheEntry(cacheEntry);
|
|
|
|
}
|
|
|
|
ASSERT (Network->NumberOfCachedResponses == 0);
|
|
|
|
return NERR_Success;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NetrBrowserStatisticsGet (
|
|
IN LPTSTR servername OPTIONAL,
|
|
IN DWORD Level,
|
|
IN OUT LPBROWSER_STATISTICS_STRUCT InfoStruct
|
|
)
|
|
{
|
|
//
|
|
// And return success.
|
|
//
|
|
|
|
return(NERR_Success);
|
|
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NetrBrowserStatisticsClear (
|
|
IN LPTSTR servername OPTIONAL
|
|
)
|
|
{
|
|
//
|
|
// And return success.
|
|
//
|
|
|
|
return(NERR_Success);
|
|
|
|
}
|
|
|
|
#if DBG
|
|
|
|
NET_API_STATUS
|
|
I_BrowserrDebugCall (
|
|
IN LPTSTR servername OPTIONAL,
|
|
IN DWORD DebugCode,
|
|
IN DWORD OptionalValue
|
|
)
|
|
{
|
|
NET_API_STATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Perform access validation on the caller.
|
|
//
|
|
|
|
Status = NetpAccessCheck(
|
|
BrGlobalBrowserSecurityDescriptor, // Security descriptor
|
|
BROWSER_CONTROL_ACCESS, // Desired access
|
|
&BrGlobalBrowserInfoMapping ); // Generic mapping
|
|
|
|
if ( Status != NERR_Success) {
|
|
|
|
BrPrint((BR_CRITICAL,
|
|
"I_BrowserrDebugCall failed NetpAccessCheck\n" ));
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
switch (DebugCode) {
|
|
case BROWSER_DEBUG_BREAK_POINT:
|
|
DbgBreakPoint();
|
|
break;
|
|
|
|
case BROWSER_DEBUG_DUMP_NETWORKS:
|
|
BrDumpNetworks();
|
|
break;
|
|
case BROWSER_DEBUG_SET_DEBUG:
|
|
BrInfo.BrowserDebug |= OptionalValue;
|
|
BrPrint(( BR_INIT, "Setting browser trace to %lx\n", BrInfo.BrowserDebug));
|
|
break;
|
|
|
|
case BROWSER_DEBUG_CLEAR_DEBUG:
|
|
BrInfo.BrowserDebug &= ~OptionalValue;
|
|
BrPrint(( BR_INIT, "Setting browser trace to %lx\n", BrInfo.BrowserDebug));
|
|
break;
|
|
case BROWSER_DEBUG_TRUNCATE_LOG:
|
|
Status = BrTruncateLog();
|
|
break;
|
|
|
|
default:
|
|
BrPrint(( BR_CRITICAL, "Unknown debug callout %lx\n", DebugCode));
|
|
DbgBreakPoint();
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NET_API_STATUS
|
|
I_BrowserrDebugTrace (
|
|
IN LPTSTR servername OPTIONAL,
|
|
IN LPSTR String
|
|
)
|
|
{
|
|
NET_API_STATUS Status;
|
|
|
|
|
|
//
|
|
// Perform access validation on the caller.
|
|
//
|
|
|
|
Status = NetpAccessCheck(
|
|
BrGlobalBrowserSecurityDescriptor, // Security descriptor
|
|
BROWSER_CONTROL_ACCESS, // Desired access
|
|
&BrGlobalBrowserInfoMapping ); // Generic mapping
|
|
|
|
if ( Status != NERR_Success) {
|
|
|
|
BrPrint((BR_CRITICAL,
|
|
"I_BrowserrDebugTrace failed NetpAccessCheck\n" ));
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// Stick the string parameter into the browser log.
|
|
//
|
|
|
|
BrowserTrace( BR_UTIL, "%s", String);
|
|
|
|
//
|
|
// And return success.
|
|
//
|
|
|
|
return(NERR_Success);
|
|
|
|
}
|
|
#else
|
|
|
|
NET_API_STATUS
|
|
I_BrowserrDebugCall (
|
|
IN LPTSTR servername OPTIONAL,
|
|
IN DWORD DebugCode,
|
|
IN DWORD OptionalValue
|
|
)
|
|
{
|
|
return(ERROR_NOT_SUPPORTED);
|
|
|
|
}
|
|
NET_API_STATUS
|
|
I_BrowserrDebugTrace (
|
|
IN LPTSTR servername OPTIONAL,
|
|
IN LPSTR String
|
|
)
|
|
{
|
|
return(ERROR_NOT_SUPPORTED);
|
|
|
|
}
|
|
#endif
|