/*++ BUILD Version: 0002 // Increment this if a change has global effects Copyright (c) 1992 Microsoft Corporation Module Name: perfnbt.c Abstract: This file implements the Extensible Objects for the LAN object types Created: Revision History: --*/ // // include files // #include #include #include #include #include #include #include #include #include #include #include "perfctr.h" // error message definition #include "perfmsg.h" #include "perfutil.h" #include "perfnbt.h" #include "datanbt.h" // New header file for getting nbt data #pragma warning (disable : 4201) #include #include #pragma warning (default : 4201) enum eSTATE { NBT_RECONNECTING, // waiting for the worker thread to run NbtConnect NBT_IDLE, // not Transport connection NBT_ASSOCIATED, // associated with an address element NBT_CONNECTING, // establishing Transport connection NBT_SESSION_INBOUND, // waiting for a session request after tcp connection setup inbound NBT_SESSION_WAITACCEPT, // waiting for accept after a listen has been satisfied NBT_SESSION_OUTBOUND, // waiting for a session response after tcp connection setup NBT_SESSION_UP, // got positive response NBT_DISCONNECTING, // sent a disconnect down to Tcp, but it hasn't completed yet NBT_DISCONNECTED // a session has been disconnected but not closed with TCP yet }; // // References to constants which initialize the Object type definitions // extern NBT_DATA_DEFINITION NbtDataDefinition; #define NBT_CONNECTION_NAME_LENGTH 17 #define NETBIOS_NAME_SIZE NBT_CONNECTION_NAME_LENGTH-1 // // Nbt data structures // typedef struct _NBT_DEVICE_DATA { HANDLE hFileHandle; UNICODE_STRING DeviceName; } NBT_DEVICE_DATA, *PNBT_DEVICE_DATA; PNBT_DEVICE_DATA pNbtDeviceData; int MaxNbtDeviceName; int NumberOfNbtDevices; // initial count - will update to last PVOID pNbtDataBuffer = NULL; int NbtDataBufferSize; DWORD dwNbtRefCount = 0; // HANDLE NbtHandle = INVALID_HANDLE_VALUE; // Handle of Nbt Device #define NBT_CONTROLLING_STREAM "CSB" // Ignore Controlling Stream XEB #define NBT_LISTEN_CONNECTION 3 // All NBT connections with type <= 3, // are just listening for clients // The error value returned by the perfctrs.dll when an error occurs while we // are getting the data for the NBT connections. // The error codes we get from the socket calls (OpenStream(), s_ioctl(), // getmsg()) are Unix errors, not Dos or Windows errors. Hopefully, somebody // will implement the conversion from these errors to Windows errors. // The error value is not used within the Collect data routine because this // routine shouldn't return an error in case it fails to collect Nbt data from // connections. In this case, it just returns the buffer it was supposed to // place the data into, unchanged. #define ERROR_NBT_NET_RESPONSE \ (RtlNtStatusToDosError(STATUS_INVALID_NETWORK_RESPONSE)) #define BUFF_SIZE 650 PM_OPEN_PROC OpenNbtPerformanceData; PM_COLLECT_PROC CollectNbtPerformanceData; PM_CLOSE_PROC CloseNbtPerformanceData; //------------------------------------------------------------------------ NTSTATUS OpenNbt( IN char *path, OUT PHANDLE pHandle, OUT UNICODE_STRING *uc_name_string ) /*++ Routine Description: This function opens a stream. Arguments: path - path to the STREAMS driver oflag - currently ignored. In the future, O_NONBLOCK will be relevant. ignored - not used Return Value: An NT handle for the stream, or INVALID_HANDLE_VALUE if unsuccessful. --*/ { HANDLE StreamHandle; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; STRING name_string; // UNICODE_STRING uc_name_string; NTSTATUS status; RtlInitString(&name_string, path); RtlAnsiStringToUnicodeString(uc_name_string, &name_string, TRUE); InitializeObjectAttributes( &ObjectAttributes, uc_name_string, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = NtCreateFile( &StreamHandle, SYNCHRONIZE | FILE_READ_DATA , // SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, 0, NULL, 0); // RtlFreeUnicodeString(&uc_name_string); *pHandle = StreamHandle; return(status); } // Open_nbt NTSTATUS DeviceIoCtrl( IN HANDLE fd, IN PVOID ReturnBuffer, IN ULONG BufferSize, IN ULONG Ioctl ) /*++ Routine Description: This procedure performs an ioctl(I_STR) on a stream. Arguments: fd - NT file handle iocp - pointer to a strioctl structure Return Value: 0 if successful, -1 otherwise. --*/ { NTSTATUS status; TDI_REQUEST_QUERY_INFORMATION QueryInfo; IO_STATUS_BLOCK iosb; PVOID pInput; ULONG SizeInput; if (Ioctl == IOCTL_TDI_QUERY_INFORMATION) { pInput = &QueryInfo; QueryInfo.QueryType = TDI_QUERY_ADAPTER_STATUS; // node status or whatever SizeInput = sizeof(TDI_REQUEST_QUERY_INFORMATION); } else { pInput = NULL; SizeInput = 0; } status = NtDeviceIoControlFile( fd, // Handle NULL, // Event NULL, // ApcRoutine NULL, // ApcContext &iosb, // IoStatusBlock Ioctl, // IoControlCode pInput, // InputBuffer SizeInput, // InputBufferSize (PVOID) ReturnBuffer, // OutputBuffer BufferSize); // OutputBufferSize if (status == STATUS_PENDING) { status = NtWaitForSingleObject( fd, // Handle TRUE, // Alertable NULL); // Timeout } return(status); } // DeviceIoCtrl PCHAR printable( IN PCHAR string, IN PCHAR StrOut ) /*++ Routine Description: This procedure converts non prinatble characaters to periods ('.') Arguments: string - the string to convert StrOut - ptr to a string to put the converted string into Return Value: a ptr to the string that was converted (Strout) --*/ { PCHAR Out; PCHAR cp; LONG i; Out = StrOut; for (cp = string, i= 0; i < NETBIOS_NAME_SIZE; cp++,i++) { if (isprint(*cp)) { *Out++ = *cp; continue; } if (*cp >= 128) { /* extended characters are ok */ *Out++ = *cp; continue; } *Out++ = '.'; } return(StrOut); } // printable #pragma warning ( disable : 4127) DWORD OpenNbtPerformanceData ( IN LPWSTR dwVoid // not used by this routine ) /*++ Routine Description: This routine will open the Nbt device and remember the handle returned by the device. Arguments: None. Return Value: ERROR_NBT_NET_RESPONSE if unable to open NBT stream device ERROR_SUCCESS if open was successful --*/ { PCHAR SubKeyLinkage=(PCHAR)"system\\currentcontrolset\\services\\netbt\\linkage"; PCHAR Linkage=(PCHAR)"Export"; CHAR *pBuffer = NULL; CHAR *lpLocalDeviceNames; LONG status, status2; DWORD Type; ULONG size; HKEY Key; HANDLE hFileHandle; UNICODE_STRING fileString; NTSTATUS ntstatus; PNBT_DEVICE_DATA pTemp; UNREFERENCED_PARAMETER (dwVoid); MonOpenEventLog(); REPORT_INFORMATION (NBT_OPEN_ENTERED, LOG_VERBOSE); if (InterlockedIncrement(&dwNbtRefCount) == 1) { status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKeyLinkage, 0, KEY_READ, &Key); if (status == ERROR_SUCCESS) { // now read the linkage values size = 0; pBuffer = NULL; status2 = RegQueryValueEx(Key, Linkage, NULL, &Type, (LPBYTE)pBuffer, &size); if ((size > 0) && ((status2 == ERROR_MORE_DATA) || (status2 == ERROR_SUCCESS))) { pBuffer = RtlAllocateHeap( RtlProcessHeap(), 0, size); if (pBuffer == NULL) { RegCloseKey(Key); return ERROR_OUTOFMEMORY; } status2 = RegQueryValueEx(Key, Linkage, NULL, &Type, (LPBYTE)pBuffer, &size); } RegCloseKey(Key); if (status2 != ERROR_SUCCESS) { if (pBuffer != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, pBuffer); } return ERROR_SUCCESS; } } else { return ERROR_SUCCESS; } if (pBuffer == NULL) { return ERROR_SUCCESS; } lpLocalDeviceNames = pBuffer; while (TRUE) { if (*lpLocalDeviceNames == '\0') { break; } ntstatus = OpenNbt (lpLocalDeviceNames, &hFileHandle, &fileString); if (ntstatus == ERROR_SUCCESS) { if (NumberOfNbtDevices == 0) { // allocate memory to hold the device data pNbtDeviceData = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NBT_DEVICE_DATA)); if (pNbtDeviceData == NULL) { RtlFreeUnicodeString(&fileString); if (pBuffer) { RtlFreeHeap(RtlProcessHeap(), 0, pBuffer); } return ERROR_OUTOFMEMORY; } } else { // resize to hold multiple devices pTemp = RtlReAllocateHeap(RtlProcessHeap(), 0, pNbtDeviceData, sizeof(NBT_DEVICE_DATA) * (NumberOfNbtDevices + 1)); if (pTemp == NULL) { NtClose(hFileHandle); RtlFreeUnicodeString(&fileString); RtlFreeHeap(RtlProcessHeap(), 0, pNbtDeviceData); pNbtDeviceData = NULL; REPORT_ERROR (TDI_PROVIDER_STATS_MEMORY, LOG_USER); break; } else { pNbtDeviceData = pTemp; } } // build the Data structure for this device instance pNbtDeviceData[NumberOfNbtDevices].hFileHandle = hFileHandle; pNbtDeviceData[NumberOfNbtDevices].DeviceName.MaximumLength = fileString.MaximumLength; pNbtDeviceData[NumberOfNbtDevices].DeviceName.Length = fileString.Length; pNbtDeviceData[NumberOfNbtDevices].DeviceName.Buffer = fileString.Buffer; NumberOfNbtDevices++; if (fileString.MaximumLength > MaxNbtDeviceName) { MaxNbtDeviceName = fileString.MaximumLength; } } // ntstatus OK else { RtlFreeUnicodeString(&fileString); } // increment to the next device string // lpLocalDeviceNames += strlen(lpLocalDeviceNames) + 1; // we only support one device at this point since we cannot // tell which Connection goes with which device break; } // while TRUE } REPORT_SUCCESS (NBT_OPEN_PERFORMANCE_DATA, LOG_DEBUG); if (pBuffer) { RtlFreeHeap(RtlProcessHeap(), 0, pBuffer); } return ERROR_SUCCESS; } #pragma warning ( default : 4127) DWORD CollectNbtPerformanceData( IN LPWSTR lpValueName, IN OUT LPVOID *lppData, IN OUT LPDWORD lpcbTotalBytes, IN OUT LPDWORD lpNumObjectTypes ) /*++ Routine Description: This routine will return the data for the Nbt counters. IN LPWSTR lpValueName pointer to a wide character null-terminated string passed by the registry. IN OUT LPVOID *lppData IN: pointer to the address of the buffer to receive the completed PerfDataBlock and subordinate structures. This routine will append its data to the buffer starting at the point referenced by *lppData. OUT: points to the first byte after the data structure added by this routine. This routine updated the value at lppdata after appending its data. IN OUT LPDWORD lpcbTotalBytes IN: the address of the DWORD that tells the size in bytes of the buffer referenced by the lppData argument OUT: the number of bytes added by this routine is writted to the DWORD pointed to by this argument IN OUT LPDWORD lpNumObjectTypes IN: the address of the DWORD to receive the number of objects added by this routine OUT: the number of objects added by this routine is writted to the DWORD pointed to by this argument Return Value: ERROR_MORE_DATA if buffer passed is too small to hold data any error conditions encountered are reported to the event log if event logging is enabled. ERROR_SUCCESS if success or any other error. Errors, however are also reported to the event log. --*/ { // Variables for reformatting the Nbt data LARGE_INTEGER UNALIGNED *pliCounter; NBT_DATA_DEFINITION *pNbtDataDefinition; PPERF_OBJECT_TYPE pNbtObject; ULONG SpaceNeeded; UNICODE_STRING ConnectionName; ANSI_STRING AnsiConnectionName; WCHAR ConnectionNameBuffer[NBT_CONNECTION_NAME_LENGTH + 20]; #if 0 // be sure to check the reference below... WCHAR DeviceNameBuffer[NBT_CONNECTION_NAME_LENGTH + 1 + 128]; #endif CHAR AnsiConnectionNameBuffer[NBT_CONNECTION_NAME_LENGTH + 1 + 20]; WCHAR TotalName[] = L"Total"; PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition; PERF_COUNTER_BLOCK *pPerfCounterBlock; CHAR NameOut[NETBIOS_NAME_SIZE +4]; // int ConnectionCounter = 0; /* this is not used anymore */ LARGE_INTEGER TotalReceived, TotalSent; DWORD dwDataReturn[2]; NTSTATUS status; tCONNECTION_LIST *pConList; tCONNECTIONS *pConns; LONG Count; int i; int NumberOfConnections = 5; // assume 5 to start PVOID pOldBuffer; if (lpValueName == NULL) { REPORT_INFORMATION (NBT_COLLECT_ENTERED, LOG_VERBOSE); } else { REPORT_INFORMATION_DATA (NBT_COLLECT_ENTERED, LOG_VERBOSE, lpValueName, (lstrlenW(lpValueName) * sizeof(WCHAR))); } // // define pointer for Object Data structure (NBT object def.) // pNbtDataDefinition = (NBT_DATA_DEFINITION *) *lppData; pNbtObject = (PPERF_OBJECT_TYPE) pNbtDataDefinition; if (!pNbtDeviceData || NumberOfNbtDevices == 0) { // // Error getting NBT info, so return 0 bytes, 0 objects and // log error // if (NumberOfNbtDevices > 0) { // only report an error if there are devices // returning data but they can't be read. REPORT_ERROR (NBT_IOCTL_INFO_ERROR, LOG_USER); } *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_SUCCESS; } if (!pNbtDataBuffer) { NbtDataBufferSize = 1024L; pNbtDataBuffer = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, NbtDataBufferSize ); if (!pNbtDataBuffer) { *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_SUCCESS; } } REPORT_SUCCESS (NBT_IOCTL_INFO_SUCCESS, LOG_VERBOSE); // Compute space needed to hold NBT data SpaceNeeded = sizeof(NBT_DATA_DEFINITION) + (NumberOfConnections * NumberOfNbtDevices * (sizeof(PERF_INSTANCE_DEFINITION) + QWORD_MULTIPLE((NBT_CONNECTION_NAME_LENGTH + 1) * sizeof(WCHAR)) + QWORD_MULTIPLE(MaxNbtDeviceName) + SIZE_OF_NBT_DATA)); if ( *lpcbTotalBytes < SpaceNeeded ) { dwDataReturn[0] = *lpcbTotalBytes; dwDataReturn[1] = SpaceNeeded; REPORT_WARNING_DATA (NBT_DATA_BUFFER_SIZE, LOG_DEBUG, &dwDataReturn, sizeof (dwDataReturn)); return ERROR_MORE_DATA; } AnsiConnectionName.Length = AnsiConnectionName.MaximumLength = sizeof(AnsiConnectionNameBuffer); AnsiConnectionName.Buffer = AnsiConnectionNameBuffer; // // If here, then there's a object to display so initialize // the Object data structure in the buffer passed to us. // RtlMoveMemory(pNbtDataDefinition, &NbtDataDefinition, sizeof(NBT_DATA_DEFINITION)); // // point to where the first instance of this will be (if we find one. // pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *) (pNbtDataDefinition + 1); TotalReceived.LowPart = 0; // initialize counters TotalSent.LowPart = 0; TotalReceived.HighPart = 0; // initialize counters TotalSent.HighPart = 0; // NOTE:- we only support NumberOfNbtDevices == 1 since // DeviceIoCtrl can't tell which connection is for which NBT device for (i=0; i < NumberOfNbtDevices; i++) { if (pNbtDeviceData[i].hFileHandle == 0 || pNbtDeviceData[i].hFileHandle == INVALID_HANDLE_VALUE) { continue; } status = STATUS_BUFFER_OVERFLOW; while (status == STATUS_BUFFER_OVERFLOW) { status = DeviceIoCtrl ( pNbtDeviceData[i].hFileHandle, pNbtDataBuffer, NbtDataBufferSize, IOCTL_NETBT_GET_CONNECTIONS); if (status == STATUS_BUFFER_OVERFLOW) { // resize to hold multiple devices NbtDataBufferSize += 1024L; pOldBuffer = pNbtDataBuffer; pNbtDataBuffer = RtlReAllocateHeap(RtlProcessHeap(), 0, pNbtDataBuffer, NbtDataBufferSize); if (pNbtDataBuffer == NULL || NbtDataBufferSize == 0x0FFFFL) { *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; RtlFreeHeap(RtlProcessHeap(), 0, pOldBuffer); pNbtDataBuffer = NULL; return ERROR_SUCCESS; } } } // while Buffer overflow pConList = (tCONNECTION_LIST *) pNbtDataBuffer; Count = pConList->ConnectionCount; pConns = pConList->ConnList; if (Count == 0) { continue; } if (NumberOfConnections < Count) { NumberOfConnections = Count; // Better check space needed to hold NBT data again // this is because the Count could be hugh SpaceNeeded = sizeof(NBT_DATA_DEFINITION) + (NumberOfConnections * NumberOfNbtDevices * (sizeof(PERF_INSTANCE_DEFINITION) + QWORD_MULTIPLE((NBT_CONNECTION_NAME_LENGTH + 1) * sizeof(WCHAR)) + QWORD_MULTIPLE(MaxNbtDeviceName ) + SIZE_OF_NBT_DATA)); if ( *lpcbTotalBytes < SpaceNeeded ) { dwDataReturn[0] = *lpcbTotalBytes; dwDataReturn[1] = SpaceNeeded; REPORT_WARNING_DATA (NBT_DATA_BUFFER_SIZE, LOG_DEBUG, &dwDataReturn, sizeof (dwDataReturn)); return ERROR_MORE_DATA; } } while ( Count-- ) { if (pConns->State == NBT_SESSION_UP) { // only care about UP connection if (pConns->RemoteName[0]) { AnsiConnectionName.Length = (USHORT)sprintf ( AnsiConnectionNameBuffer, "%16.16s", printable(pConns->RemoteName, NameOut)); } else if (pConns->LocalName[0]) { if (pConns->LocalName[NETBIOS_NAME_SIZE-1] < ' ') { AnsiConnectionName.Length = (USHORT)sprintf ( AnsiConnectionNameBuffer, "%15.15s%02.2X", printable(pConns->LocalName, NameOut), pConns->LocalName[NETBIOS_NAME_SIZE-1]); } else { AnsiConnectionName.Length = (USHORT)sprintf ( AnsiConnectionNameBuffer, "%16.16s", printable(pConns->LocalName, NameOut)); } } else { AnsiConnectionNameBuffer[0] = ' '; AnsiConnectionName.Length = 1; } ConnectionName.Length = ConnectionName.MaximumLength = sizeof(ConnectionNameBuffer); ConnectionName.Buffer = ConnectionNameBuffer; RtlAnsiStringToUnicodeString (&ConnectionName, &AnsiConnectionName, FALSE); // no need to put in device name since we can // only support one device #if 0 lstrcpyW (DeviceNameBuffer, pNbtDeviceData[i].DeviceName.Buffer); lstrcatW (DeviceNameBuffer, L" "); lstrcatW (DeviceNameBuffer, ConnectionNameBuffer); ConnectionName.Length = lstrlenW (DeviceNameBuffer) * sizeof(WCHAR); ConnectionName.MaximumLength = sizeof(DeviceNameBuffer); ConnectionName.Buffer = DeviceNameBuffer; #endif // // load instance data into buffer // MonBuildInstanceDefinition (pPerfInstanceDefinition, (PVOID *) &pPerfCounterBlock, 0, 0, (DWORD)PERF_NO_UNIQUE_ID, // no unique ID, Use the name instead // ConnectionCounter++, &ConnectionName); // // adjust object size values to include new instance // pNbtObject->NumInstances++; // // initialize this instance's counter block pPerfCounterBlock->ByteLength = SIZE_OF_NBT_DATA; pliCounter = (LARGE_INTEGER UNALIGNED * ) (pPerfCounterBlock + 2); *(pliCounter++) = pConns->BytesRcvd; TotalReceived.QuadPart = TotalReceived.QuadPart + pConns->BytesRcvd.QuadPart; *pliCounter++ = pConns->BytesSent; TotalSent.QuadPart = TotalSent.QuadPart + pConns->BytesSent.QuadPart; pliCounter->QuadPart = pConns->BytesRcvd.QuadPart + pConns->BytesSent.QuadPart; // // update pointer for next instance // pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *) (((PBYTE) pPerfCounterBlock) + SIZE_OF_NBT_DATA); } // pConns->State == NBT_SESSION_UP pConns++; } // while ( Count-- ) } // for i < NumberOfNbtDevices // The last instance definition contains the total data from all the // displayed connections RtlInitUnicodeString (&ConnectionName, TotalName); MonBuildInstanceDefinition (pPerfInstanceDefinition, (PVOID *) &pPerfCounterBlock, 0, 0, // ConnectionCounter++, (DWORD)PERF_NO_UNIQUE_ID, // no unique ID, Use the name instead &ConnectionName); // // adjust object size values to include new instance // pNbtObject->NumInstances++; pNbtObject->TotalByteLength += sizeof (PERF_INSTANCE_DEFINITION) + SIZE_OF_NBT_DATA; // initialize counter block for this instance pPerfCounterBlock->ByteLength = SIZE_OF_NBT_DATA; // load counters pliCounter = (LARGE_INTEGER UNALIGNED * ) (pPerfCounterBlock + 2); (*(pliCounter++)) = TotalReceived; (*(pliCounter++)) = TotalSent; pliCounter->QuadPart = TotalReceived.QuadPart + TotalSent.QuadPart; pliCounter++; // Set returned values *lppData = (LPVOID)pliCounter; *lpNumObjectTypes = NBT_NUM_PERF_OBJECT_TYPES; *lpcbTotalBytes = (DWORD)((LPBYTE)pliCounter-(LPBYTE)pNbtObject); pNbtDataDefinition->NbtObjectType.TotalByteLength = *lpcbTotalBytes; REPORT_INFORMATION (NBT_COLLECT_DATA, LOG_DEBUG); return ERROR_SUCCESS; } DWORD CloseNbtPerformanceData( ) /*++ Routine Description: This routine closes the open handles to Nbt devices. Arguments: None. Return Value: ERROR_SUCCESS --*/ { int i; REPORT_INFORMATION (NBT_CLOSE, LOG_VERBOSE); if (InterlockedDecrement(&dwNbtRefCount) == 0) { if (pNbtDeviceData) { for (i=0; i < NumberOfNbtDevices; i++) { if (pNbtDeviceData[i].DeviceName.Buffer) { RtlFreeUnicodeString(&(pNbtDeviceData[i].DeviceName)); } if (pNbtDeviceData[i].hFileHandle) { NtClose (pNbtDeviceData[i].hFileHandle); } } RtlFreeHeap( RtlProcessHeap(), 0, pNbtDeviceData); pNbtDeviceData = NULL; NumberOfNbtDevices = 0; } if (pNbtDataBuffer) { RtlFreeHeap( RtlProcessHeap(), 0, pNbtDataBuffer); pNbtDataBuffer = NULL; NbtDataBufferSize = 0; } } MonCloseEventLog(); return ERROR_SUCCESS; }