WindowsXP-SP1/admin/cmdline/lib/rmtconnectivity.c
2020-09-30 16:53:49 +02:00

873 lines
26 KiB
C

// *********************************************************************************
//
// Copyright (c) Microsoft Corporation
//
// Module Name:
//
// RmtConnectivity.c
//
// Abstract:
//
// This modules implements remote connectivity functionality for all the
// command line tools.
//
// Author:
//
// Sunil G.V.N. Murali (murali.sunil@wipro.com) 13-Nov-2000
//
// Revision History:
//
// Sunil G.V.N. Murali (murali.sunil@wipro.com) 13-Sep-2000 : Created It.
//
// *********************************************************************************
#include "pch.h"
#include "cmdline.h"
#include "cmdlineres.h"
//
// constants / defines / enumerations
//
#define STR_INPUT_PASSWORD GetResString( IDS_STR_INPUT_PASSWORD )
#define ERROR_LOCAL_CREDENTIALS GetResString( IDS_ERROR_LOCAL_CREDENTIALS )
// share names
#define SHARE_IPC _T( "IPC$" )
#define SHARE_ADMIN _T( "ADMIN$" )
#define SHARE_CDRIVE _T( "C$" )
#define SHARE_DDRIVE _T( "D$" )
// externs
extern BOOL g_bWinsockLoaded;
// ***************************************************************************
// Routine Description:
// Validates the server name
//
// Arguments:
// [ in ] pszServer : server name
//
// Return Value:
// TRUE if valid, FALSE if not valid
// ***************************************************************************
BOOL IsValidIPAddress( LPCTSTR pszAddress )
{
// local variables
DWORD dw = 0;
LONG lValue = 0;
LPTSTR pszTemp = NULL;
DWORD dwOctets[ 4 ] = { 0, 0, 0, 0 };
__MAX_SIZE_STRING szBuffer = NULL_STRING;
// check the buffer
if ( pszAddress == NULL )
{
SetLastError( DNS_ERROR_INVALID_TYPE );
SaveLastError();
return FALSE;
}
// parse and get the octet values
lstrcpy( szBuffer, pszAddress );
pszTemp = _tcstok( szBuffer, _T( "." ) );
while ( pszTemp != NULL )
{
// check whether the current octet is numeric or not
if ( IsNumeric( pszTemp, 10, FALSE ) == FALSE )
return FALSE;
// get the value of the octet and check the range
lValue = AsLong( pszTemp, 10 );
if ( lValue < 0 || lValue >= 255 )
return FALSE;
// fetch next octet
dwOctets[ dw++ ] = lValue;
pszTemp = _tcstok( NULL, _T( "." ) );
}
// check and return
if ( dw != 4 )
{
SetLastError( DNS_ERROR_INVALID_TYPE );
SaveLastError();
return FALSE;
}
// now check the special condition
// ?? time being this is not implemented ??
// return the validity of the ip address
return TRUE;
}
// ***************************************************************************
// Routine Description:
// Validates the server name
//
// Arguments:
// [ in ] pszServer : server name
//
// Return Value:
// TRUE if valid, FALSE if not valid
// ***************************************************************************
BOOL IsValidServer( LPCTSTR pszServer )
{
// local variables
LONG lIndex = 0;
LPTSTR pszTemp = NULL;
LPCTSTR pszComputerName = NULL;
TCHAR pszInvalidChars[] = _T( " \\/[]:|<>+=;,?$#()!@^\"`{}*%" );
// check for NULL ... if NULL return
if ( pszServer == NULL )
return TRUE;
// check the length of the string
if ( lstrlen( pszServer ) == 0 )
return TRUE;
// check whether this is a valid ip address or not
if ( IsValidIPAddress( pszServer ) == TRUE )
return TRUE; // it's valid ip address ... so is valid server name
// now check the server name for invalid characters
// \/[]:|<>+=;,?$#()!@^"`{}*%
pszTemp = __calloc( lstrlen( pszServer ) + 5, sizeof( TCHAR ) );
if ( pszTemp == NULL )
{
SetLastError( E_OUTOFMEMORY );
SaveLastError();
return FALSE;
}
// copy the contents into the internal buffer and check for the invalid characters
lstrcpy( pszTemp, pszServer );
for( lIndex = 0; lIndex < lstrlen( pszInvalidChars ); lIndex++ )
{
if ( _tcschr( pszTemp, pszInvalidChars[ lIndex ] ) != NULL )
{
SetLastError( ERROR_BAD_NETPATH );
SaveLastError();
__free( pszTemp );
return FALSE;
}
}
// copy the server name again ... and check if the computer name by any chance is a number
lstrcpy( pszTemp, pszServer );
pszComputerName = _tcstok( pszTemp, _T( "." ) );
if ( pszComputerName == NULL )
pszComputerName = pszServer;
// check for the numeric system name ..
if ( pszComputerName[ 0 ] != _T('-') && IsNumeric(pszComputerName, 10, FALSE) == TRUE )
{
SetLastError( ERROR_INVALID_COMPUTERNAME );
SaveLastError();
__free( pszTemp );
return FALSE;
}
// valid server name
__free( pszTemp );
return TRUE;
}
// ***************************************************************************
// Routine Description:
// Get HostName from ipaddress.
//
// Arguments:
//
// Return Value:
//
// ***************************************************************************
BOOL GetHostByIPAddr( LPCTSTR pszServer, LPTSTR pszHostName, BOOL bNeedFQDN )
{
// local variables
WSADATA wsaData;
DWORD dwErr = 0;
DWORD dwLength = 0;
ULONG ulInetAddr = 0;
HOSTENT* pHostEnt = NULL;
BOOL bReturnValue = FALSE;
LPSTR pszTemp = NULL;
WORD wVersionRequested = 0;
// check whether winsock module is loaded into process memory or not
// if not load it now
if ( g_bWinsockLoaded == FALSE )
{
// initiate the use of Ws2_32.dll by a process ( VERSION: 2.2 )
wVersionRequested = MAKEWORD( 2, 2 );
dwErr = WSAStartup( wVersionRequested, &wsaData );
if ( dwErr != 0 )
{
SetLastError( WSAGetLastError() );
__free( pszTemp );
return FALSE;
}
// remember that winsock library is loaded
g_bWinsockLoaded = TRUE;
}
// allocate the a buffer to store the server name in multibyte format
dwLength = lstrlen( pszServer );
pszTemp = ( LPSTR ) __calloc( dwLength + 5, sizeof( CHAR ) );
if ( pszTemp == NULL )
{
SetLastError( E_OUTOFMEMORY );
SaveLastError();
return FALSE;
}
// convert the server name into multibyte string. this is because curren winsock implementation
// works only with multibyte string and there is no support for unicode
GetAsMultiByteString( pszServer, pszTemp, dwLength );
// inet_addr function converts a string containing an Internet Protocol (Ipv4)
// dotted address into a proper address for the IN_ADDR structure.
ulInetAddr = inet_addr( pszTemp );
if ( ulInetAddr == INADDR_NONE )
{
__free( pszTemp );
SetLastError( STG_E_UNKNOWN );
SaveLastError();
return FALSE;
}
// gethostbyaddr function retrieves the host information corresponding to a network address.
pHostEnt = gethostbyaddr( (LPSTR) &ulInetAddr, sizeof( ulInetAddr ), PF_INET );
if ( pHostEnt == NULL )
{
// ?? DONT KNOW WHAT TO DO IF THIS FUNCTION FAILS ??
// ?? CURRENTLY SIMPLY RETURNS FALSE ??
return FALSE;
}
// release the memory allocated so far
__free( pszTemp );
// check whether user wants the FQDN name or NetBIOS name
// if NetBIOS name is required, then remove the domain name
pszTemp = pHostEnt->h_name;
if ( bNeedFQDN == FALSE )
pszTemp = strtok( pHostEnt->h_name, "." );
// we got info in char type ... convert it into current build's compatible type
GetCompatibleStringFromMultiByte( pszTemp, pszHostName, MAX_COMPUTERNAME_LENGTH );
// return
return TRUE;
}
// ***************************************************************************
// Routine Description:
// Connects to the remote Server. This is stub function.
//
// Arguments:
// [ in ] pszServer : server name
// [ in ] pszUser : user
// [ in ] pszPassword : password
//
// Return Value:
// NO_ERROR if succeeds other appropriate error code if failed
//
// ***************************************************************************
DWORD ConnectServer( LPCTSTR pszServer, LPCTSTR pszUser, LPCTSTR pszPassword )
{
// invoke the original function and return the result
return ConnectServer2( pszServer, pszUser, pszPassword, _T( "IPC$" ) );
}
// ***************************************************************************
// Routine Description:
// Connects to the remote Server
//
// Arguments:
// [ in ] pszServer : server name
// [ in ] pszUser : user
// [ in ] pszPassword : password
// [ in ] pszShare : share name to connect to
//
// Return Value:
// NO_ERROR if succeeds other appropriate error code if failed
//
// ***************************************************************************
DWORD ConnectServer2( LPCTSTR pszServer, LPCTSTR pszUser,
LPCTSTR pszPassword, LPCTSTR pszShare )
{
// local variables
DWORD dwSize = 0;
DWORD dwConnect = 0;
NETRESOURCE resource;
__MAX_SIZE_STRING szUNCPath = NULL_STRING;
__MAX_SIZE_STRING szMachine = NULL_STRING;
// if the server name refers to the local system,
// and also, if user credentials were not supplied, then treat
// connection is successfull
// if user credentials information is passed for local system,
// return ERROR_LOCAL_CREDENTIALS
if ( IsLocalSystem( pszServer ) == TRUE )
{
if ( pszUser == NULL || lstrlen( pszUser ) == 0 )
return NO_ERROR; // local sustem
else
{
SetLastError( E_LOCAL_CREDENTIALS );
SetReason( ERROR_LOCAL_CREDENTIALS );
return E_LOCAL_CREDENTIALS;
}
}
// check whether the server name is in UNC format or not
// if yes, extract the server name
lstrcpy( szMachine, pszServer ); // assume server is not in UNC format
if ( IsUNCFormat( pszServer ) == TRUE )
lstrcpy( szMachine, pszServer + 2 );
// validate the server name
if ( IsValidServer( szMachine ) == FALSE )
return GetLastError();
//
// prepare the machine name into UNC format
lstrcpy( szUNCPath, NULL_STRING );
if ( pszShare == NULL || lstrlen( pszShare ) == 0 )
{
FORMAT_STRING( szUNCPath, _T( "\\\\%s" ), szMachine );
}
else
{
FORMAT_STRING2( szUNCPath, _T( "\\\\%s\\%s" ), szMachine, pszShare );
}
// initialize the resource structure with null
ZeroMemory( &resource, sizeof( resource ) );
resource.dwType = RESOURCETYPE_ANY;
resource.lpProvider = NULL;
resource.lpLocalName = NULL;
resource.lpRemoteName = szUNCPath;
// try establishing connection to the remote server
dwConnect = WNetAddConnection2( &resource, pszPassword, pszUser, 0 );
// check the result
// and if error has occured, get the appropriate message
switch( dwConnect )
{
case NO_ERROR:
{
dwConnect = 0;
SetReason( NULL_STRING ); // clear the error message
// check for the OS compatibilty
if ( IsCompatibleOperatingSystem( GetTargetVersion( szMachine ) ) == FALSE )
{
// since the connection already established close the connection
CloseConnection( szMachine );
// set the error text
SetReason( ERROR_OS_INCOMPATIBLE );
dwConnect = ERROR_EXTENDED_ERROR;
}
// ...
break;
}
case ERROR_EXTENDED_ERROR:
WNetSaveLastError(); // save the extended error
break;
default:
// set the last error
SaveLastError();
break;
}
// return the result of the connection establishment
return dwConnect;
}
// ***************************************************************************
// Routine Description:
//
// Closes the remote connection.
//
// Arguments:
// [in] szServer --remote machine to close the connection
//
// Return Value:
//
// DWORD --NO_ERROR if succeeds.
// --Possible error codes.
//
// ***************************************************************************
DWORD CloseConnection( LPCTSTR szServer )
{
// forcibly close the connection
return CloseConnection2( szServer, NULL, CI_CLOSE_BY_FORCE | CI_SHARE_IPC );
}
// ***************************************************************************
// Routine Description:
// Closes the established connection on the remote system.
//
// Arguments:
// [ in ] szServer - Null terminated string that specifies the remote
// system name. NULL specifie the local system.
//
// Return Value:
//
// ***************************************************************************
DWORD CloseConnection2( LPCTSTR szServer, LPCTSTR pszShare, DWORD dwFlags )
{
// local variables
BOOL bForce = FALSE;
DWORD dwCancel = 0;
__STRING_256 szMachine = NULL_STRING;
__STRING_256 szUNCServer = NULL_STRING;
// check the server contents ... it might be referring to the local system
if ( szServer == NULL || lstrlen( szServer ) == 0 )
return NO_ERROR;
// check whether the server name is in UNC format or not
// if yes, extract the server name
lstrcpy( szMachine, szServer ); // assume server is not in UNC format
if ( IsUNCFormat( szServer ) == TRUE )
lstrcpy( szMachine, szServer + 2 );
// determine if share name has to appended or not for this server name
if ( dwFlags & CI_SHARE_IPC )
{
// --> \\server\ipc$
FORMAT_STRING2( szUNCServer, _T( "\\\\%s\\%s" ), szMachine, SHARE_IPC );
}
else if ( dwFlags & CI_SHARE_ADMIN )
{
// --> \\server\admin$
FORMAT_STRING2( szUNCServer, _T( "\\\\%s\\%s" ), szMachine, SHARE_ADMIN );
}
else if ( dwFlags & CI_SHARE_CDRIVE )
{
// --> \\server\c$
FORMAT_STRING2( szUNCServer, _T( "\\\\%s\\%s" ), szMachine, SHARE_CDRIVE );
}
else if ( dwFlags & CI_SHARE_DDRIVE )
{
// --> \\server\d$
FORMAT_STRING2( szUNCServer, _T( "\\\\%s\\%s" ), szMachine, SHARE_DDRIVE );
}
else if ( dwFlags & CI_SHARE_CUSTOM && pszShare != NULL )
{
// --> \\server\share
FORMAT_STRING2( szUNCServer, _T( "\\\\%s\\%s" ), szMachine, pszShare );
}
else
{
// --> \\server
FORMAT_STRING( szUNCServer, _T( "\\\\%s" ), szMachine );
}
// determine whether to close this connection forcibly or not
if ( dwFlags & CI_CLOSE_BY_FORCE )
bForce = TRUE;
//
// cancel the connection
dwCancel = WNetCancelConnection2( szUNCServer, 0, bForce );
// check the result
// and if error has occured, get the appropriate message
switch( dwCancel )
{
case NO_ERROR:
dwCancel = 0;
SetReason( NULL_STRING ); // clear the error message
break;
case ERROR_EXTENDED_ERROR:
WNetSaveLastError(); // save the extended error
break;
default:
// set the last error
SaveLastError();
break;
}
// return the result of the cancelling the connection
return dwCancel;
}
// ***************************************************************************
// Routine Description:
// Determines whether server name is specified in UNC format or not
//
// Arguments:
// [ in ] pszServer : server name
//
// Return Value:
// TRUE : if specified in UNC format
// FALSE : if not specified in UNC format
//
// ***************************************************************************
BOOL IsUNCFormat( LPCTSTR pszServer )
{
return ( StringCompare( pszServer, _T( "\\\\" ), TRUE, 2 ) == 0 );
}
// ***************************************************************************
// Routine Description:
// Determines whether server is referring to the local or remote system
//
// Arguments:
// [ in ] pszServer : server name
//
// Return Value:
// TRUE : for local system
// FALSE : for remote system
//
// ***************************************************************************
BOOL IsLocalSystem( LPCTSTR pszServer )
{
// local variables
DWORD dwSize = 0;
__STRING_128 szTemp = NULL_STRING;
__STRING_128 szHostName = NULL_STRING;
// if the server name is empty, it is a local system
if ( pszServer == NULL || lstrlen( pszServer ) == 0 )
return TRUE;
// get the local system name and check
dwSize = SIZE_OF_ARRAY( szTemp );
GetComputerNameEx( ComputerNamePhysicalNetBIOS, szTemp, &dwSize );
if ( StringCompare( szTemp, pszServer, TRUE, 0 ) == 0 )
return TRUE;
//Check pszSever having IP address
if( IsValidIPAddress( pszServer ) == TRUE )
{
// resolve the ipaddress to host name
if( GetHostByIPAddr( pszServer, szHostName, FALSE ) == FALSE )
return FALSE;
// check if resolved ipaddress matches with the current host name
if ( StringCompare( szTemp, szHostName, TRUE, 0 ) == 0 )
return TRUE; // local system
else
return FALSE; // not a local system
}
// get the local system fully qualified name and check
dwSize = SIZE_OF_ARRAY( szTemp );
GetComputerNameEx( ComputerNamePhysicalDnsFullyQualified, szTemp, &dwSize );
if ( StringCompare( szTemp, pszServer, TRUE, 0 ) == 0 )
return TRUE;
// finally ... it might not be local system name
// NOTE: there are chances for us to not be able to identify whether
// the system name specified is a local system or remote system
return FALSE;
}
// ***************************************************************************
// Routine Description:
//
// Establishes a connection to the remote system.
//
// Arguments:
//
// [in] szServer --Nullterminated string to establish the conection.
// --NULL connects to the local system.
// [in] szUserName --Null terminated string that specifies the user name.
// --NULL takes the default user name.
// [in] dwUserLength --Length of the username.
// [in] szPassword --Null terminated string that specifies the password
// --NULL takes the default user name's password.
// [in] dwPasswordLength --Length of the password.
// [in] bNeedPassword --True if password is required to establish the connection.
// --False if it is not required.
//
// Return Value:
//
// BOOL --True if it establishes
// --False if it fails.
//
// ***************************************************************************
BOOL EstablishConnection( LPCTSTR szServer, LPTSTR szUserName, DWORD dwUserLength,
LPTSTR szPassword, DWORD dwPasswordLength, BOOL bNeedPassword )
{
// local variables
DWORD dwSize = 0;
BOOL bDefault = FALSE;
DWORD dwConnectResult = 0;
__MAX_SIZE_STRING szBuffer = NULL_STRING;
// clear the error .. if any
SetLastError( NO_ERROR );
// sometime users want the utility to prompt for the password
// check what user wants the utility to do
if ( bNeedPassword == TRUE && szPassword != NULL && lstrcmp( szPassword, _T( "*" ) ) == 0 )
{
// user wants the utility to prompt for the password
// so skip this part and let the flow directly jump the password acceptance part
}
else
{
// try to establish connection to the remote system with the credentials supplied
bDefault = FALSE;
if ( lstrlen( szUserName ) == 0 )
{
// user name is empty
// so, it is obvious that password will also be empty
// even if password is specified, we have to ignore that
bDefault = TRUE;
dwConnectResult = ConnectServer( szServer, NULL, NULL );
}
else
{
// credentials were supplied
// but password might not be specified ... so check and act accordingly
dwConnectResult = ConnectServer( szServer,
szUserName, ( bNeedPassword == FALSE ? szPassword : NULL ) );
// determine whether to close the connection or retain the connection
if ( bNeedPassword == FALSE )
{
// connection might have already established .. so to be on safer side
// we inform the caller not to close the connection
bDefault = TRUE;
}
}
// check the result ... if successful in establishing connection ... return
if ( dwConnectResult == NO_ERROR )
{
// if connected with default params, pass additional information to the caller
if ( bDefault == TRUE )
SetLastError( I_NO_CLOSE_CONNECTION );
return TRUE;
}
// now check the kind of error occurred
switch( dwConnectResult )
{
case ERROR_LOGON_FAILURE:
case ERROR_INVALID_PASSWORD:
break;
case ERROR_SESSION_CREDENTIAL_CONFLICT:
// user credentials conflict ... client has to handle this situation
// wrt to this module, connection to the remote system is success
SetLastError( dwConnectResult );
return TRUE;
case E_LOCAL_CREDENTIALS:
// user credentials not accepted for local system
SetLastError( E_LOCAL_CREDENTIALS );
SetReason( ERROR_LOCAL_CREDENTIALS );
return TRUE;
case ERROR_DUP_NAME:
case ERROR_NETWORK_UNREACHABLE:
case ERROR_HOST_UNREACHABLE:
case ERROR_PROTOCOL_UNREACHABLE:
case ERROR_INVALID_NETNAME:
// change the error code so that user gets correct message
SetLastError( ERROR_NO_NETWORK );
SaveLastError();
SetLastError( dwConnectResult ); // reset the error code
return FALSE;
default:
return FALSE; // no use of accepting the password .. return failure
break;
}
// if failed in establishing connection to the remote terminal
// even if the password is specifed, then there is nothing to do ... simply return failure
if ( bNeedPassword == FALSE )
return FALSE;
}
// check whether user name is specified or not
// if not, get the local system's current user name under whose credentials, the process
// is running
if ( lstrlen( szUserName ) == 0 )
{
// get the user name
if ( GetUserNameEx( NameSamCompatible, szUserName, &dwUserLength ) == FALSE )
{
// error occured while trying to get the current user info
SaveLastError();
return FALSE;
}
}
// format the user name
// if ( _tcschr( szUserName, _T( '\\' ) ) == NULL )
// {
// // server not present in user name ... prepare ... this is only for display purpose
// FORMAT_STRING2( szBuffer, _T( "%s\\%s" ), szServer, szUserName );
// lstrcpy( szUserName, szBuffer );
// }
// accept the password from the user
FORMAT_STRING( szBuffer, STR_INPUT_PASSWORD, szUserName );
WriteConsole( GetStdHandle( STD_ERROR_HANDLE ),
szBuffer, lstrlen( szBuffer ), &dwSize, NULL );
GetPassword( szPassword, MAX_PASSWORD_LENGTH );
// now again try to establish the connection using the currently
// supplied credentials
dwConnectResult = ConnectServer( szServer, szUserName, szPassword );
if ( dwConnectResult == NO_ERROR )
return TRUE; // connection established successfully
// now check the kind of error occurred
switch( dwConnectResult )
{
case ERROR_SESSION_CREDENTIAL_CONFLICT:
// user credentials conflict ... client has to handle this situation
// wrt to this module, connection to the remote system is success
SetLastError( dwConnectResult );
return TRUE;
case E_LOCAL_CREDENTIALS:
// user credentials not accepted for local system
SetLastError( E_LOCAL_CREDENTIALS );
SetReason( ERROR_LOCAL_CREDENTIALS );
return TRUE;
case ERROR_DUP_NAME:
case ERROR_NETWORK_UNREACHABLE:
case ERROR_HOST_UNREACHABLE:
case ERROR_PROTOCOL_UNREACHABLE:
case ERROR_INVALID_NETNAME:
// change the error code so that user gets correct message
SetLastError( ERROR_NO_NETWORK );
SaveLastError();
SetLastError( dwConnectResult ); // reset the error code
return FALSE;
}
// return the failure
return FALSE;
}
// ***************************************************************************
// Routine Description:
// Establishes a connection to the remote system.
//
// Arguments:
//
// Return Value:
//
// ***************************************************************************
BOOL EstablishConnection2( PTCONNECTIONINFO pci )
{
// local variables
LPCTSTR pszShare = NULL;
// clear the error .. if any
SetLastError( NO_ERROR );
// identify the share to which user wishes to connect to
if ( pci->dwFlags & CI_SHARE_IPC )
pszShare = SHARE_IPC;
else if ( pci->dwFlags & CI_SHARE_ADMIN )
pszShare = SHARE_ADMIN;
else if ( pci->dwFlags & CI_SHARE_CDRIVE )
pszShare = SHARE_CDRIVE;
else if ( pci->dwFlags & CI_SHARE_DDRIVE )
pszShare = SHARE_DDRIVE;
return TRUE;
}
// ***************************************************************************
// Routine Description:
//
// Arguments:
//
// Return Value:
//
// ***************************************************************************
DWORD GetTargetVersion( LPCTSTR pszServer )
{
// local variables
DWORD dwVersion = 0;
LPTSTR pszUNCPath = NULL;
NET_API_STATUS netstatus;
SERVER_INFO_101* pSrvInfo = NULL;
// check the inputs
if ( pszServer == NULL )
return 0;
// allocate memory for having server in UNC format
pszUNCPath = (LPTSTR) __calloc( lstrlen( pszServer ) + 5, sizeof( TCHAR ) );
if ( pszUNCPath == NULL )
{
SetLastError( E_OUTOFMEMORY );
SaveLastError();
return 0;
}
// prepare the server name in UNC format
lstrcpy( pszUNCPath, pszServer );
if ( lstrlen( pszServer ) != 0 && IsUNCFormat( pszServer ) == FALSE )
{
FORMAT_STRING( pszUNCPath, _T( "\\\\%s" ), pszServer );
}
// get the version info
netstatus = NetServerGetInfo( pszUNCPath, 101, (LPBYTE*) &pSrvInfo );
// release the memory
__free( pszUNCPath );
// check the result .. if not success return
if ( netstatus != NERR_Success )
return 0;
// prepare the version
dwVersion = 0;
if ( ( pSrvInfo->sv101_type & SV_TYPE_NT ) )
{
// --> "sv101_version_major" least significant 4 bits of the byte,
// the major release version number of the operating system.
// --> "sv101_version_minor" the minor release version number of the operating system
dwVersion = (pSrvInfo->sv101_version_major & MAJOR_VERSION_MASK) * 1000;
dwVersion += pSrvInfo->sv101_version_minor;
}
// release the buffer allocated by network api
NetApiBufferFree( pSrvInfo );
// return
return dwVersion;
}
// ***************************************************************************
// Routine Description:
//
// Arguments:
//
// Return Value:
//
// ***************************************************************************
BOOL IsCompatibleOperatingSystem( DWORD dwVersion )
{
// OS version above windows 2000 is compatible
return (dwVersion >= 5000);
}