/*++ Copyright (c) 1996 Microsoft Corporation Module Name: rpcex.cxx Abstract: This module defines K2 rpc support. Author: Johnson Apacible (JohnsonA) June-19-1996 --*/ #include "ftpdp.hxx" #include "time.h" #include "timer.h" # define ASSUMED_AVERAGE_USER_NAME_LEN ( 40) # define CONN_LEEWAY ( 3) // // Private functions. // BOOL GenDoubleNullStringFromMultiLine( IN LPCWSTR lpsz, IN OUT LPWSTR * ppszz, IN OUT LPDWORD pcchLen) { DWORD cchLen; DWORD nLines; LPCWSTR pszSrc; DBG_ASSERT( lpsz != NULL && ppszz != NULL && pcchLen != NULL); // Initialize *ppszz = NULL; *pcchLen = 0; // // 1. Find the length of the the complete message including new lines // For each new line we may potentially need an extra blank char // So allocate space = nLines + length + 2 terminating null chars. // cchLen = lstrlenW( lpsz); for ( pszSrc = lpsz, nLines = 0; *pszSrc != L'\0'; pszSrc++) { if ( *pszSrc == L'\n') { nLines++; } } // for // Allocate sufficient space for the string. *ppszz = (LPWSTR ) TCP_ALLOC( (cchLen + nLines + 3) * sizeof(WCHAR)); if ( *ppszz == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return (FALSE); } // // walk down the local copy and convert all the line feed characters to // be null char // // // Since the MULTI_SZ string cannot contain empty strings // we convert empty lines (ones with just \n into " \0" // i.e. with a blank character. // pszSrc = lpsz; LPWSTR pszDst = *ppszz; if ( *pszSrc == L'\n') { // first line is a linefeed. insert a blank and proceed to next line. *pszDst = L' '; *(pszDst + 1) = L'\0'; // move forward pszDst += 2; pszSrc++; } for( ; *pszSrc != L'\0'; pszSrc++, pszDst++) { if ( *pszSrc == L'\n') { // we are at boundary of new line. if ( pszSrc > lpsz && *(pszSrc - 1) == L'\n') { // we detected an empty line. Store an additional blank. *pszDst++ = L' '; } *pszDst = L'\0'; // put null char in place of line feed. } else { *pszDst = *pszSrc; } } // for *pszDst++ = L'\0'; // terminate with 1st null chars. *pszDst++ = L'\0'; // terminate with 2nd null chars. *pcchLen = DIFF(pszDst - *ppszz); DBG_ASSERT( *pcchLen <= cchLen + nLines + 3); return ( TRUE); } // GenDoubleNullStringFromMultiline() BOOL FTP_SERVER_INSTANCE::WriteParamsToRegistry( LPFTP_CONFIG_INFO pConfig ) /*++ This function writes parameters to the registry Arguments: hkey HKEY for registry entry of parameters of FTP server. pConfig pointer to configuration information. Returns: TRUE on success and FALSE if there is any failure. --*/ { DWORD err = NO_ERROR; HKEY hkey = NULL; err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, QueryRegParamKey(), 0, KEY_WRITE, &hkey ); if( hkey == NULL ) { err = ERROR_INVALID_HANDLE; SetLastError( err); DBGPRINTF(( DBG_CONTEXT, "Invalid Registry key given. error %lu\n", err )); return FALSE; } // // Write the registry data. // if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_ALLOW_ANONYMOUS ) ) { err = WriteRegistryDword( hkey, FTPD_ALLOW_ANONYMOUS, pConfig->fAllowAnonymous ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_ALLOW_GUEST_ACCESS ) ) { err = WriteRegistryDword( hkey, FTPD_ALLOW_GUEST_ACCESS, pConfig->fAllowGuestAccess ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_ANNOTATE_DIRECTORIES ) ) { err = WriteRegistryDword( hkey, FTPD_ANNOTATE_DIRS, pConfig->fAnnotateDirectories ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_ANONYMOUS_ONLY ) ) { err = WriteRegistryDword( hkey, FTPD_ANONYMOUS_ONLY, pConfig->fAnonymousOnly ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_LISTEN_BACKLOG ) ) { err = WriteRegistryDword( hkey, FTPD_LISTEN_BACKLOG, pConfig->dwListenBacklog ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_LOWERCASE_FILES ) ) { err = WriteRegistryDword( hkey, FTPD_LOWERCASE_FILES, pConfig->fLowercaseFiles ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_MSDOS_DIR_OUTPUT ) ) { err = WriteRegistryDword( hkey, FTPD_MSDOS_DIR_OUTPUT, pConfig->fMsdosDirOutput ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_SHOW_4_DIGIT_YEAR ) ) { err = WriteRegistryDword( hkey, FTPD_SHOW_4_DIGIT_YEAR, pConfig->fFourDigitYear ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_EXIT_MESSAGE ) ) { err = RegSetValueExW( hkey, FTPD_EXIT_MESSAGE_W, 0, REG_SZ, (BYTE *)pConfig->lpszExitMessage, ( wcslen( pConfig->lpszExitMessage ) + 1 ) * sizeof(WCHAR) ); } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_GREETING_MESSAGE ) ) { LPWSTR pszzGreetingMessage = NULL; DWORD cchLen = 0; if (GenDoubleNullStringFromMultiLine( pConfig->lpszGreetingMessage, &pszzGreetingMessage, &cchLen) ) { DBG_ASSERT( pszzGreetingMessage != NULL); err = RegSetValueExW( hkey, FTPD_GREETING_MESSAGE_W, 0, REG_MULTI_SZ, (BYTE *) pszzGreetingMessage, cchLen * sizeof(WCHAR)); TCP_FREE( pszzGreetingMessage); } else { err = ERROR_NOT_ENOUGH_MEMORY; } } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_BANNER_MESSAGE ) ) { LPWSTR pszzBannerMessage = NULL; DWORD cchLen = 0; if (GenDoubleNullStringFromMultiLine( pConfig->lpszBannerMessage, &pszzBannerMessage, &cchLen) ) { DBG_ASSERT( pszzBannerMessage != NULL); err = RegSetValueExW( hkey, FTPD_BANNER_MESSAGE_W, 0, REG_MULTI_SZ, (BYTE *) pszzBannerMessage, cchLen * sizeof(WCHAR)); TCP_FREE( pszzBannerMessage); } else { err = ERROR_NOT_ENOUGH_MEMORY; } } if( !err && IsFieldSet( pConfig->FieldControl, FC_FTP_MAX_CLIENTS_MESSAGE ) ) { err = RegSetValueExW( hkey, FTPD_MAX_CLIENTS_MSG_W, 0, REG_SZ, (BYTE *)pConfig->lpszMaxClientsMessage, ( wcslen( pConfig->lpszMaxClientsMessage ) + 1 ) * sizeof(WCHAR) ); } RegCloseKey( hkey); if( err ) { SetLastError( err ); return FALSE; } return TRUE; } // WriteParamsToRegistry DWORD FTP_IIS_SERVICE::GetServiceConfigInfoSize( IN DWORD dwLevel ) { switch (dwLevel) { case 1: return sizeof(FTP_CONFIG_INFO); } return 0; } // FTP_IIS_SERVICE::GetServerConfigInfoSize BOOL FTP_SERVER_INSTANCE::SetServiceConfig( IN PCHAR pBuffer ) /*++ Description Sets the common service admin information for the servers specified in dwServerMask. Arguments: pConfig - Admin information to set Note: --*/ { LPFTP_CONFIG_INFO pConfig = (LPFTP_CONFIG_INFO)pBuffer; DWORD err = NO_ERROR; // // If success, then Write the new info to the registry, then read it back. // if( WriteParamsToRegistry( pConfig )) { err = InitFromRegistry( pConfig->FieldControl); } else { err = GetLastError(); } IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT, "FtpSetServiceConfig returns with %d %lu\n", err )); } return(err == NO_ERROR); } // FTP_SERVER_INSTANCE::SetServiceConfig BOOL FTP_SERVER_INSTANCE::GetServiceConfig( IN PCHAR pBuffer, IN DWORD dwLevel ) /*++ Description Retrieves the admin information Arguments: pBuffer - Buffer to fill up. dwLevel - info level of information to return. Note: --*/ { LPFTP_CONFIG_INFO pConfig = (LPFTP_CONFIG_INFO)pBuffer; DWORD err = NO_ERROR; ZeroMemory( pConfig, sizeof(*pConfig) ); // // Obtain and Return the admin information. // err = GetConfigInformation( pConfig); IF_DEBUG( RPC) { DBGPRINTF(( DBG_CONTEXT, "FtprGetAdminInformation() returns Error=%u\n", err)); } return (err == NO_ERROR); } // FTP_SERVER_INSTANCE::GetServiceConfig BOOL FTP_SERVER_INSTANCE::EnumerateUsers( OUT PCHAR * pBuffer, OUT PDWORD nRead ) /*++ Description Enumerates the connected users. Arguments: pBuffer - Buffer to fill up. nRead - number of entries filled --*/ { DWORD err; DWORD cbBuffer; LPIIS_USER_INFO_1 pInfo; IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT,"Entering FtpEnumerateUsers\n")); } // // Determine the necessary buffer size. // cbBuffer = (GetCurrentConnectionsCount() + CONN_LEEWAY) * sizeof( IIS_USER_INFO_1 ); *nRead = 0; pInfo = (LPIIS_USER_INFO_1)MIDL_user_allocate( cbBuffer); if (pInfo == NULL) { err = ERROR_NOT_ENOUGH_MEMORY; } else { // // Make a first attempt at enumerating the user info // err = NO_ERROR; if ( !::EnumerateUsers( (PCHAR)pInfo, &cbBuffer, nRead, this )) { // // Free up old buffer and allocate big one now. // We will try once more to get the data again // with a larger buffer. // if ( cbBuffer > 0) { MIDL_user_free( pInfo ); pInfo = (LPIIS_USER_INFO_1)MIDL_user_allocate(cbBuffer); if( pInfo == NULL ) { err = ERROR_NOT_ENOUGH_MEMORY; } else { // // Since we do not lock the active connections list // it is possible some one came in now and hence the // buffer is insufficient to hold all people. // Ignore this case, as we are never // going to be accurate // ::EnumerateUsers( (PCHAR)pInfo, &cbBuffer, nRead, this ); if ( *nRead == 0 ) { MIDL_user_free(pInfo); pInfo = NULL; } } } // cbBuffer > 0 } // if unsuccessful at first attempt } if( err != NO_ERROR ) { IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT, "I_FtprEnumerateUsers failed. Error = %lu\n", err )); } SetLastError(err); return(FALSE); } *pBuffer = (PCHAR)pInfo; IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT, "FtpEnumerateUsers returns %d entries, buffer [%x]\n", *nRead, *pBuffer )); } return TRUE; } // EnumerateUsers BOOL FTP_SERVER_INSTANCE::DisconnectUser( IN DWORD dwIdUser ) /*++ Description Disconnect the user Arguments: dwIdUser - Identifies the user to disconnect. If 0, then disconnect ALL users. --*/ { IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT, "Entering FtpDisconnectUsers with id[%d]\n", dwIdUser)); } if ( !::DisconnectUser( dwIdUser, this ) ) { IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT, "DisconnectUser failed with %d\n", GetLastError())); } SetLastError(NERR_UserNotFound); return(FALSE); } return(TRUE); } // DisconnectUser BOOL FTP_SERVER_INSTANCE::GetStatistics( IN DWORD dwLevel, OUT PCHAR* pBuffer ) /*++ Description Disconnect Queries the server statistics Arguments: dwLevel - Info level. Currently only level 0 is supported. pBuffer - Will receive a pointer to the statistics structure. --*/ { APIERR err = NO_ERROR; IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT, "FtpQueryStatistics2, level %lu\n", dwLevel )); } // // Return the proper statistics based on the infolevel. // switch( dwLevel ) { case 0 : { LPFTP_STATISTICS_0 pstats0; pstats0 = (LPFTP_STATISTICS_0) MIDL_user_allocate(sizeof(*pstats0)); if( pstats0 == NULL ) { err = ERROR_NOT_ENOUGH_MEMORY; } else { ATQ_STATISTICS atqStat; ZeroMemory( pstats0, sizeof( *pstats0 ) ); QueryStatsObj()->CopyToStatsBuffer( pstats0 ); // // Get instance's bandwidth throttling statistics // if ( QueryBandwidthInfo() ) { if ( AtqBandwidthGetInfo( QueryBandwidthInfo(), ATQ_BW_STATISTICS, (ULONG_PTR *) &atqStat ) ) { pstats0->TotalBlockedRequests = atqStat.cBlockedRequests; pstats0->TotalRejectedRequests = atqStat.cRejectedRequests; pstats0->TotalAllowedRequests = atqStat.cAllowedRequests; pstats0->CurrentBlockedRequests = atqStat.cCurrentBlockedRequests; pstats0->MeasuredBandwidth = atqStat.MeasuredBandwidth; } } pstats0->TimeOfLastClear = GetCurrentTimeInSeconds() - pstats0->TimeOfLastClear; // // Copy Global statistics counter values // pstats0->ConnectionAttempts = g_pFTPStats->QueryStatsObj()->ConnectionAttempts; *pBuffer = (PCHAR)pstats0; } } break; default : err = ERROR_INVALID_LEVEL; break; } IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT, "FtpQueryStatistics2 returns Error = %lu\n", err )); } SetLastError(err); return(err == NO_ERROR); } // QueryStatistics BOOL FTP_SERVER_INSTANCE::ClearStatistics( VOID ) /*++ Description Clears the server statistics Arguments: None. --*/ { IF_DEBUG( RPC ) { DBGPRINTF(( DBG_CONTEXT, "Entering FtpClearStatistics2\n")); } QueryStatsObj()->ClearStatistics(); return TRUE; } // ClearStatistics