/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) Microsoft Corp. All rights reserved. // // FILE // // localfile.cpp // // SYNOPSIS // // Defines the class LocalFile. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #define STACK_ALLOC(type, num) (type*)_alloca(sizeof(type) * (num)) ////////// // Misc. enumerator constants. ////////// const LONG INET_LOG_FORMAT_INTERNET_STD = 0; const LONG INET_LOG_FORMAT_NCSA = 3; const LONG INET_LOG_FORMAT_ODBC_RADIUS = 0xFFFF; ////////// // Inserts a class attribute into the request. ////////// extern "C" HRESULT WINAPI InsertClassAttribute( IAttributesRaw* pRaw ) { ////////// // Check if this was proxied. ////////// PIASATTRIBUTE attr = IASPeekAttribute( pRaw, IAS_ATTRIBUTE_PROVIDER_TYPE, IASTYPE_ENUM ); if (attr && attr->Value.Enumerator == IAS_PROVIDER_RADIUS_PROXY) { return S_OK; } ////////// // Check if Generate Class Attribute is disabled ////////// PIASATTRIBUTE generateClassAttr = IASPeekAttribute( pRaw, IAS_ATTRIBUTE_GENERATE_CLASS_ATTRIBUTE, IASTYPE_BOOLEAN ); if (generateClassAttr && generateClassAttr->Value.Boolean == FALSE) { return S_OK; } ////////// // Create a new class attribute // Do not remove any existing class attribute. ////////// ATTRIBUTEPOSITION pos; pos.pAttribute = IASClass::createAttribute(NULL); if (pos.pAttribute == NULL) { return E_OUTOFMEMORY; } ////////// // Insert into the request. ////////// HRESULT hr = pRaw->AddAttributes(1, &pos); IASAttributeRelease(pos.pAttribute); return hr; } LocalFile::LocalFile() throw () : computerNameLen(0) { } STDMETHODIMP LocalFile::Initialize() { // Get the Unicode computer name. WCHAR uniName[MAX_COMPUTERNAME_LENGTH + 1]; DWORD len = sizeof(uniName) / sizeof(WCHAR); if (!GetComputerNameW(uniName, &len)) { // If it failed, we'll just use an empty string. len = 0; } // Convert the Unicode to UTF-8. computerNameLen = IASUnicodeToUtf8(uniName, len, computerName); IASClass::initialize(); return Accountant::Initialize(); } STDMETHODIMP LocalFile::Shutdown() { log.Close(); return Accountant::Shutdown(); } STDMETHODIMP LocalFile::PutProperty(LONG Id, VARIANT *pValue) { if (pValue == NULL) { return E_INVALIDARG; } switch (Id) { case PROPERTY_ACCOUNTING_LOG_OPEN_NEW_FREQUENCY: { if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; } switch (V_I4(pValue)) { case IAS_LOGGING_UNLIMITED_SIZE: case IAS_LOGGING_WHEN_FILE_SIZE_REACHES: case IAS_LOGGING_DAILY: case IAS_LOGGING_WEEKLY: case IAS_LOGGING_MONTHLY: return log.SetPeriod( static_cast( V_I4(pValue) ) ); break; default: return E_INVALIDARG; } break; } case PROPERTY_ACCOUNTING_LOG_OPEN_NEW_SIZE: { if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; } if (V_I4(pValue) <= 0) { return E_INVALIDARG; } log.SetMaxSize(V_I4(pValue) * 0x100000ui64); break; } case PROPERTY_ACCOUNTING_LOG_FILE_DIRECTORY: { if (V_VT(pValue) != VT_BSTR) { return DISP_E_TYPEMISMATCH; } if (V_BSTR(pValue) == NULL) { return E_INVALIDARG; } return log.SetDirectory(V_BSTR(pValue)); break; } case PROPERTY_ACCOUNTING_LOG_IAS1_FORMAT: { if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; } switch (V_I4(pValue)) { case INET_LOG_FORMAT_ODBC_RADIUS: format = formatODBCRecord; break; case INET_LOG_FORMAT_INTERNET_STD: format = formatW3CRecord; break; default: return E_INVALIDARG; } break; } case PROPERTY_ACCOUNTING_LOG_DELETE_IF_FULL: { if (V_VT(pValue) != VT_BOOL) { return DISP_E_TYPEMISMATCH; } log.SetDeleteIfFull((V_BOOL(pValue)) != 0); break; } default: { return Accountant::PutProperty(Id, pValue); } } return S_OK; } void LocalFile::Process(IASTL::IASRequest& request) { // Do some custom preprocessing. switch (request.get_Request()) { case IAS_REQUEST_ACCOUNTING: { if (request.get_Response() == IAS_RESPONSE_INVALID) { request.SetResponse(IAS_RESPONSE_ACCOUNTING, S_OK); } break; } case IAS_REQUEST_ACCESS_REQUEST: { InsertClassAttribute(request); break; } default: { break; } } // Create a FormattedBuffer of the correct type. FormattedBuffer buffer((format == formatODBCRecord) ? '\"' : '\0'); RecordEvent(&buffer, request); } void LocalFile::InsertRecord( void* context, IASTL::IASRequest& request, const SYSTEMTIME& localTime, PATTRIBUTEPOSITION first, PATTRIBUTEPOSITION last ) { FormattedBuffer& buffer = *static_cast(context); // Invoke the currently configured formatter. (this->*format)(request, buffer, localTime, first, last); // We're done. buffer.endRecord(); } void LocalFile::Flush( void* context, IASTL::IASRequest& request, const SYSTEMTIME& localTime ) { FormattedBuffer& buffer = *static_cast(context); if (!buffer.empty()) { if (!log.Write( request.get_Protocol(), localTime, buffer.getBuffer(), buffer.getLength() )) { IASTL::issue_error(HRESULT_FROM_WIN32(ERROR_WRITE_FAULT)); } } } void LocalFile::formatODBCRecord( IASTL::IASRequest& request, FormattedBuffer& buffer, const SYSTEMTIME& localTime, PATTRIBUTEPOSITION firstPos, PATTRIBUTEPOSITION lastPos ) const { ////////// // Column 1: Computer name. ////////// buffer.append('\"'); buffer.append((PBYTE)computerName, computerNameLen); buffer.append('\"'); ////////// // Column 2: Service name. ////////// buffer.beginColumn(); switch (request.get_Protocol()) { case IAS_PROTOCOL_RADIUS: buffer.append((const BYTE*)"\"IAS\"", 5); break; case IAS_PROTOCOL_RAS: buffer.append((const BYTE*)"\"RAS\"", 5); break; } ////////// // Column 3: Record time. ////////// buffer.beginColumn(); buffer.appendDate(localTime); buffer.beginColumn(); buffer.appendTime(localTime); ////////// // Allocate a blank record. ////////// PATTRIBUTEPOSITION *firstField, *curField, *lastField; size_t nfield = schema.getNumFields() + 1; firstField = STACK_ALLOC(PATTRIBUTEPOSITION, nfield); memset(firstField, 0, sizeof(PATTRIBUTEPOSITION) * nfield); lastField = firstField + nfield; ////////// // Sort the attributes to coalesce multi-valued attributes. ////////// std::sort(firstPos, lastPos, IASTL::IASOrderByID()); ////////// // Add a null terminator. This will make it easier to handle multi-valued // attributes. ////////// lastPos->pAttribute = NULL; ////////// // Fill in the fields. ////////// PATTRIBUTEPOSITION curPos; DWORD lastSeen = (DWORD)-1; for (curPos = firstPos; curPos != lastPos; ++curPos) { // Only process if this is a new attribute type. if (curPos->pAttribute->dwId != lastSeen) { lastSeen = curPos->pAttribute->dwId; firstField[schema.getOrdinal(lastSeen)] = curPos; } } ////////// // Pack the record into the buffer. We skip field 0, since that's where // we map all the attributes we don't want to log. ////////// for (curField = firstField + 1; curField != lastField; ++curField) { buffer.beginColumn(); if (*curField) { buffer.append(*curField); } } } void LocalFile::formatW3CRecord( IASTL::IASRequest& request, FormattedBuffer& buffer, const SYSTEMTIME& localTime, PATTRIBUTEPOSITION firstPos, PATTRIBUTEPOSITION lastPos ) const { ////////// // Column 1: NAS-IP-Addresses ////////// PIASATTRIBUTE attr = IASPeekAttribute( request, RADIUS_ATTRIBUTE_NAS_IP_ADDRESS, IASTYPE_INET_ADDR ); if (attr == 0) { attr = IASPeekAttribute( request, IAS_ATTRIBUTE_CLIENT_IP_ADDRESS, IASTYPE_INET_ADDR ); } if (attr != 0) { buffer.append(attr->Value); } ////////// // Column 2: User-Name ////////// buffer.beginColumn(); attr = IASPeekAttribute(request, RADIUS_ATTRIBUTE_USER_NAME, IASTYPE_OCTET_STRING); if (attr) { buffer.append(attr->Value); } ////////// // Column 3: Record time. ////////// buffer.beginColumn(); buffer.appendDate(localTime); buffer.beginColumn(); buffer.appendTime(localTime); ////////// // Column 4: Service name. ////////// buffer.beginColumn(); switch (request.get_Protocol()) { case IAS_PROTOCOL_RADIUS: buffer.append("IAS"); break; case IAS_PROTOCOL_RAS: buffer.append("RAS"); break; } ////////// // Column 5: Computer name. ////////// buffer.beginColumn(); buffer.append((PBYTE)computerName, computerNameLen); ////////// // Pack the attributes into the buffer. ////////// PATTRIBUTEPOSITION curPos; for (curPos = firstPos; curPos != lastPos; ++curPos) { if (!schema.excludeFromLog(curPos->pAttribute->dwId)) { buffer.beginColumn(); buffer.append(curPos->pAttribute->dwId); buffer.beginColumn(); buffer.append(*(curPos->pAttribute)); } } }