/*++ Copyright (c) 1991-92 Microsoft Corporation Module Name: Supports.c Abstract: This module determines which optional features that a given remote machine supports. These features are of interest to the RpcXlate code, among other people. Author: John Rogers (JohnRo) 28-Mar-1991 Environment: Only runs under NT, although the interface is portable (Win/32). Requires ANSI C extensions: slash-slash comments, long external names. Revision History: 28-Mar-1991 Johnro Created. 02-Apr-1991 JohnRo Moved NetpRdrFsControlTree to . 06-May-1991 JohnRo Implement UNICODE. 26-Jul-1991 JohnRo Quiet DLL stub debug output. 31-Oct-1991 JohnRo RAID 3414: allow explicit local server name. Also allow use of NetRemoteComputerSupports() for local computer. Minor UNICODE work. 08-May-1992 JohnRo Use equates. 22-Sep-1992 JohnRo RAID 6739: Browser too slow when not logged into browsed domain. --*/ // These must be included first: #include // IN, NULL, etc. #include // DWORD, LPDWORD, LPTSTR, TCHAR, etc. #include // NET_API_STATUS, NET_API_FUNCTION. // These may be included in any order: #include // IF_DEBUG(). #include // NetpIsRemote(), NIRFLAG_ stuff, IS equates. #include // NERR_Success, etc. #include // My prototype, SUPPORTS_ equates. #include // NetpIsRemoteNameValid(). #include // NetpAssert(). #include // NetpMemoryAllocate(), NetpMemoryFree(). #include // NetpRdrFsControlTree(). #include // LMR_TRANSACTION, etc. #include // PREFIX_ equates. #include // STRCAT(), STRCPY(), STRLEN(). #include // USE_IPC NET_API_STATUS NET_API_FUNCTION NetRemoteComputerSupports( IN LPCWSTR UncServerName OPTIONAL, // Must start with "\\". IN DWORD OptionsWanted, // Set SUPPORT_ bits wanted. OUT LPDWORD OptionsSupported // Supported features, masked. ) #define SHARE_SUFFIX (LPTSTR) TEXT("\\IPC$") #define SHARE_SUFFIX_LEN 5 #ifdef UNICODE #define LOCAL_FLAGS ( SUPPORTS_REMOTE_ADMIN_PROTOCOL \ | SUPPORTS_RPC \ | SUPPORTS_SAM_PROTOCOL \ | SUPPORTS_UNICODE \ | SUPPORTS_LOCAL ) #else // not UNICODE #define LOCAL_FLAGS ( SUPPORTS_REMOTE_ADMIN_PROTOCOL \ | SUPPORTS_RPC \ | SUPPORTS_SAM_PROTOCOL \ | SUPPORTS_LOCAL ) #endif // not UNICODE /*++ Routine Description: NetRemoteComputerSupports queries the redirector about a given remote system. This is done to find out which optional features the remote system supports. The features of interest are Unicode, RPC, and the Remote Admin Protocol. This will establish a connection if one doesn't already exist. Arguments: UncServerName - Gives name of remote server to query. This must begin with "\\". OptionsWanted - Gives a set of bits indicating which features the caller is interested in. (At least one bit must be on.) OptionsSupported - Points to a DWORD which will be set with set of bits indicating which of the features selected by OptionsWanted are actually implemented on the computer with UncServerName. (All other bits in this DWORD will be set to 0.) The value of OptionsSupported is undefined if the return value is not NERR_Success. Return Value: NET_API_STATUS. --*/ { NET_API_STATUS Status; DWORD TempSupported = 0; IF_DEBUG(SUPPORTS) { NetpKdPrint(( PREFIX_NETAPI "NetRemoteComputerSupports: input mask is " FORMAT_HEX_DWORD ".\n", OptionsWanted)); } // Error check what caller gave us. if (OptionsSupported == NULL) { return (ERROR_INVALID_PARAMETER); } else if (OptionsWanted == 0) { // Not what caller really intended, probably. return (ERROR_INVALID_PARAMETER); } // // Handle no name given (implies local computer). // if ( (UncServerName==NULL) || ((*UncServerName) == (TCHAR) '\0') ) { TempSupported = LOCAL_FLAGS & OptionsWanted; } else { TCHAR CanonServerName[MAX_PATH]; DWORD LocalOrRemote; // Will be set to ISLOCAL or ISREMOTE. // // Name was given. Canonicalize it and check if it's remote. // Status = NetpIsRemote( (LPWSTR)UncServerName, // input: uncanon name & LocalOrRemote, // output: local or remote flag CanonServerName, // output: canon name 0); // flags: normal IF_DEBUG(SUPPORTS) { NetpKdPrint(( PREFIX_NETAPI "NetRemoteComputerSupports: canon status is " FORMAT_API_STATUS ", Lcl/rmt=" FORMAT_HEX_DWORD ", canon buf is '" FORMAT_LPTSTR "'.\n", Status, LocalOrRemote, CanonServerName)); } if (Status != NERR_Success) { return (Status); } if (LocalOrRemote == ISLOCAL) { // // Explicit local name given. // TempSupported = LOCAL_FLAGS & OptionsWanted; } else { // // Explicit remote name given. // DWORD RedirCapabilities; PLMR_CONNECTION_INFO_2 RedirConnInfo; DWORD RedirConnInfoSize = sizeof(LMR_CONNECTION_INFO_2) + ( (MAX_PATH+1 + MAX_PATH+1) * sizeof(TCHAR) ); PLMR_REQUEST_PACKET RedirRequest; DWORD RedirRequestSize = sizeof(LMR_REQUEST_PACKET); LPTSTR TreeConnName; // Build tree connect name. TreeConnName = NetpMemoryAllocate( (STRLEN(CanonServerName) + SHARE_SUFFIX_LEN + 1) * sizeof(TCHAR) ); if (TreeConnName == NULL) { return (ERROR_NOT_ENOUGH_MEMORY); } (void) STRCPY(TreeConnName, CanonServerName); (void) STRCAT(TreeConnName, SHARE_SUFFIX); NetpAssert(NetpIsRemoteNameValid(TreeConnName)); // Alloc fsctl buffers. RedirConnInfo = NetpMemoryAllocate(RedirConnInfoSize); if (RedirConnInfo == NULL) { NetpMemoryFree(TreeConnName); return (ERROR_NOT_ENOUGH_MEMORY); } RedirRequest = NetpMemoryAllocate(RedirRequestSize); if (RedirRequest == NULL) { NetpMemoryFree(RedirConnInfo); NetpMemoryFree(TreeConnName); return (ERROR_NOT_ENOUGH_MEMORY); } RedirRequest->Level = 2; RedirRequest->Type = GetConnectionInfo; RedirRequest->Version = REQUEST_PACKET_VERSION; // Open tree conn (which will establish connection with the remote // server if one doesn't already exist) and do the FSCTL. Status = NetpRdrFsControlTree( TreeConnName, // \\server\IPC$ NULL, // No transport. USE_IPC, // Connection type FSCTL_LMR_GET_CONNECTION_INFO, // fsctl func code NULL, // security descriptor RedirRequest, // in buffer RedirRequestSize, // in buffer size RedirConnInfo, // out buffer RedirConnInfoSize, // out buffer size FALSE); // not a "null session" API. IF_DEBUG(SUPPORTS) { NetpKdPrint(( PREFIX_NETAPI "NetRemoteComputerSupports: back from fsctl, " "status is " FORMAT_API_STATUS ".\n", Status)); } // Handle remote machine not found. if (Status != NERR_Success) { NetpMemoryFree(RedirConnInfo); NetpMemoryFree(RedirRequest); NetpMemoryFree(TreeConnName); return (Status); } RedirCapabilities = RedirConnInfo->Capabilities; IF_DEBUG(SUPPORTS) { NetpKdPrint(( PREFIX_NETAPI "NetRemoteComputerSupports: redir mask is " FORMAT_HEX_DWORD ".\n", RedirCapabilities)); } NetpMemoryFree(RedirConnInfo); NetpMemoryFree(RedirRequest); NetpMemoryFree(TreeConnName); if (OptionsWanted & SUPPORTS_REMOTE_ADMIN_PROTOCOL) { if (RedirCapabilities & CAPABILITY_REMOTE_ADMIN_PROTOCOL) { TempSupported |= SUPPORTS_REMOTE_ADMIN_PROTOCOL; } } if (OptionsWanted & SUPPORTS_RPC) { if (RedirCapabilities & CAPABILITY_RPC) { TempSupported |= SUPPORTS_RPC; } } if (OptionsWanted & SUPPORTS_SAM_PROTOCOL) { if (RedirCapabilities & CAPABILITY_SAM_PROTOCOL) { TempSupported |= SUPPORTS_SAM_PROTOCOL; } } if (OptionsWanted & SUPPORTS_UNICODE) { if (RedirCapabilities & CAPABILITY_UNICODE) { TempSupported |= SUPPORTS_UNICODE; } } } } IF_DEBUG(SUPPORTS) { NetpKdPrint(( PREFIX_NETAPI "NetRemoteComputerSupports: output mask is " FORMAT_HEX_DWORD ".\n", TempSupported)); } // Make sure we don't tell caller anything he/she didn't want to know. NetpAssert( (TempSupported & (~OptionsWanted)) == 0); // Tell caller what we know. *OptionsSupported = TempSupported; return (NERR_Success); } // NetRemoteComputerSupports