/*++ Copyright (c) 2000-2002 Microsoft Corporation Module Name: ullogp.h (Http.sys Ansi Logging) Abstract: This module implements the logging facilities for Http.sys including the NCSA, IIS and W3CE types of logging. Author: Ali E. Turkoglu (aliTu) 10-May-2000 Revision History: --*/ #ifndef _ULLOGP_H_ #define _ULLOGP_H_ // // Private definitions for the Ul Logging Module // #define UTF8_LOGGING_ENABLED() (g_UTF8Logging) #define _UL_GET_LOG_FILE_NAME_PREFIX(x) \ ( (x) == HttpLoggingTypeW3C ? L"\\extend" : \ (x) == HttpLoggingTypeIIS ? L"\\inetsv" : \ (x) == HttpLoggingTypeNCSA ? L"\\ncsa" : L"\\unknwn" \ ) #define _UL_GET_LOG_FILE_NAME_PREFIX_UTF8(x) \ ( (x) == HttpLoggingTypeW3C ? L"\\u_extend" : \ (x) == HttpLoggingTypeIIS ? L"\\u_inetsv" : \ (x) == HttpLoggingTypeNCSA ? L"\\u_ncsa" : L"\\u_unknwn" \ ) #define UL_GET_LOG_FILE_NAME_PREFIX(x) \ (UTF8_LOGGING_ENABLED() ? _UL_GET_LOG_FILE_NAME_PREFIX_UTF8(x) :\ _UL_GET_LOG_FILE_NAME_PREFIX(x)) // // Obsolete - Only used by Old Hit // Replace this with a switch statement inside a inline function // which is more efficient, if u start using it again // #define UL_GET_NAME_FOR_HTTP_VERB(v) \ ( (v) == HttpVerbUnparsed ? L"UNPARSED" : \ (v) == HttpVerbUnknown ? L"UNKNOWN" : \ (v) == HttpVerbInvalid ? L"INVALID" : \ (v) == HttpVerbOPTIONS ? L"OPTIONS" : \ (v) == HttpVerbGET ? L"GET" : \ (v) == HttpVerbHEAD ? L"HEAD" : \ (v) == HttpVerbPOST ? L"POST" : \ (v) == HttpVerbPUT ? L"PUT" : \ (v) == HttpVerbDELETE ? L"DELETE" : \ (v) == HttpVerbTRACE ? L"TRACE" : \ (v) == HttpVerbCONNECT ? L"CONNECT" : \ (v) == HttpVerbTRACK ? L"TRACK" : \ (v) == HttpVerbMOVE ? L"MOVE" : \ (v) == HttpVerbCOPY ? L"COPY" : \ (v) == HttpVerbPROPFIND ? L"PROPFIND" : \ (v) == HttpVerbPROPPATCH ? L"PROPPATCH" : \ (v) == HttpVerbMKCOL ? L"MKCOL" : \ (v) == HttpVerbLOCK ? L"LOCK" : \ (v) == HttpVerbUNLOCK ? L"UNLOCK" : \ (v) == HttpVerbSEARCH ? L"SEARCH" : \ L"???" \ ) #define UL_DEFAULT_NCSA_FIELDS (MD_EXTLOG_CLIENT_IP | \ MD_EXTLOG_USERNAME | \ MD_EXTLOG_DATE | \ MD_EXTLOG_TIME | \ MD_EXTLOG_METHOD | \ MD_EXTLOG_URI_STEM | \ MD_EXTLOG_URI_QUERY | \ MD_EXTLOG_PROTOCOL_VERSION | \ MD_EXTLOG_HTTP_STATUS | \ MD_EXTLOG_BYTES_SENT) #define UL_DEFAULT_IIS_FIELDS (MD_EXTLOG_CLIENT_IP | \ MD_EXTLOG_USERNAME | \ MD_EXTLOG_DATE | \ MD_EXTLOG_TIME | \ MD_EXTLOG_SITE_NAME | \ MD_EXTLOG_COMPUTER_NAME | \ MD_EXTLOG_SERVER_IP | \ MD_EXTLOG_TIME_TAKEN | \ MD_EXTLOG_BYTES_RECV | \ MD_EXTLOG_BYTES_SENT | \ MD_EXTLOG_HTTP_STATUS | \ MD_EXTLOG_WIN32_STATUS | \ MD_EXTLOG_METHOD | \ MD_EXTLOG_URI_STEM) #define UL_GET_LOG_TYPE_MASK(x,y) \ ( (x) == HttpLoggingTypeW3C ? (y) : \ (x) == HttpLoggingTypeIIS ? UL_DEFAULT_IIS_FIELDS : \ (x) == HttpLoggingTypeNCSA ? UL_DEFAULT_NCSA_FIELDS : 0 \ ) // // The order of the following should match with // UL_LOG_FIELD_TYPE enum definition. // PWSTR UlFieldTitleLookupTable[] = { L" date", L" time", L" s-sitename", L" s-computername", L" s-ip", L" cs-method", L" cs-uri-stem", L" cs-uri-query", L" s-port", L" cs-username", L" c-ip", L" cs-version", L" cs(User-Agent)", L" cs(Cookie)", L" cs(Referer)", L" cs-host", L" sc-status", L" sc-substatus", L" sc-win32-status", L" sc-bytes", L" cs-bytes", L" time-taken" }; #define UL_GET_LOG_FIELD_TITLE(x) \ ((x)>=UlLogFieldMaximum ? L"Unknown" : UlFieldTitleLookupTable[(x)]) #define UL_GET_LOG_TITLE_IF_PICKED(x,y,z) \ ((y)&(z) ? UL_GET_LOG_FIELD_TITLE((x)) : L"") // // Pick the local time for file name & rollover if format is NCSA or IIS, // otherwise (W3C) pick the local time only if LocaltimeRollover is set. // #define UL_PICK_TIME_FIELD(pEntry, tflocal,tfgmt) \ ((pEntry->Flags.LocaltimeRollover || \ (pEntry->Format != HttpLoggingTypeW3C)) ? (tflocal) : (tfgmt)) #define DEFAULT_LOG_FILE_EXTENSION L"log" #define DEFAULT_LOG_FILE_EXTENSION_PLUS_DOT L".log" #define SIZE_OF_GMT_OFFSET (6) #define IS_LOGGING_DISABLED(g) \ ((g) == NULL || \ (g)->LoggingConfig.Flags.Present == 0 || \ (g)->LoggingConfig.LoggingEnabled == FALSE) // // UlpWriteW3CLogRecord attempts to use a buffer size upto this // #define UL_DEFAULT_WRITE_BUFFER_LEN (512) // // When a log field exceeds its limit it's replaced by // the following default warning string. // #define LOG_FIELD_TOO_BIG "..." // // No log record can be longer than this. Applies to all // log formats. // #define MAX_LOG_RECORD_LEN (10240) // // Any log field provided to the driver enforced by some // sanity limit. // #define MAX_LOG_DEFAULT_FIELD_LEN (64) // // WARNING: Logging capturing functions, especially the W3C // one has been designed with respect to the above hard coded // numbers. If you change any of the above numbers,you SHOULD // review the capturing functions to avoid the buffer overruns. // See also the MAX_LOG_RECORD_ALLOCATION_LENGTH down below. // // // W3C Capture and complete functions will allocate and use this // much extra space for non-string fields for cache-miss case. // This is used for worst case allocation. // #define MAX_W3C_FIX_FIELD_OVERHEAD \ (/* Date */ W3C_DATE_FIELD_LEN + 1 + \ /* Time */ W3C_TIME_FIELD_LEN + 1 + \ /* ServerPort */ MAX_USHORT_STR + 1 + \ /* PVersion */ UL_HTTP_VERSION_LENGTH + 1 + \ /* PStatus */ UL_MAX_HTTP_STATUS_CODE_LENGTH + 1 + \ /* Win32Status */ MAX_ULONG_STR + 1 + \ /* SubStatus */ MAX_USHORT_STR + 1 + \ /* BSent */ MAX_ULONGLONG_STR + 1 + \ /* BReceived */ MAX_ULONGLONG_STR + 1 + \ /* TTaken */ MAX_ULONGLONG_STR + 1 + \ /* "\r\n\0" */ 3 \ ) // // W3C complete function will allocate and use this much // much extra space for non-string fields for cache-hit // case. This is used for worst case allocation. // #define MAX_W3C_CACHE_FIELD_OVERHEAD \ (/* Date */ W3C_DATE_FIELD_LEN + 1 + \ /* Time */ W3C_TIME_FIELD_LEN + 1 + \ /* UserName "- "*/ 2 + \ /* ClientIp */ MAX_IP_ADDR_STRING_LEN + 1 + \ /* PVersion */ UL_HTTP_VERSION_LENGTH + 1 + \ /* PStatus */ UL_MAX_HTTP_STATUS_CODE_LENGTH + 1 + \ /* Win32Status */ MAX_ULONG_STR + 1 + \ /* SubStatus */ MAX_USHORT_STR + 1 + \ /* BSent */ MAX_ULONGLONG_STR + 1 + \ /* BReceived */ MAX_ULONGLONG_STR + 1 + \ /* TTaken */ MAX_ULONGLONG_STR + 1 + \ /* "\r\n\0" */ 3 \ ) // // Similar definitions for NCSA and IIS formats. // #define MAX_NCSA_CACHE_FIELD_OVERHEAD \ (/* ClientIp */ MAX_IP_ADDR_STRING_LEN + 1 + \ /* Fix Dash */ 2 + \ /* UserName */ 2 + \ /* Date & Time */ NCSA_FIX_DATE_AND_TIME_FIELD_SIZE + \ /* PVersion " */ UL_HTTP_VERSION_LENGTH + 1 + 1 + \ /* PStatus */ UL_MAX_HTTP_STATUS_CODE_LENGTH + 1 + \ /* BSent */ MAX_ULONGLONG_STR + \ /* \r\n\0 */ 3 \ ) #define MAX_IIS_CACHE_FIELD_OVERHEAD \ (/* Client Ip */ MAX_IP_ADDR_STRING_LEN + 2 + \ /* UserName */ 3 + \ /* Date & Time */ IIS_MAX_DATE_AND_TIME_FIELD_SIZE + \ /* TTaken */ MAX_ULONGLONG_STR + 2 + \ /* BReceived */ MAX_ULONGLONG_STR + 2 + \ /* BSent */ MAX_ULONGLONG_STR + 2 + \ /* PStatus */ UL_MAX_HTTP_STATUS_CODE_LENGTH + 2 + \ /* Win32Status */ MAX_ULONG_STR + 2 \ ) // // Default IIS fragments must be big enough to hold the max-length fields. // C_ASSERT(IIS_LOG_LINE_DEFAULT_FIRST_FRAGMENT_ALLOCATION_SIZE >= (/* ClientIp */ 2 + MAX_LOG_DEFAULT_FIELD_LEN + /* UserName */ 2 + MAX_LOG_USERNAME_FIELD_LEN + /* Date&Time */ 2 + IIS_MAX_DATE_AND_TIME_FIELD_SIZE)); C_ASSERT(IIS_LOG_LINE_DEFAULT_FIRST_FRAGMENT_ALLOCATION_SIZE >= (/* ServiceName */ 2 + MAX_LOG_DEFAULT_FIELD_LEN + /* ServerName */ 2 + MAX_LOG_DEFAULT_FIELD_LEN + /* ServerIp */ 2 + MAX_LOG_DEFAULT_FIELD_LEN + /* TimeTaken */ 2 + MAX_ULONGLONG_STR + /* BytesReceived */ 2 + MAX_ULONGLONG_STR + /* BytesSend */ 2 + MAX_ULONGLONG_STR + /* Protocol St. */ 2 + UL_MAX_HTTP_STATUS_CODE_LENGTH + /* Win32 St. */ 2 + MAX_ULONG_STR )); #define IIS_LOG_LINE_MAX_THIRD_FRAGMENT_SIZE \ (/* Method */ 2 + MAX_LOG_METHOD_FIELD_LEN + \ /* UriQuery */ 2 + MAX_LOG_EXTEND_FIELD_LEN + \ /* UriStem */ 2 + MAX_LOG_EXTEND_FIELD_LEN + \ /* "r\n\0" */ 3) C_ASSERT(UL_ANSI_LOG_LINE_BUFFER_SIZE == (IIS_LOG_LINE_DEFAULT_FIRST_FRAGMENT_ALLOCATION_SIZE + IIS_LOG_LINE_DEFAULT_SECOND_FRAGMENT_ALLOCATION_SIZE + IIS_LOG_LINE_DEFAULT_THIRD_FRAGMENT_ALLOCATION_SIZE) ); // // Maximum log record allocation for W3C format; // // MAX_LOG_RECORD_LEN : Upper limit for log record // + 16 Bytes : 4 * (sizeof(LOG_FIELD_TOO_BIG) + // SeparatorSpace:' ') // : For UserAgent,Cookie,Referer,Host // + MAX_W3C_FIX_FIELD_OVERHEAD : To be able to ensure reserved space for // : post-generated log fields. #define MAX_LOG_RECORD_ALLOCATION_LENGTH \ (MAX_LOG_RECORD_LEN + \ 4 * (STRLEN_LIT(LOG_FIELD_TOO_BIG) + 1) + \ MAX_W3C_FIX_FIELD_OVERHEAD \ ) // // Private function calls // NTSTATUS UlpConstructLogEntry( IN PHTTP_CONFIG_GROUP_LOGGING pConfig, OUT PUL_LOG_FILE_ENTRY * ppEntry ); VOID UlpInsertLogEntry( IN PUL_LOG_FILE_ENTRY pEntry ); NTSTATUS UlpAppendW3CLogTitle( IN PUL_LOG_FILE_ENTRY pEntry, OUT PCHAR pDestBuffer, IN OUT PULONG pBytesCopied ); VOID UlpInitializeTimers( VOID ); VOID UlpTerminateTimers( VOID ); NTSTATUS UlpRecycleLogFile( IN PUL_LOG_FILE_ENTRY pEntry ); NTSTATUS UlpHandleRecycle( IN OUT PVOID pContext ); __inline BOOLEAN UlpIsLogFileOverFlow( IN PUL_LOG_FILE_ENTRY pEntry, IN ULONG ReqdBytes ); VOID UlpEventLogWriteFailure( IN PUL_LOG_FILE_ENTRY pEntry, IN NTSTATUS Status ); NTSTATUS UlpFlushLogFile( IN PUL_LOG_FILE_ENTRY pEntry ); NTSTATUS UlpRefreshFileName( IN PUNICODE_STRING pDirectory, IN PUL_LOG_FILE_ENTRY pEntry ); VOID UlpGetGMTOffset(); VOID UlpLogHttpCacheHitWorker( IN PUL_LOG_DATA_BUFFER pLogData, IN PUL_CONFIG_GROUP_OBJECT pConfigGroup ); NTSTATUS UlpWriteToLogFile( IN PUL_LOG_FILE_ENTRY pFile, IN ULONG RecordSize, IN PCHAR pRecord, IN ULONG UsedOffset1, IN ULONG UsedOffset2 ); NTSTATUS UlpWriteToLogFileShared( IN PUL_LOG_FILE_ENTRY pFile, IN ULONG RecordSize, IN PCHAR pRecord, IN ULONG UsedOffset1, IN ULONG UsedOffset2 ); NTSTATUS UlpWriteToLogFileExclusive( IN PUL_LOG_FILE_ENTRY pFile, IN ULONG RecordSize, IN PCHAR pRecord, IN ULONG UsedOffset1, IN ULONG UsedOffset2 ); NTSTATUS UlpWriteToLogFileDebug( IN PUL_LOG_FILE_ENTRY pFile, IN ULONG RecordSize, IN PCHAR pRecord, IN ULONG UsedOffset1, IN ULONG UsedOffset2 ); NTSTATUS UlpMakeEntryInactive( IN OUT PUL_LOG_FILE_ENTRY pEntry ); PUL_LOG_DATA_BUFFER UlpAllocateLogDataBuffer( IN ULONG Size, IN PUL_INTERNAL_REQUEST pRequest, IN PUL_CONFIG_GROUP_OBJECT pConfigGroup ); NTSTATUS UlpCreateLogFile( IN OUT PUL_LOG_FILE_ENTRY pEntry, IN PUL_CONFIG_GROUP_OBJECT pConfigGroup ); #ifdef IMPLEMENT_SELECTIVE_LOGGING /***************************************************************************++ Routine Description: Simple macro will return TRUE if request status code type is matching with user's selection in the logging config. Arguments: pConfigGroup - Config Group for the logging configuration. StatusCode - Protocol status code. --***************************************************************************/ __inline BOOLEAN UlpIsRequestSelected( IN PUL_CONFIG_GROUP_OBJECT pConfigGroup, IN USHORT StatusCode ) { ASSERT(StatusCode <= UL_MAX_HTTP_STATUS_CODE); ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup)); // // The 4xx and 5xx status codes are considered an error. // // - 4xx: Client Error - The request contains bad syntax or cannot // be fulfilled // // - 5xx: Server Error - The server failed to fulfill an apparently // valid request // switch(pConfigGroup->LoggingConfig.SelectiveLogging) { case HttpLogAllRequests: return TRUE; break; case HttpLogSuccessfulRequests: return ((BOOLEAN) (StatusCode < 400 || StatusCode >= 600)); break; case HttpLogErrorRequests: return ((BOOLEAN) (StatusCode >= 400 && StatusCode < 600)); break; default: ASSERT(!"Invalid Selective Logging Type !"); break; } return FALSE; } #endif __inline NTSTATUS UlpCheckAndWrite( IN OUT PUL_LOG_FILE_ENTRY pEntry, IN PUL_CONFIG_GROUP_OBJECT pConfigGroup, IN PUL_LOG_DATA_BUFFER pLogData ) { NTSTATUS Status = STATUS_SUCCESS; PUL_STR_LOG_DATA pStrData = &pLogData->Data.Str; ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry)); ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup)); // // Check whether we have to create the log file first or not. // if (!pEntry->Flags.Active) { UlAcquirePushLockExclusive(&pEntry->EntryPushLock); // // Ping again to see if we have been blocked on the lock, and // somebody else already took care of the creation. // if (!pEntry->Flags.Active) { Status = UlpCreateLogFile(pEntry, pConfigGroup); } UlReleasePushLockExclusive(&pEntry->EntryPushLock); if (!NT_SUCCESS(Status)) { return Status; } } // // Now we know that the log file is there, therefore it's time to write. // Status = UlpWriteToLogFile ( pEntry, pStrData->Offset1 + pStrData->Offset2 + pLogData->Used, (PCHAR) pLogData->Line, pStrData->Offset1, pStrData->Offset2 ); return Status; } ULONG UlpGetLogLineSizeForW3C( IN PHTTP_LOG_FIELDS_DATA pLogData, IN ULONG Mask, IN BOOLEAN Utf8Enabled ); #define IS_PURE_CACHE_HIT(pUriEntry, pLogData) \ ((pUriEntry) && ((pLogData)->Flags.CacheAndSendResponse == 0)) #endif // _ULLOGP_H_