|
|
// *********************************************************************************
//
// 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 ([email protected]) 13-Nov-2000
//
// Revision History:
//
// Sunil G.V.N. Murali ([email protected]) 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); }
|