From: Itamar Tal <ita...@guardicore.com> this patch add support for some more functionality in the qemu-guest-agent, both for windows and linux. Main added features are: - interface listing in Windows - Process list in Windows - network connections enumeration in Windows - file delete for both Windows and Posix - file stat() for both Windows and Posix - system uptime for both Windows and Posix
Itamar, Guardicore ita...@guardicore.com --- qga/commands-posix.c | 49 +++++ qga/commands-win32.c | 552 +++++++++++++++++++++++++++++++++++++++++++++++- qga/commands.c | 25 +++ qga/guest-agent-core.h | 2 + qga/main.c | 9 + qga/qapi-schema.json | 172 +++++++++++++++ qga/win32-definitions.h | 115 ++++++++++ qga/win32-iptypes.h | 411 +++++++++++++++++++++++++++++++++++ 8 files changed, 1332 insertions(+), 3 deletions(-) create mode 100644 qga/win32-definitions.h create mode 100644 qga/win32-iptypes.h diff --git a/qga/commands-posix.c b/qga/commands-posix.c index ba8de62..15e39fc 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -23,6 +23,7 @@ #include <string.h> #include <sys/stat.h> #include <inttypes.h> +#include <sys/sysinfo.h> #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -2456,3 +2457,51 @@ void ga_command_state_init(GAState *s, GACommandState *cs) #endif ga_command_state_add(cs, guest_file_init, NULL); } + +uint64_t qmp_guest_uptime(Error **errp) +{ + struct sysinfo sys_info; + if (sysinfo(&sys_info)) + { + error_setg(errp, "Failed reading system info"); + } + + return sys_info.uptime; +} + +GuestFileStat *qmp_guest_file_stat(const char *path, Error **errp) +{ + GuestFileStat * file_stat = g_malloc0(sizeof(GuestFileStat)); + struct stat file_os_stat; + + if (stat(path, &file_os_stat)) + { + error_setg(errp, "Failed to get file stat() for file '%s'", path); + return NULL; + } + + file_stat->mode = file_os_stat.st_mode; + file_stat->inode = file_os_stat.st_ino; + file_stat->dev = file_os_stat.st_dev; + file_stat->nlink = file_os_stat.st_nlink; + file_stat->uid = file_os_stat.st_uid; + file_stat->gid = file_os_stat.st_gid; + file_stat->size = file_os_stat.st_size; + file_stat->atime = file_os_stat.st_atime; + file_stat->mtime = file_os_stat.st_mtime; + file_stat->ctime = file_os_stat.st_ctime; + + return file_stat; +} + +GuestProcessInfoList *qmp_guest_get_process_list(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestActiveConnectionList *qmp_guest_get_active_connections(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 3ef0549..55d8f2e 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -19,6 +19,7 @@ #include "qga/guest-agent-core.h" #include "qga/vss-win32.h" #include "qga-qmp-commands.h" +#include "qga/win32-definitions.h" #include "qapi/qmp/qerror.h" #include "qemu/queue.h" @@ -26,6 +27,41 @@ #define SHTDN_REASON_FLAG_PLANNED 0x80000000 #endif +ULONG (__stdcall * GetAdaptersAddresses)( uint32_t Family, + uint32_t Flags, + void * Reserved, + IP_ADAPTER_ADDRESSES * AdapterAddresses, + uint32_t * SizePointer) = NULL; + +DWORD (__stdcall * GetExtendedTcpTable)(void * pTcpTable, + DWORD * pdwSize, + BOOL bOrder, + ULONG ulAf, + TCP_TABLE_CLASS TableClass, + ULONG Reserved) = NULL; + +DWORD (__stdcall * GetExtendedUdpTable)( void * pUdpTable, + DWORD * pdwSize, + BOOL bOrder, + ULONG ulAf, + UDP_TABLE_CLASS TableClass, + ULONG Reserved) = NULL; + +NTSTATUS (__stdcall * RtlUnicodeStringToAnsiString)(ANSI_STRING * DestinationString, + UNICODE_STRING * SourceString, + BOOLEAN AllocateDestinationString) = NULL; + +NTSTATUS (__stdcall * NtQueryInformationProcess)( HANDLE ProcessHandle, + PROCESSINFOCLASS ProcessInformationClass, + void * ProcessInformation, + uint32_t ProcessInformationLength, + uint32_t * ReturnLength) = NULL; + +NTSTATUS (__stdcall * NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, + void * SystemInformation, + uint32_t SystemInformationLength, + uint32_t * ReturnLength) = NULL; + /* multiple of 100 nanoseconds elapsed between windows baseline * (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */ #define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \ @@ -591,8 +627,132 @@ void qmp_guest_suspend_hybrid(Error **errp) GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) { - error_set(errp, QERR_UNSUPPORTED); - return NULL; + GuestNetworkInterfaceList * iface_list = NULL; + IP_ADAPTER_ADDRESSES * ip_adapter_addresses = NULL; + IP_ADAPTER_ADDRESSES * current_adapter = NULL; + uint32_t adapter_buff_size = 15 * 1024; + ULONG ret = 0; + + for (;;) + { + ip_adapter_addresses = g_malloc0(adapter_buff_size); + if (NULL == ip_adapter_addresses) + { + error_setg(errp, "Failed allocating adapters info buffer"); + return NULL; + } + + ret = GetAdaptersAddresses( AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_INCLUDE_PREFIX, + NULL, + ip_adapter_addresses, + &adapter_buff_size); + if (NO_ERROR != ret) + { + (void)g_free(ip_adapter_addresses); + ip_adapter_addresses = NULL; + + if (ERROR_BUFFER_OVERFLOW == ret) + { + adapter_buff_size <<= 1; + continue; + } + + error_setg(errp, "Error quering network adapters info"); + return NULL; + } + + break; + } + + /* add all adapters to the list */ + for ( current_adapter = ip_adapter_addresses; + NULL != current_adapter; + current_adapter = current_adapter->Next) + { + IP_ADAPTER_UNICAST_ADDRESS * ip_address = NULL; + UNICODE_STRING adapter_name_us; + ANSI_STRING adapter_name; + GuestNetworkInterfaceList * new_item = g_malloc0(sizeof(GuestNetworkInterfaceList)); + new_item->value = g_malloc0(sizeof(GuestNetworkInterface)); + new_item->next = iface_list; + iface_list = new_item; + + adapter_name_us.Buffer = current_adapter->FriendlyName; + adapter_name_us.Length = wcslen(current_adapter->FriendlyName) * sizeof(wchar_t); + adapter_name_us.MaximumLength = adapter_name_us.Length + sizeof(wchar_t); + adapter_name.MaximumLength = adapter_name_us.MaximumLength; + adapter_name.Length = 0; + adapter_name.Buffer = g_malloc0(adapter_name_us.MaximumLength); + + /* convert the adapter name to ansi string */ + (void)RtlUnicodeStringToAnsiString( &adapter_name, + &adapter_name_us, + FALSE); + new_item->value->name = adapter_name.Buffer; + + /* copy physical address */ + if (current_adapter->PhysicalAddressLength != 0) + { + int i = 0; + + new_item->value->hardware_address = g_malloc0(current_adapter->PhysicalAddressLength * 3 + 1); + + for (i = 0; i < current_adapter->PhysicalAddressLength; ++i) + { + sprintf(&new_item->value->hardware_address[i * 3], + "%02x:", current_adapter->PhysicalAddress[i]); + } + + new_item->value->hardware_address[i * 3 - 1] = '\0'; + new_item->value->has_hardware_address = true; + } + + /* build IP list */ + for ( ip_address = current_adapter->FirstUnicastAddress; + NULL != ip_address; + ip_address = ip_address->Next) + { + GuestIpAddressList * new_ip = NULL; + + /* skip unknown addresses */ + if ((AF_INET != ip_address->Address.lpSockaddr->sa_family) && + (AF_INET6 != ip_address->Address.lpSockaddr->sa_family)) + { + continue; + } + + /* allocate new item in the list */ + new_ip = g_malloc0(sizeof(GuestIpAddressList)); + new_ip->value = g_malloc0(sizeof(GuestIpAddress)); + new_item->value->has_ip_addresses = true; + + /* push the new IP at the head of the list */ + new_ip->next = new_item->value->ip_addresses; + new_item->value->ip_addresses = new_ip; + + + /* convert IP info by family */ + if (AF_INET == ip_address->Address.lpSockaddr->sa_family) + { + SOCKADDR_IN * in_addr = (SOCKADDR_IN *)ip_address->Address.lpSockaddr; + new_ip->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; + new_ip->value->ip_address = g_strdup(inet_ntoa(in_addr->sin_addr)); + } + else if (AF_INET6 == ip_address->Address.lpSockaddr->sa_family) + { + new_ip->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; + new_ip->value->ip_address = g_strdup("IPv6 not supported"); + } + } + } + + (void)g_free(ip_adapter_addresses); + + return iface_list; } int64_t qmp_guest_get_time(Error **errp) @@ -703,11 +863,73 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) return NULL; } +int ga_win_commands_init(void) +{ + HMODULE ntdll_module = GetModuleHandle("ntdll.dll"); + HMODULE iphlpapi_module = LoadLibrary("iphlpapi.dll"); + WSADATA wsa_data = {0}; + + if (WSAStartup(MAKEWORD(2,2), &wsa_data)) + { + g_critical("failed to initialize WSA engine"); + goto error; + } + + if ((NULL == ntdll_module) || + (NULL == iphlpapi_module)) + { + goto error; + } + + NtQuerySystemInformation = (void *)GetProcAddress(ntdll_module, "NtQuerySystemInformation"); + if (NULL == NtQuerySystemInformation) + { + goto error; + } + + RtlUnicodeStringToAnsiString = (void *)GetProcAddress(ntdll_module, "RtlUnicodeStringToAnsiString"); + if (NULL == RtlUnicodeStringToAnsiString) + { + goto error; + } + + NtQueryInformationProcess = (void *)GetProcAddress(ntdll_module, "NtQueryInformationProcess"); + if (NULL == NtQueryInformationProcess) + { + goto error; + } + + GetAdaptersAddresses = (void *)GetProcAddress(iphlpapi_module, "GetAdaptersAddresses"); + if (NULL == GetAdaptersAddresses) + { + goto error; + } + + GetExtendedTcpTable = (void *)GetProcAddress(iphlpapi_module, "GetExtendedTcpTable"); + if (NULL == GetExtendedTcpTable) + { + goto error; + } + + GetExtendedUdpTable = (void *)GetProcAddress(iphlpapi_module, "GetExtendedUdpTable"); + if (NULL == GetExtendedUdpTable) + { + goto error; + } + + return 1; + +error: + (void)FreeLibrary(iphlpapi_module); + + return 0; +} + /* add unsupported commands to the blacklist */ GList *ga_command_blacklist_init(GList *blacklist) { const char *list_unsupported[] = { - "guest-suspend-hybrid", "guest-network-get-interfaces", + "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus", "guest-set-user-password", "guest-get-memory-blocks", "guest-set-memory-blocks", @@ -742,3 +964,327 @@ void ga_command_state_init(GAState *s, GACommandState *cs) } ga_command_state_add(cs, guest_file_init, NULL); } + +uint64_t qmp_guest_uptime(Error **errp) +{ + uint64_t uptime_milli = GetTickCount(); + return uptime_milli / 1000; +} + +GuestFileStat *qmp_guest_file_stat(const char *path, Error **errp) +{ + GuestFileStat * file_stat = g_malloc0(sizeof(GuestFileStat)); + struct _stat file_os_stat = {0}; + + if (_stat(path, &file_os_stat)) + { + error_setg(errp, "Failed to get file stat() for file '%s': %d", path, (int)GetLastError()); + return NULL; + } + + file_stat->mode = file_os_stat.st_mode; + file_stat->inode = file_os_stat.st_ino; + file_stat->dev = file_os_stat.st_dev; + file_stat->nlink = file_os_stat.st_nlink; + file_stat->uid = file_os_stat.st_uid; + file_stat->gid = file_os_stat.st_gid; + file_stat->size = file_os_stat.st_size; + file_stat->atime = file_os_stat.st_atime; + file_stat->mtime = file_os_stat.st_mtime; + file_stat->ctime = file_os_stat.st_ctime; + + return file_stat; +} + +GuestProcessInfoList *qmp_guest_get_process_list(Error **errp) +{ + uint32_t bytes_needed = sizeof(SYSTEM_PROCESS_INFORMATION) * 20; + SYSTEM_PROCESS_INFORMATION * process_list = NULL; + SYSTEM_PROCESS_INFORMATION * process_list_iter = NULL; + GuestProcessInfoList * process_list_head = NULL; + GuestProcessInfoList * process_list_last = NULL; + NTSTATUS status = STATUS_SUCCESS; + + /* find the minimal buffer for the process list */ + for (;;) + { + process_list = g_malloc0(bytes_needed); + if (NULL == process_list) + { + error_setg(errp, "Failed allocating space for process list (%d bytes)", bytes_needed); + return NULL; + } + + /* query the process list (if enough bytes are given) */ + status = NtQuerySystemInformation( SystemProcessInformation, + process_list, + bytes_needed, + &bytes_needed); + if (STATUS_SUCCESS != status) + { + (void)g_free(process_list); + + if (STATUS_INFO_LENGTH_MISMATCH == status) + { + bytes_needed <<= 1; + continue; + } + + error_setg(errp, "Failed quering process list (status %08X)", (int)status); + return NULL; + } + + break; + } + + /* iterate the process list and build the JSON reply */ + for (process_list_iter = process_list;;) + { + GuestProcessInfoList * list_item = g_malloc0(sizeof(GuestProcessInfoList)); + ANSI_STRING process_name; + HANDLE process_handle = NULL; + UNICODE_STRING * process_image_path = NULL; + PROCESS_BASIC_INFORMATION process_basic_info = {0}; + + if (NULL == process_list_head) + { + process_list_head = list_item; + } + if (NULL != process_list_last) + { + process_list_last->next = list_item; + } + process_list_last = list_item; + + list_item->next = NULL; + list_item->value = g_malloc0(sizeof(GuestProcessInfo)); + list_item->value->process_id = (int)process_list_iter->UniqueProcessId; + list_item->value->session_id = (int)process_list_iter->SessionId; + + process_name.MaximumLength = process_list_iter->ImageName.MaximumLength; + process_name.Length = 0; + process_name.Buffer = g_malloc0(process_name.MaximumLength); + + /* convert the image name to ansi string */ + (void)RtlUnicodeStringToAnsiString( &process_name, + &process_list_iter->ImageName, + FALSE); + list_item->value->process_name = process_name.Buffer; + + if (!process_list_iter->NextEntryOffset) + { + break; + } + + process_handle = OpenProcess( PROCESS_QUERY_INFORMATION, + FALSE, + list_item->value->process_id); + if (NULL != process_handle) + { + status = NtQueryInformationProcess( process_handle, + ProcessBasicInformation, + &process_basic_info, + sizeof(PROCESS_BASIC_INFORMATION), + NULL); + if (STATUS_SUCCESS == status) + { + list_item->value->parent_id = (int)process_basic_info.InheritedFromUniqueProcessId; + } + + process_image_path = g_malloc0(sizeof(UNICODE_STRING) + 256 * sizeof(wchar_t)); + + status = NtQueryInformationProcess( process_handle, + ProcessImageFileName, + process_image_path, + sizeof(UNICODE_STRING) + 256 * sizeof(wchar_t), + NULL); + if (STATUS_SUCCESS == status) + { + process_name.MaximumLength = process_image_path->MaximumLength; + process_name.Length = 0; + process_name.Buffer = g_malloc0(process_name.MaximumLength); + + /* convert the image name to ansi string */ + (void)RtlUnicodeStringToAnsiString( &process_name, + process_image_path, + FALSE); + list_item->value->image_path = process_name.Buffer; + } + + (void)g_free(process_image_path); + + (void)CloseHandle(process_handle); + } + + process_list_iter = (void *)((uint32_t)process_list_iter + process_list_iter->NextEntryOffset); + } + + (void)g_free(process_list); + + return process_list_head; +} + +GuestActiveConnectionList *qmp_guest_get_active_connections(Error **errp) +{ + MIB_UDPTABLE_OWNER_MODULE * udp_table = NULL; + MIB_TCPTABLE_OWNER_MODULE * tcp_table = NULL; + DWORD bytes_needed = sizeof(MIB_UDPTABLE_OWNER_MODULE) * 30; + GuestActiveConnectionList * connections = NULL; + int entry_index = 0; + DWORD ret; + + /* get the UDP table */ + for (;;) + { + udp_table = g_malloc0(bytes_needed); + if (NULL == udp_table) + { + error_setg(errp, "Failed allocating active UDP connections table"); + return NULL; + } + + ret = GetExtendedUdpTable( udp_table, + &bytes_needed, + TRUE, + AF_INET, + UDP_TABLE_OWNER_MODULE, + 0); + if (NO_ERROR != ret) + { + (void)g_free(tcp_table); + udp_table = NULL; + + if (ERROR_INSUFFICIENT_BUFFER == ret) + { + bytes_needed <<= 1; + continue; + } + } + + break; + } + + /* add the UDP connections to the list backward */ + for ( entry_index = udp_table->dwNumEntries - 1; + entry_index >= 0; + --entry_index) + { + /* allocate new active connection item */ + GuestActiveConnectionList * new_item = g_malloc0(sizeof(GuestActiveConnectionList)); + new_item->value = g_malloc0(sizeof(GuestActiveConnection)); + + /* push the connection to the head of the list */ + new_item->next = connections; + connections = new_item; + + new_item->value->source_addr = g_strdup(inet_ntoa(*(struct in_addr *)&udp_table->table[entry_index].dwLocalAddr)); + new_item->value->source_port = htons(udp_table->table[entry_index].dwLocalPort); + new_item->value->owner_process_id = udp_table->table[entry_index].dwOwningPid; + new_item->value->if_family = GUEST_IP_ADDRESS_TYPE_IPV4; + new_item->value->protocol = GUEST_IP_PROTOCOL_UDP; + new_item->value->start_time = udp_table->table[entry_index].liCreateTimestamp.QuadPart; + } + + (void)g_free(udp_table); + udp_table = NULL; + + bytes_needed = sizeof(MIB_TCPROW_OWNER_PID) * 30; + + /* get the TCP table */ + for (;;) + { + tcp_table = g_malloc0(bytes_needed); + if (NULL == tcp_table) + { + error_setg(errp, "Failed allocating active connections table"); + return NULL; + } + + ret = GetExtendedTcpTable( tcp_table, + &bytes_needed, + TRUE, + AF_INET, + TCP_TABLE_OWNER_MODULE_ALL, + 0); + if (NO_ERROR != ret) + { + (void)g_free(tcp_table); + tcp_table = NULL; + + if (ERROR_INSUFFICIENT_BUFFER == ret) + { + bytes_needed <<= 1; + continue; + } + } + + break; + } + + /* add the TCP connections to the list backward */ + for ( entry_index = tcp_table->dwNumEntries - 1; + entry_index >= 0; + --entry_index) + { + /* allocate new active connection item */ + GuestActiveConnectionList * new_item = g_malloc0(sizeof(GuestActiveConnectionList)); + new_item->value = g_malloc0(sizeof(GuestActiveConnection)); + + /* push the connection to the head of the list */ + new_item->next = connections; + connections = new_item; + + new_item->value->source_addr = g_strdup(inet_ntoa(*(struct in_addr *)&tcp_table->table[entry_index].dwLocalAddr)); + new_item->value->source_port = htons(tcp_table->table[entry_index].dwLocalPort); + new_item->value->dest_addr = g_strdup(inet_ntoa(*(struct in_addr *)&tcp_table->table[entry_index].dwRemoteAddr)); + new_item->value->dest_port = htons(tcp_table->table[entry_index].dwRemotePort); + new_item->value->owner_process_id = tcp_table->table[entry_index].dwOwningPid; + new_item->value->if_family = GUEST_IP_ADDRESS_TYPE_IPV4; + new_item->value->protocol = GUEST_IP_PROTOCOL_TCP; + new_item->value->start_time = tcp_table->table[entry_index].liCreateTimestamp.QuadPart; + + switch (tcp_table->table[entry_index].dwState) + { + case MIB_TCP_STATE_CLOSED: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_CLOSED; + break; + case MIB_TCP_STATE_LISTEN: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_LISTEN; + break; + case MIB_TCP_STATE_SYN_SENT: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_SYN_SENT; + break; + case MIB_TCP_STATE_SYN_RCVD: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_SYN_RCVD; + break; + case MIB_TCP_STATE_ESTAB: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_ESTABLISHED; + break; + case MIB_TCP_STATE_FIN_WAIT1: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_FIN_WAIT1; + break; + case MIB_TCP_STATE_FIN_WAIT2: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_FIN_WAIT2; + break; + case MIB_TCP_STATE_CLOSE_WAIT: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_CLOSE_WAIT; + break; + case MIB_TCP_STATE_CLOSING: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_CLOSING; + break; + case MIB_TCP_STATE_LAST_ACK: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_LAST_ACK; + break; + case MIB_TCP_STATE_TIME_WAIT: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_TIME_WAIT; + break; + case MIB_TCP_STATE_DELETE_TCB: + new_item->value->state = GUEST_TCP_PROTOCOL_STATE_DELETE_TCB; + break; + } + } + + (void)g_free(tcp_table); + + return connections; +} diff --git a/qga/commands.c b/qga/commands.c index 7834967..2fb364c 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -70,3 +70,28 @@ struct GuestAgentInfo *qmp_guest_info(Error **errp) qmp_for_each_command(qmp_command_info, info); return info; } + +void qmp_guest_file_delete(const char *path, Error **errp) +{ + if (unlink(path)) + { + error_setg(errp, "Error deleting file '%s'", path); + } +} + +char *qmp_guest_get_hostname(Error **errp) +{ + char hostname[64]; + + if (gethostname(hostname, 64)) + { +#ifdef _WIN32 + error_setg(errp, "Error getting hostname (%d)", WSAGetLastError()); +#else + error_setg(errp, "Error getting hostname"); +#endif + return NULL; + } + + return g_strdup(hostname); +} diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index e92c6ab..89d4165 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -40,4 +40,6 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp); #ifndef _WIN32 void reopen_fd_to_null(int fd); +#else +int ga_win_commands_init(void); #endif diff --git a/qga/main.c b/qga/main.c index 9939a2b..3992aac 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1163,6 +1163,15 @@ int main(int argc, char **argv) g_critical("failed to register signal handlers"); goto out_bad; } +#else + { + + if (!ga_win_commands_init()) + { + g_critical("failed initializing commands module"); + goto out_bad; + } + } #endif s->main_loop = g_main_loop_new(NULL, false); diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 95f49e3..1e38f98 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -891,3 +891,175 @@ ## { 'command': 'guest-get-memory-block-info', 'returns': 'GuestMemoryBlockInfo' } + +## +# @GuestFileStat +# +# @st_mode: file access permissions mode +# @st_ino: file inode id +# @st_dev: file device node +# @st_nlink: number of links pointing to the file +# @st_uid: file user id +# @st_gid: file group id +# @st_atime: file access time +# @st_mtime: file modification time +# @st_ctime: file creation time +# @st_size: file size in bytes +# +# Since: 2.3 +## +{ 'type': 'GuestFileStat', + 'data': {'mode': 'int', 'inode': 'int', 'dev': 'int', + 'nlink': 'int', 'uid': 'int', 'gid': 'int', + 'size': 'uint64', 'atime': 'int', 'mtime': 'int', + 'ctime': 'int' + }} + +## +# @guest-file-stat: +# +# Get the stat() for a file in the guest's operating system +# +# Returns: hostname string. +# +# Since 2.3 +## +{ 'command': 'guest-file-stat', + 'data': { 'path': 'str' }, + 'returns': 'GuestFileStat' } + +## +# @guest-file-delete: +# +# Delete a file in the guest's operating system +# +# Returns: +# +# Since 2.3 +## +{ 'command': 'guest-file-delete', + 'data': { 'path': 'str' }} + +## +# @guest-get-hostname: +# +# Get the hostname of the guest's operating system +# +# Returns: hostname string. +# +# Since 2.3 +## +{ 'command': 'guest-get-hostname', + 'returns': 'str' } + +## +# @guest-uptime: +# +# Get the time in seconds since the guest machine operating system was started +# +# Returns: uptime in seconds +# +# Since 2.3 +## +{ 'command': 'guest-uptime', + 'returns': 'uint64' } + +## +# @GuestProcessInfo +# +# @process-id: the process unique id +# @parent-id: the process parent unique id +# @process-name: the name of the process +# @image-path: full path of the process image +# @session-id: the session id of the process +# +# Since: 2.3 +## +{ 'type': 'GuestProcessInfo', + 'data': {'process-id': 'int', 'parent-id': 'int', 'process-name': 'str', + 'image-path': 'str', 'session-id': 'int'}} + +## +# @guest-get-process-list: +# +# Get the list of active processes on the guest operating system +# +# Returns: array of active processes +# +# Since 2.3 +## +{ 'command': 'guest-get-process-list', + 'returns': ['GuestProcessInfo'] } + +## +# @GuestIpProtocol: +# +# An enumeration of supported IP protocols +# +# @tcp: TCP +# +# @udp: UDP +# +# Since: 2.3 +## +{ 'enum': 'GuestIpProtocol', + 'data': [ 'tcp', 'udp' ] } + +## +# @GuestTcpProtocolState: +# +# An enumeration of TCP connection state +# +# @closed: CLOSED +# @listen: LISTEN +# @syn-sent: SYN_SENT +# @syn-rcvd: SYN_RCVD +# @established: ESTABLISHED +# @fin-wait1: FIN_WAIT1 +# @fin-wait2: FIN_WAIT2 +# @close-wait: CLOSE_WAIT +# @closing: CLOSING +# @last-ack: LAST_ACK +# @time-wait: TIME_WAIT +# @delete-tcb: DELETE_TCB +# +# Since: 2.3 +## +{ 'enum': 'GuestTcpProtocolState', + 'data': [ 'closed', 'listen', 'syn-sent', 'syn-rcvd', 'established', + 'fin-wait1', 'fin-wait2', 'close-wait', 'closing', + 'last-ack', 'time-wait', 'delete-tcb' ] } + +## +# @GuestActiveConnection +# +# @if-family: 4 / 6 +# @protocol: TCP / UDP +# @source-addr: the source IP address of the connection +# @source-port: the source port of the connection +# @dest-addr: the destination IP address of the connection +# @dest-port: the destination port of the connection +# @owner-process_id: the process unique id for the connection owner +# @state: connection protocol state +# @start-time: time where bind() was called for the connection +# +# Since: 2.3 +## +{ 'type': 'GuestActiveConnection', + 'data': { 'source-addr': 'str', 'source-port': 'int', 'dest-addr': 'str', + 'dest-port': 'int', 'owner-process_id': 'int', 'state': 'GuestTcpProtocolState', + 'if-family': 'GuestIpAddressType', 'protocol': 'GuestIpProtocol', + 'start-time': 'uint64'}} + + ## +# @guest-get-active-connections: +# +# Get the list of active connections on the guest operating system +# +# Returns: array of active connections +# +# Since 2.3 +## +{ 'command': 'guest-get-active-connections', + 'returns': ['GuestActiveConnection'] } + diff --git a/qga/win32-definitions.h b/qga/win32-definitions.h new file mode 100644 index 0000000..6bb610e --- /dev/null +++ b/qga/win32-definitions.h @@ -0,0 +1,115 @@ + +#ifndef WIN32_DEFINITIONS_H_ +#define WIN32_DEFINITIONS_H_ 1 + +#include "win32-iptypes.h" + +#define STATUS_INFO_LENGTH_MISMATCH (0xC0000004) + +typedef +enum _PROCESSINFOCLASS +{ + ProcessBasicInformation=0x0000, + ProcessDebugPort=0x0007, + ProcessWow64Information=0x001a, + ProcessImageFileName=0x001b, + ProcessBreakOnTermination=0x001d, +} PROCESSINFOCLASS; + +typedef struct +{ + ULONG AllocationSize; + ULONG ActualSize; + ULONG Flags; + ULONG Unknown1; + UNICODE_STRING Unknown2; + HANDLE InputHandle; + HANDLE OutputHandle; + HANDLE ErrorHandle; + UNICODE_STRING CurrentDirectory; + HANDLE CurrentDirectoryHandle; + UNICODE_STRING SearchPaths; + UNICODE_STRING ApplicationName; + UNICODE_STRING CommandLine; + PVOID EnvironmentBlock; + ULONG Unknown[9]; + UNICODE_STRING Unknown3; + UNICODE_STRING Unknown4; + UNICODE_STRING Unknown5; + UNICODE_STRING Unknown6; +} PROCESS_PARAMETERS, *PPROCESS_PARAMETERS; + +typedef struct +{ + ULONG AllocationSize; + ULONG Unknown1; + HINSTANCE ProcessHinstance; + PVOID ListDlls; + PPROCESS_PARAMETERS ProcessParameters; + ULONG Unknown2; + HANDLE Heap; +} PEB, *PPEB; + +typedef struct +{ + DWORD ExitStatus; + PPEB PebBaseAddress; + DWORD AffinityMask; + DWORD BasePriority; + ULONG UniqueProcessId; + ULONG InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION; + +typedef +enum _SYSTEM_INFORMATION_CLASS +{ + SystemBasicInformation=0x0000, + SystemProcessorInformation=0x0001, + SystemPerformanceInformation=0x0002, + SystemTimeOfDayInformation=0x0003, + SystemPathInformation=0x0004, + SystemProcessInformation=0x0005, + SystemDeviceInformation=0x0007, + SystemModuleInformation=0x000B, +} SYSTEM_INFORMATION_CLASS; + +typedef +struct _SYSTEM_PROCESS_INFORMATION // Size=184 +{ + ULONG NextEntryOffset; // Size=4 Offset=0 + ULONG NumberOfThreads; // Size=4 Offset=4 + LARGE_INTEGER WorkingSetPrivateSize; // Size=8 Offset=8 + ULONG HardFaultCount; // Size=4 Offset=16 + ULONG NumberOfThreadsHighWatermark; // Size=4 Offset=20 + ULONGLONG CycleTime; // Size=8 Offset=24 + LARGE_INTEGER CreateTime; // Size=8 Offset=32 + LARGE_INTEGER UserTime; // Size=8 Offset=40 + LARGE_INTEGER KernelTime; // Size=8 Offset=48 + UNICODE_STRING ImageName; // Size=8 Offset=56 + LONG BasePriority; // Size=4 Offset=64 + PVOID UniqueProcessId; // Size=4 Offset=68 + PVOID InheritedFromUniqueProcessId; // Size=4 Offset=72 + ULONG HandleCount; // Size=4 Offset=76 + ULONG SessionId; // Size=4 Offset=80 + ULONG UniqueProcessKey; // Size=4 Offset=84 + ULONG PeakVirtualSize; // Size=4 Offset=88 + ULONG VirtualSize; // Size=4 Offset=92 + ULONG PageFaultCount; // Size=4 Offset=96 + ULONG PeakWorkingSetSize; // Size=4 Offset=100 + ULONG WorkingSetSize; // Size=4 Offset=104 + ULONG QuotaPeakPagedPoolUsage; // Size=4 Offset=108 + ULONG QuotaPagedPoolUsage; // Size=4 Offset=112 + ULONG QuotaPeakNonPagedPoolUsage; // Size=4 Offset=116 + ULONG QuotaNonPagedPoolUsage; // Size=4 Offset=120 + ULONG PagefileUsage; // Size=4 Offset=124 + ULONG PeakPagefileUsage; // Size=4 Offset=128 + ULONG PrivatePageCount; // Size=4 Offset=132 + LARGE_INTEGER ReadOperationCount; // Size=8 Offset=136 + LARGE_INTEGER WriteOperationCount; // Size=8 Offset=144 + LARGE_INTEGER OtherOperationCount; // Size=8 Offset=152 + LARGE_INTEGER ReadTransferCount; // Size=8 Offset=160 + LARGE_INTEGER WriteTransferCount; // Size=8 Offset=168 + LARGE_INTEGER OtherTransferCount; // Size=8 Offset=176 +} SYSTEM_PROCESS_INFORMATION; + +#endif /* WIN32_DEFINITIONS_H_ */ diff --git a/qga/win32-iptypes.h b/qga/win32-iptypes.h new file mode 100644 index 0000000..baae4ea --- /dev/null +++ b/qga/win32-iptypes.h @@ -0,0 +1,411 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + iptypes.h + +--*/ + +#ifndef IP_TYPES_INCLUDED +#define IP_TYPES_INCLUDED + +#include <time.h> + +#define INET_ADDRSTRLEN (16) +#define INET6_ADDRSTRLEN (48) + +// Definitions and structures used by getnetworkparams and getadaptersinfo apis + +#define MAX_ADAPTER_DESCRIPTION_LENGTH 128 // arb. +#define MAX_ADAPTER_NAME_LENGTH 256 // arb. +#define MAX_ADAPTER_ADDRESS_LENGTH 8 // arb. +#define DEFAULT_MINIMUM_ENTITIES 32 // arb. +#define MAX_HOSTNAME_LEN 128 // arb. +#define MAX_DOMAIN_NAME_LEN 128 // arb. +#define MAX_SCOPE_ID_LEN 256 // arb. + +// +// types +// + +// Node Type + +#define BROADCAST_NODETYPE 1 +#define PEER_TO_PEER_NODETYPE 2 +#define MIXED_NODETYPE 4 +#define HYBRID_NODETYPE 8 + +// +// IP_ADDRESS_STRING - store an IP address as a dotted decimal string +// + +typedef struct { + char String[4 * 4]; +} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING; + +// +// IP_ADDR_STRING - store an IP address with its corresponding subnet mask, +// both as dotted decimal strings +// + +typedef struct _IP_ADDR_STRING { + struct _IP_ADDR_STRING* Next; + IP_ADDRESS_STRING IpAddress; + IP_MASK_STRING IpMask; + DWORD Context; +} IP_ADDR_STRING, *PIP_ADDR_STRING; + +// +// ADAPTER_INFO - per-adapter information. All IP addresses are stored as +// strings +// + +typedef struct _IP_ADAPTER_INFO { + struct _IP_ADAPTER_INFO* Next; + DWORD ComboIndex; + char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4]; + char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4]; + UINT AddressLength; + BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH]; + DWORD Index; + UINT Type; + UINT DhcpEnabled; + PIP_ADDR_STRING CurrentIpAddress; + IP_ADDR_STRING IpAddressList; + IP_ADDR_STRING GatewayList; + IP_ADDR_STRING DhcpServer; + BOOL HaveWins; + IP_ADDR_STRING PrimaryWinsServer; + IP_ADDR_STRING SecondaryWinsServer; + time_t LeaseObtained; + time_t LeaseExpires; +} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO; + +// +// The following types require Winsock2. +// + +typedef enum { + IpPrefixOriginOther = 0, + IpPrefixOriginManual, + IpPrefixOriginWellKnown, + IpPrefixOriginDhcp, + IpPrefixOriginRouterAdvertisement, +} IP_PREFIX_ORIGIN; + +typedef enum { + IpSuffixOriginOther = 0, + IpSuffixOriginManual, + IpSuffixOriginWellKnown, + IpSuffixOriginDhcp, + IpSuffixOriginLinkLayerAddress, + IpSuffixOriginRandom, +} IP_SUFFIX_ORIGIN; + +typedef enum { + IpDadStateInvalid = 0, + IpDadStateTentative, + IpDadStateDuplicate, + IpDadStateDeprecated, + IpDadStatePreferred, +} IP_DAD_STATE; + +typedef struct _IP_ADAPTER_UNICAST_ADDRESS { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD Flags; + }; + }; + struct _IP_ADAPTER_UNICAST_ADDRESS *Next; + SOCKET_ADDRESS Address; + + IP_PREFIX_ORIGIN PrefixOrigin; + IP_SUFFIX_ORIGIN SuffixOrigin; + IP_DAD_STATE DadState; + + ULONG ValidLifetime; + ULONG PreferredLifetime; + ULONG LeaseLifetime; +} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS; + +typedef struct _IP_ADAPTER_ANYCAST_ADDRESS { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD Flags; + }; + }; + struct _IP_ADAPTER_ANYCAST_ADDRESS *Next; + SOCKET_ADDRESS Address; +} IP_ADAPTER_ANYCAST_ADDRESS, *PIP_ADAPTER_ANYCAST_ADDRESS; + +typedef struct _IP_ADAPTER_MULTICAST_ADDRESS { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD Flags; + }; + }; + struct _IP_ADAPTER_MULTICAST_ADDRESS *Next; + SOCKET_ADDRESS Address; +} IP_ADAPTER_MULTICAST_ADDRESS, *PIP_ADAPTER_MULTICAST_ADDRESS; + +// +// Per-address Flags +// +#define IP_ADAPTER_ADDRESS_DNS_ELIGIBLE 0x01 +#define IP_ADAPTER_ADDRESS_TRANSIENT 0x02 + +typedef struct _IP_ADAPTER_DNS_SERVER_ADDRESS { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD Reserved; + }; + }; + struct _IP_ADAPTER_DNS_SERVER_ADDRESS *Next; + SOCKET_ADDRESS Address; +} IP_ADAPTER_DNS_SERVER_ADDRESS, *PIP_ADAPTER_DNS_SERVER_ADDRESS; + +typedef struct _IP_ADAPTER_PREFIX { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD Flags; + }; + }; + struct _IP_ADAPTER_PREFIX *Next; + SOCKET_ADDRESS Address; + ULONG PrefixLength; +} IP_ADAPTER_PREFIX, *PIP_ADAPTER_PREFIX; + +// +// Per-adapter Flags +// +#define IP_ADAPTER_DDNS_ENABLED 0x01 +#define IP_ADAPTER_REGISTER_ADAPTER_SUFFIX 0x02 +#define IP_ADAPTER_DHCP_ENABLED 0x04 +#define IP_ADAPTER_RECEIVE_ONLY 0x08 +#define IP_ADAPTER_NO_MULTICAST 0x10 +#define IP_ADAPTER_IPV6_OTHER_STATEFUL_CONFIG 0x20 + +// +// OperStatus values from RFC 2863 +// +typedef enum { + IfOperStatusUp = 1, + IfOperStatusDown, + IfOperStatusTesting, + IfOperStatusUnknown, + IfOperStatusDormant, + IfOperStatusNotPresent, + IfOperStatusLowerLayerDown +} IF_OPER_STATUS; + +// +// Scope levels from RFC 2373 used with ZoneIndices array. +// +typedef enum { + ScopeLevelInterface = 1, + ScopeLevelLink = 2, + ScopeLevelSubnet = 3, + ScopeLevelAdmin = 4, + ScopeLevelSite = 5, + ScopeLevelOrganization = 8, + ScopeLevelGlobal = 14 +} SCOPE_LEVEL; + +typedef struct _IP_ADAPTER_ADDRESSES { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD IfIndex; + }; + }; + struct _IP_ADAPTER_ADDRESSES *Next; + PCHAR AdapterName; + PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress; + PIP_ADAPTER_ANYCAST_ADDRESS FirstAnycastAddress; + PIP_ADAPTER_MULTICAST_ADDRESS FirstMulticastAddress; + PIP_ADAPTER_DNS_SERVER_ADDRESS FirstDnsServerAddress; + PWCHAR DnsSuffix; + PWCHAR Description; + PWCHAR FriendlyName; + BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH]; + DWORD PhysicalAddressLength; + DWORD Flags; + DWORD Mtu; + DWORD IfType; + IF_OPER_STATUS OperStatus; + DWORD Ipv6IfIndex; + DWORD ZoneIndices[16]; + PIP_ADAPTER_PREFIX FirstPrefix; +} IP_ADAPTER_ADDRESSES, *PIP_ADAPTER_ADDRESSES; + +// +// Flags used as argument to GetAdaptersAddresses(). +// "SKIP" flags are added when the default is to include the information. +// "INCLUDE" flags are added when the default is to skip the information. +// +#define GAA_FLAG_SKIP_UNICAST 0x0001 +#define GAA_FLAG_SKIP_ANYCAST 0x0002 +#define GAA_FLAG_SKIP_MULTICAST 0x0004 +#define GAA_FLAG_SKIP_DNS_SERVER 0x0008 +#define GAA_FLAG_INCLUDE_PREFIX 0x0010 +#define GAA_FLAG_SKIP_FRIENDLY_NAME 0x0020 + +// +// IP_PER_ADAPTER_INFO - per-adapter IP information such as DNS server list. +// + +typedef struct _IP_PER_ADAPTER_INFO { + UINT AutoconfigEnabled; + UINT AutoconfigActive; + PIP_ADDR_STRING CurrentDnsServer; + IP_ADDR_STRING DnsServerList; +} IP_PER_ADAPTER_INFO, *PIP_PER_ADAPTER_INFO; + +// +// FIXED_INFO - the set of IP-related information which does not depend on DHCP +// + +typedef struct { + char HostName[MAX_HOSTNAME_LEN + 4] ; + char DomainName[MAX_DOMAIN_NAME_LEN + 4]; + PIP_ADDR_STRING CurrentDnsServer; + IP_ADDR_STRING DnsServerList; + UINT NodeType; + char ScopeId[MAX_SCOPE_ID_LEN + 4]; + UINT EnableRouting; + UINT EnableProxy; + UINT EnableDns; +} FIXED_INFO, *PFIXED_INFO; + +typedef struct ip_interface_name_info { + ULONG Index; // Interface Index + ULONG MediaType; // Interface Types - see ipifcons.h + UCHAR ConnectionType; + UCHAR AccessType; + GUID DeviceGuid; // Device GUID is the guid of the device + // that IP exposes + GUID InterfaceGuid; // Interface GUID, if not GUID_NULL is the + // GUID for the interface mapped to the device. +} IP_INTERFACE_NAME_INFO, *PIP_INTERFACE_NAME_INFO; + +typedef enum { + TCP_TABLE_BASIC_LISTENER, + TCP_TABLE_BASIC_CONNECTIONS, + TCP_TABLE_BASIC_ALL, + TCP_TABLE_OWNER_PID_LISTENER, + TCP_TABLE_OWNER_PID_CONNECTIONS, + TCP_TABLE_OWNER_PID_ALL, + TCP_TABLE_OWNER_MODULE_LISTENER, + TCP_TABLE_OWNER_MODULE_CONNECTIONS, + TCP_TABLE_OWNER_MODULE_ALL +} TCP_TABLE_CLASS, *PTCP_TABLE_CLASS; + +typedef enum { + UDP_TABLE_BASIC, + UDP_TABLE_OWNER_PID, + UDP_TABLE_OWNER_MODULE +} UDP_TABLE_CLASS, *PUDP_TABLE_CLASS; + +#define TCPIP_OWNING_MODULE_SIZE (16) +#define ANY_SIZE (1) + +typedef enum +{ + MIB_TCP_STATE_CLOSED = 1, + MIB_TCP_STATE_LISTEN = 2, + MIB_TCP_STATE_SYN_SENT = 3, + MIB_TCP_STATE_SYN_RCVD = 4, + MIB_TCP_STATE_ESTAB = 5, + MIB_TCP_STATE_FIN_WAIT1 = 6, + MIB_TCP_STATE_FIN_WAIT2 = 7, + MIB_TCP_STATE_CLOSE_WAIT = 8, + MIB_TCP_STATE_CLOSING = 9, + MIB_TCP_STATE_LAST_ACK = 10, + MIB_TCP_STATE_TIME_WAIT = 11, + MIB_TCP_STATE_DELETE_TCB = 12 +} MIB_TCP_STATE; + +typedef struct _MIB_UDPROW { + DWORD dwLocalAddr; + DWORD dwLocalPort; +} MIB_UDPROW, *PMIB_UDPROW; + +typedef struct _MIB_UDPTABLE { + DWORD dwNumEntries; + MIB_UDPROW table[ANY_SIZE]; +} MIB_UDPTABLE, *PMIB_UDPTABLE; + +typedef struct _MIB_UDPROW_OWNER_PID { + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwOwningPid; +} MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID; + +typedef struct _MIB_UDPTABLE_OWNER_PID { + DWORD dwNumEntries; + MIB_UDPROW_OWNER_PID table[ANY_SIZE]; +} MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID; + +typedef struct _MIB_UDPROW_OWNER_MODULE { + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwOwningPid; + LARGE_INTEGER liCreateTimestamp; + union { + struct { + int SpecificPortBind :1; + }; + int dwFlags; + }; + ULONGLONG OwningModuleInfo[TCPIP_OWNING_MODULE_SIZE]; +} MIB_UDPROW_OWNER_MODULE, *PMIB_UDPROW_OWNER_MODULE; + +typedef struct _MIB_UDPTABLE_OWNER_MODULE { + DWORD dwNumEntries; + MIB_UDPROW_OWNER_MODULE table[ANY_SIZE]; +} MIB_UDPTABLE_OWNER_MODULE, *PMIB_UDPTABLE_OWNER_MODULE; + +typedef struct _MIB_TCPROW_OWNER_MODULE { + DWORD dwState; + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwRemoteAddr; + DWORD dwRemotePort; + DWORD dwOwningPid; + LARGE_INTEGER liCreateTimestamp; + ULONGLONG OwningModuleInfo[TCPIP_OWNING_MODULE_SIZE]; +} MIB_TCPROW_OWNER_MODULE, *PMIB_TCPROW_OWNER_MODULE; + +typedef struct { + DWORD dwNumEntries; + MIB_TCPROW_OWNER_MODULE table[ANY_SIZE]; +} MIB_TCPTABLE_OWNER_MODULE, *PMIB_TCPTABLE_OWNER_MODULE; + +typedef struct _MIB_TCPROW_OWNER_PID { + DWORD dwState; + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwRemoteAddr; + DWORD dwRemotePort; + DWORD dwOwningPid; +} MIB_TCPROW_OWNER_PID, *PMIB_TCPROW_OWNER_PID; + +typedef struct { + DWORD dwNumEntries; + MIB_TCPROW_OWNER_PID table[ANY_SIZE]; +} MIB_TCPTABLE_OWNER_PID, *PMIB_TCPTABLE_OWNER_PID; + +#endif /* IP_TYPES_INCLUDED */ -- 2.3.4