From patchwork Tue Mar 31 08:34:37 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: itamar.tal4@gmail.com X-Patchwork-Id: 456534 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id A64CE14008F for ; Tue, 31 Mar 2015 19:35:59 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="verification failed; unprotected key" header.d=gmail.com header.i=@gmail.com header.b=Qd0GRQCi; dkim-adsp=none (unprotected policy); dkim-atps=neutral Received: from localhost ([::1]:37520 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YcreP-00024C-RY for incoming@patchwork.ozlabs.org; Tue, 31 Mar 2015 04:35:57 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:54349) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YcrdX-0000i4-Mi for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:35:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YcrdS-0004GG-Og for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:35:03 -0400 Received: from mail-wi0-x22e.google.com ([2a00:1450:400c:c05::22e]:35556) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YcrdS-0004G7-5q for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:34:58 -0400 Received: by widdi4 with SMTP id di4so325430wid.0 for ; Tue, 31 Mar 2015 01:34:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :signed-off-by; bh=eGyyq7b13EavwJ2ggWk57ADnFxmHniF2dgxG8nHBm5c=; b=Qd0GRQCinSIHLlNHb/eoHDj1U3BZug65DTJHuw3gbYF7kvKcpRdbfbTtoqbJ4zKiAX hicI/f+xB0P7VMsnD6VkcANeucSCjeXg93OhciVNjDmkVkOvJD+3jehr2kyE5n31K6UX EWjO3U9BOAVPG4M865WYPqmSc7AbxKzrVtkMV9HKPOpZbgKtLl613g7zuNxtSsEZ9HXK AxBtcqv4isPbNYjPreRLOcR3xgXzrP+MXnWnBNDumiDVyuIzCoiSt+C+tLwA6CuYMPo5 3odWHguNe8phR3Ms0zpjuWkY2nIwiuFn8HSKWZ20/G1PTVJgfTBmTZiDsDv4hP9Pj0kl LGag== X-Received: by 10.180.75.73 with SMTP id a9mr3432868wiw.45.1427790897455; Tue, 31 Mar 2015 01:34:57 -0700 (PDT) Received: from hpvm.guardicore.com (bzq-233-168-31-124.red.bezeqint.net. [31.168.233.124]) by mx.google.com with ESMTPSA id fy2sm19857741wic.15.2015.03.31.01.34.55 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 31 Mar 2015 01:34:56 -0700 (PDT) From: itamar.tal4@gmail.com X-Google-Original-From: itamar@guardicore.com To: qemu-devel@nongnu.org Date: Tue, 31 Mar 2015 11:34:37 +0300 Message-Id: <1427790877-14544-5-git-send-email-itamar@guardicore.com> X-Mailer: git-send-email 2.3.4 In-Reply-To: <1427790877-14544-1-git-send-email-itamar@guardicore.com> References: <1427790877-14544-1-git-send-email-itamar@guardicore.com> Signed-off-by: Itamar Tal X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:400c:c05::22e Cc: ori@guardicore.com, ariel@guardicore.com, mdroth@linux.vnet.ibm.com, pavel@guardicore.com, Itamar Tal Subject: [Qemu-devel] [PATCH 4/4] added qga processes and connections enumeration commands (win/linux) X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Itamar Tal This patch add more missing commands to the qga (both Windows and Linux): - current active process list - current active connections list the abovementioned commands are added to align the guest agent functionality with other common hypervisor agents. --- qga/commands-posix.c | 608 ++++++++++++++++++++++++++++++++++++++++++++++++ qga/commands-win32.c | 404 +++++++++++++++++++++++++++++++- qga/qapi-schema.json | 99 ++++++++ qga/win32-definitions.h | 110 +++++++++ qga/win32-iptypes.h | 412 ++++++++++++++++++++++++++++++++ 5 files changed, 1631 insertions(+), 2 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 38b639c..c4b82eb 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -9,6 +9,7 @@ * * Changes (itamar@guardicore.com): * - file attributes, removal, hashes + * - added process and connection enumeration support * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -50,6 +51,53 @@ extern char **environ; #include #include +#define PRG_HASH_SIZE (211) + +typedef +struct prg_node { + struct prg_node *next; + int inode; + int pid; +} prg_node; + +typedef +struct prg_hash { + struct prg_node *hash[PRG_HASH_SIZE]; +} prg_hash; + +#define PRG_LOCAL_ADDRESS "local_address" +#define PRG_INODE "inode" +#define PRG_SOCKET_PFX "socket:[" +#define PRG_SOCKET_PFXl (strlen(PRG_SOCKET_PFX)) +#define PRG_SOCKET_PFX2 "[0000]:" +#define PRG_SOCKET_PFX2l (strlen(PRG_SOCKET_PFX2)) + +#define PRG_HASHIT(x) ((x) % PRG_HASH_SIZE) +#ifndef LINE_MAX +#define LINE_MAX 4096 +#endif +#define PATH_PROC "/proc" +#define PATH_FD_SUFF "fd" +#define PATH_FD_SUFFl strlen(PATH_FD_SUFF) +#define PATH_PROC_X_FD PATH_PROC "/%s/" PATH_FD_SUFF +#define PATH_CMDLINE "cmdline" +#define PATH_CMDLINEl strlen(PATH_CMDLINE) + +/* These enums are used by IPX too. :-( */ +enum { + TCP_ESTABLISHED = 1, + TCP_SYN_SENT, + TCP_SYN_RECV, + TCP_FIN_WAIT1, + TCP_FIN_WAIT2, + TCP_TIME_WAIT, + TCP_CLOSE, + TCP_CLOSE_WAIT, + TCP_LAST_ACK, + TCP_LISTEN, + TCP_CLOSING /* now a valid state */ +}; + #ifdef FIFREEZE #define CONFIG_FSFREEZE #endif @@ -2491,4 +2539,564 @@ uint32_t qmp_guest_uptime(Error **errp) return sys_info.uptime; } +GuestProcessInfoList *qmp_guest_get_process_list(Error **errp) +{ + struct dirent *ent = NULL; + GuestProcessInfoList *process_list_head = NULL; + GuestProcessInfoList *process_list_last = NULL; + + DIR *procfs = opendir("/proc"); + + if (NULL == procfs) { + error_setg(errp, "Failed opening procfs (error %d)", errno); + return NULL; + } + + /* read entries until EOD */ + errno = 0; + while (NULL != (ent = readdir(procfs))) { + int pid = strtoul(ent->d_name, NULL, 10); + GuestProcessInfoList *list_item = NULL; + char local_path[256]; + FILE *info_file = NULL; + char info[256]; + size_t len; + + /* ensure directory name is a number */ + if ((0 == pid) || (ULONG_MAX == pid)) { + continue; + } + + list_item = g_malloc0(sizeof(GuestProcessInfoList)); + 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 = pid; + + /* read process name */ + snprintf(local_path, 256, "/proc/%d/comm", pid); + info_file = fopen(local_path, "rb"); + if (NULL != info_file) { + len = fread(info, 1, 256, info_file); + info[len] = '\0'; + if ((0 < len) && ('\n' == info[len - 1])) { + info[len - 1] = '\0'; + } + list_item->value->process_name = g_strdup(info); + + fclose(info_file); + } + + /* read process parent id */ + snprintf(local_path, 256, "/proc/%d/status", pid); + info_file = fopen(local_path, "rb"); + if (NULL != info_file) { + char *line = NULL; + size_t line_size = 0; + + /* read lines, search for ppid */ + for (;;) { + len = getline(&line, &line_size, info_file); + if (-1 == len) { + break; + } + + if (line == strstr(line, "PPid:")) { + list_item->value->parent_id = strtoul(line + 5, NULL, 10); + } + } + + fclose(info_file); + } + + /* read process session id */ + snprintf(local_path, 256, "/proc/%d/sessionid", pid); + info_file = fopen(local_path, "rb"); + if (NULL != info_file) { + len = fread(info, 1, 256, info_file); + info[len] = '\0'; + list_item->value->session_id = strtoul(info, NULL, 10); + + fclose(info_file); + } + + /* read process image path */ + snprintf(local_path, 256, "/proc/%d/exe", pid); + len = readlink(local_path, info, sizeof(info) - 1); + if (-1 != len) { + info[len] = '\0'; + list_item->value->image_path = g_strdup(info); + } + + errno = 0; + } + + (void)closedir(procfs); + + if (0 != errno) { + error_setg(errp, "Error while reading procfs directory (error %d)", + errno); + return NULL; + } + + return process_list_head; +} + +/* this function is taken out of netstat (found in net-tools) and modified */ +static void prg_cache_add(prg_hash *hash, int inode, int pid) +{ + unsigned hi = PRG_HASHIT(inode); + struct prg_node **pnp = NULL, *pn = NULL; + + /* look for the inode in the table or find next free entry for the inode */ + for (pnp = &hash->hash[hi]; (pn = *pnp); pnp = &pn->next) { + if (pn->inode == inode) { + /* Some warning should be appropriate here + as we got multiple processes for one i-node */ + return; + } + } + + /* allocate new entry for this inode */ + *pnp = g_malloc0(sizeof(struct prg_node)); + if (NULL == *pnp) { + return; + } + + pn = *pnp; + pn->next = NULL; + pn->inode = inode; + pn->pid = pid; +} + +/* this function is taken out of netstat (found in net-tools) and modified */ +static int prg_cache_get(prg_hash *hash, int inode) +{ + unsigned hi = PRG_HASHIT(inode); + struct prg_node *pn = NULL; + + for (pn = hash->hash[hi]; pn; pn = pn->next) { + if (pn->inode == inode) { + return pn->pid; + } + } + + return -1; +} + +/* this function is taken out of netstat (found in net-tools) and modified */ +static void prg_cache_clear(prg_hash *hash) +{ + struct prg_node **pnp = NULL, *pn = NULL; + + + for (pnp = hash->hash; pnp < hash->hash + PRG_HASH_SIZE; pnp++) { + pn = *pnp; + while (NULL != pn) { + *pnp = pn->next; + free(pn); + pn = *pnp; + } + } +} + +/* this function is taken out of netstat (found in net-tools) and modified */ +static void extract_type_1_socket_inode(const char lname[], long *inode_p) +{ + + /* If lname is of the form "socket:[12345]", extract the "12345" + as *inode_p. Otherwise, return -1 as *inode_p. + */ + + if (strlen(lname) < PRG_SOCKET_PFXl+3) { + *inode_p = -1; + } else if (memcmp(lname, PRG_SOCKET_PFX, PRG_SOCKET_PFXl)) { + *inode_p = -1; + } else if (lname[strlen(lname)-1] != ']') { + *inode_p = -1; + } else { + char inode_str[strlen(lname + 1)]; /* e.g. "12345" */ + const int inode_str_len = strlen(lname) - PRG_SOCKET_PFXl - 1; + char *serr = NULL; + + strncpy(inode_str, lname + PRG_SOCKET_PFXl, inode_str_len); + inode_str[inode_str_len] = '\0'; + *inode_p = strtol(inode_str, &serr, 0); + if (!serr || *serr || *inode_p < 0 || *inode_p >= INT_MAX) { + *inode_p = -1; + } + } +} + +/* this function is taken out of netstat (found in net-tools) and modified */ +static void extract_type_2_socket_inode(const char lname[], long *inode_p) +{ + + /* If lname is of the form "[0000]:12345", extract the "12345" + as *inode_p. Otherwise, return -1 as *inode_p. + */ + + if (strlen(lname) < PRG_SOCKET_PFX2l+1) { + *inode_p = -1; + } else if (memcmp(lname, PRG_SOCKET_PFX2, PRG_SOCKET_PFX2l)) { + *inode_p = -1; + } else { + char *serr = NULL; + + *inode_p = strtol(lname + PRG_SOCKET_PFX2l, &serr, 0); + if (!serr || *serr || *inode_p < 0 || *inode_p >= INT_MAX) { + *inode_p = -1; + } + } +} + +/* this function is taken out of netstat (found in net-tools) and modified */ +static int prg_cache_load(prg_hash *hash) +{ + char line[LINE_MAX]; + int procfdlen, lnamelen; + char lname[30]; + long inode; + const char *cs = NULL; + DIR *dirproc = NULL, *dirfd = NULL; + struct dirent *direproc = NULL, *direfd = NULL; + + /* reset the hash table */ + memset(hash, 0, sizeof(prg_hash)); + + dirproc = opendir(PATH_PROC); + if (NULL == dirproc) { + return 0; + } + + for (;;) { + int pid = 0; + + errno = 0; + direproc = readdir(dirproc); + if (NULL == direproc) { + break; + } + +#ifdef DIRENT_HAVE_D_TYPE_WORKS + if (direproc->d_type != DT_DIR) { + continue; + } +#endif + for (cs = direproc->d_name; *cs; cs++) { + if (!isdigit(*cs)) { + break; + } + } + if (*cs) { + continue; + } + + procfdlen = snprintf(line, sizeof(line), + PATH_PROC_X_FD, direproc->d_name); + if ((procfdlen <= 0) || (procfdlen >= sizeof(line) - 5)) { + continue; + } + errno = 0; + dirfd = opendir(line); + if (NULL == dirfd) { + continue; + } + line[procfdlen] = '/'; + for (;;) { + errno = 0; + direfd = readdir(dirfd); + if (NULL == direfd) { + break; + } + +#ifdef DIRENT_HAVE_D_TYPE_WORKS + if (direfd->d_type != DT_LNK) { + continue; + } +#endif + if (procfdlen + 1 + strlen(direfd->d_name) + 1 > sizeof(line)) { + continue; + } + memcpy(line + procfdlen - PATH_FD_SUFFl, PATH_FD_SUFF "/", + PATH_FD_SUFFl + 1); + strcpy(line + procfdlen + 1, direfd->d_name); + lnamelen = readlink(line, lname, sizeof(lname)-1); + lname[lnamelen] = '\0'; /*make it a null-terminated string*/ + + extract_type_1_socket_inode(lname, &inode); + + if (inode < 0) { + extract_type_2_socket_inode(lname, &inode); + } + + if (inode < 0) { + continue; + } + + pid = strtoul(direproc->d_name, NULL, 10); + prg_cache_add(hash, inode, pid); + } + closedir(dirfd); + dirfd = NULL; + } + if (dirproc) { + closedir(dirproc); + } + if (dirfd) { + closedir(dirfd); + } + + return 1; +} + +/* this function is taken out of netstat (found in net-tools) and modified */ +static GuestActiveConnection *udp_do_one(prg_hash *hash, const char *line) +{ + GuestActiveConnection *conn = NULL; + char local_addr[64], rem_addr[64]; + char more[512]; + int num, local_port, rem_port, d, state, timer_run, uid, timeout; + struct sockaddr_in localaddr, remaddr; + unsigned long rxq, txq, time_len, retr, inode; + + more[0] = '\0'; + num = sscanf(line, + "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X" + " %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", + &d, local_addr, &local_port, + rem_addr, &rem_port, &state, + &txq, &rxq, &timer_run, &time_len, + &retr, &uid, &timeout, &inode, more); + + if (strlen(local_addr) > 8) { + /* ipv6 support is missing */ + } else { + sscanf(local_addr, "%X", + &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); + sscanf(rem_addr, "%X", + &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); + ((struct sockaddr *) &localaddr)->sa_family = AF_INET; + ((struct sockaddr *) &remaddr)->sa_family = AF_INET; + } + + retr = 0L; + + if (num < 10) { + return NULL; + } + + conn = g_malloc0(sizeof(GuestActiveConnection)); + + if (AF_INET == localaddr.sin_family) { + conn->source_addr = g_strdup(inet_ntoa(localaddr.sin_addr)); + conn->dest_addr = g_strdup(inet_ntoa(remaddr.sin_addr)); + } + + conn->source_port = local_port; + conn->dest_port = rem_port; + conn->owner_process_id = prg_cache_get(hash, inode); + conn->if_family = GUEST_IP_ADDRESS_TYPE_IPV4; + conn->protocol = GUEST_IP_PROTOCOL_UDP; + + switch (state) { + case TCP_CLOSE: + conn->state = GUEST_TCP_PROTOCOL_STATE_CLOSED; + break; + case TCP_ESTABLISHED: + conn->state = GUEST_TCP_PROTOCOL_STATE_ESTABLISHED; + break; + default: + conn->state = GUEST_TCP_PROTOCOL_STATE_LISTEN; + break; + } + + return conn; +} + +/* this function is taken out of netstat (found in net-tools) and modified */ +static GuestActiveConnection *tcp_do_one(prg_hash *hash, const char *line) +{ + unsigned long rxq, txq, time_len, retr, inode; + int num, local_port, rem_port, d, state, uid, timer_run, timeout; + char rem_addr[128], local_addr[128], more[512]; + struct sockaddr_in localaddr, remaddr; + GuestActiveConnection *conn = NULL; + + num = sscanf(line, + "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X" + " %lX:%lX %X:%lX %lX %d %d %ld %512s\n", + &d, local_addr, &local_port, rem_addr, &rem_port, &state, + &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, + &inode, more); + + if (strlen(local_addr) > 8) { + /* ipv6 support is missing */ + } else { + sscanf(local_addr, "%X", + &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); + sscanf(rem_addr, "%X", + &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); + ((struct sockaddr *) &localaddr)->sa_family = AF_INET; + ((struct sockaddr *) &remaddr)->sa_family = AF_INET; + } + + if (num < 11) { + return NULL; + } + + if (state == TCP_LISTEN) { + time_len = 0; + retr = 0L; + rxq = 0L; + txq = 0L; + } + + conn = g_malloc0(sizeof(GuestActiveConnection)); + + if (AF_INET == localaddr.sin_family) { + conn->source_addr = g_strdup(inet_ntoa(localaddr.sin_addr)); + conn->dest_addr = g_strdup(inet_ntoa(remaddr.sin_addr)); + } + + conn->source_port = local_port; + conn->dest_port = rem_port; + conn->owner_process_id = prg_cache_get(hash, inode); + conn->if_family = GUEST_IP_ADDRESS_TYPE_IPV4; + conn->protocol = GUEST_IP_PROTOCOL_TCP; + + switch (state) { + case TCP_CLOSE: + conn->state = GUEST_TCP_PROTOCOL_STATE_CLOSED; + break; + case TCP_LISTEN: + conn->state = GUEST_TCP_PROTOCOL_STATE_LISTEN; + break; + case TCP_SYN_SENT: + conn->state = GUEST_TCP_PROTOCOL_STATE_SYN_SENT; + break; + case TCP_SYN_RECV: + conn->state = GUEST_TCP_PROTOCOL_STATE_SYN_RCVD; + break; + case TCP_ESTABLISHED: + conn->state = GUEST_TCP_PROTOCOL_STATE_ESTABLISHED; + break; + case TCP_FIN_WAIT1: + conn->state = GUEST_TCP_PROTOCOL_STATE_FIN_WAIT1; + break; + case TCP_FIN_WAIT2: + conn->state = GUEST_TCP_PROTOCOL_STATE_FIN_WAIT2; + break; + case TCP_CLOSE_WAIT: + conn->state = GUEST_TCP_PROTOCOL_STATE_CLOSE_WAIT; + break; + case TCP_CLOSING: + conn->state = GUEST_TCP_PROTOCOL_STATE_CLOSING; + break; + case TCP_LAST_ACK: + conn->state = GUEST_TCP_PROTOCOL_STATE_LAST_ACK; + break; + case TCP_TIME_WAIT: + conn->state = GUEST_TCP_PROTOCOL_STATE_TIME_WAIT; + break; + } + + return conn; +} + +GuestActiveConnectionList *qmp_guest_get_active_connections(Error **errp) +{ + GuestActiveConnectionList *conn_list_head = NULL; + GuestActiveConnectionList *conn_list_last = NULL; + prg_hash hash; + FILE *udp_info = NULL; + FILE *tcp_info = fopen("/proc/net/tcp", "r"); + int i = 0; + + if (NULL == tcp_info) { + error_setg(errp, "Error opening '/proc/net/tcp' (error %d)", errno); + return NULL; + } + + prg_cache_load(&hash); + + for (i = 0; !feof(tcp_info); ++i) { + char tcp_info_line[1024]; + GuestActiveConnectionList *conn_item = NULL; + GuestActiveConnection *conn = NULL; + + if (NULL == fgets(tcp_info_line, 1024, tcp_info)) { + continue; + } + + if (i == 0) { + continue; + } + + conn = tcp_do_one(&hash, tcp_info_line); + if (NULL == conn) { + continue; + } + + /* add connection to the end of list */ + conn_item = g_malloc0(sizeof(GuestActiveConnectionList)); + if (NULL == conn_list_head) { + conn_list_head = conn_item; + } + if (NULL != conn_list_last) { + conn_list_last->next = conn_item; + } + conn_list_last = conn_item; + conn_item->value = conn; + } + + fclose(tcp_info); + + udp_info = fopen("/proc/net/udp", "r"); + if (NULL == udp_info) { + error_setg(errp, "Error opening '/proc/net/udp' (error %d)", errno); + return NULL; + } + + for (i = 0; !feof(udp_info); ++i) { + char udp_info_line[1024]; + GuestActiveConnectionList *conn_item = NULL; + GuestActiveConnection *conn = NULL; + + if (NULL == fgets(udp_info_line, 1024, udp_info)) { + continue; + } + + if (i == 0) { + continue; + } + + conn = udp_do_one(&hash, udp_info_line); + if (NULL == conn) { + continue; + } + + /* add connection to the end of list */ + conn_item = g_malloc0(sizeof(GuestActiveConnectionList)); + if (NULL == conn_list_head) { + conn_list_head = conn_item; + } + if (NULL != conn_list_last) { + conn_list_last->next = conn_item; + } + conn_list_last = conn_item; + conn_item->value = conn; + } + + prg_cache_clear(&hash); + + return conn_list_head; +} diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 6987f4e..30dcd50 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -9,6 +9,7 @@ * * Changes (itamar@guardicore.com): * - file attributes, removal, hashes + * - added process and connection enumeration support * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -22,6 +23,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" @@ -29,6 +31,10 @@ #define SHTDN_REASON_FLAG_PLANNED 0x80000000 #endif +#ifndef MAX_PATH +#define MAX_PATH (256) +#endif + /* 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 * \ @@ -37,6 +43,38 @@ #define INVALID_SET_FILE_POINTER ((DWORD)-1) +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, + BOOL 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; + typedef struct GuestFileHandle { int64_t id; HANDLE fh; @@ -386,17 +424,59 @@ static void guest_file_init(void) } int ga_win_commands_init(void) -{ + { + HMODULE ntdll_module = GetModuleHandle("ntdll.dll"); + HMODULE iphlpapi_module = LoadLibrary("iphlpapi.dll"); WSADATA wsa_data = {0}; + if ((NULL == ntdll_module) || + (NULL == iphlpapi_module)) { + goto error; + } + if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { g_critical("failed to initialize WSA engine"); 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; + } + + 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: + error: + (void)FreeLibrary(iphlpapi_module); + return 0; } @@ -902,4 +982,324 @@ uint32_t qmp_guest_uptime(Error **errp) return uptime_milli / 1000; } +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; + DWORD current_process_id = GetCurrentProcessId(); + 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, "No memory 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; + + if (0 == list_item->value->process_id) { + list_item->value->process_name = g_strdup("System Idle Process"); + process_list_iter = (void *)((uint32_t)process_list_iter + + process_list_iter->NextEntryOffset); + continue; + } else if (4 == list_item->value->process_id) { + list_item->value->process_name = g_strdup("System"); + process_list_iter = (void *)((uint32_t)process_list_iter + + process_list_iter->NextEntryOffset); + continue; + } + + 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; + } + + if (current_process_id != list_item->value->process_id) { + process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, + FALSE, + list_item->value->process_id); + } else { + char curr_image_path[MAX_PATH]; + + process_handle = GetCurrentProcess(); + + if (GetModuleFileName(NULL, + curr_image_path, + MAX_PATH)) { + list_item->value->image_path = g_strdup(&curr_image_path[0]); + } + } + + if (NULL != process_handle) { + /* get process parent ID */ + 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; + } + + /* get process image path */ + if (NULL == list_item->value->image_path) { + process_image_path = g_malloc0(sizeof(UNICODE_STRING) + + MAX_PATH * sizeof(wchar_t)); + + status = NtQueryInformationProcess(process_handle, + ProcessImageFileName, + process_image_path, + sizeof(UNICODE_STRING) + + MAX_PATH * 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) { + MIB_UDPROW_OWNER_MODULE *udp_row = &udp_table->table[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_row->dwLocalAddr)); + new_item->value->source_port = htons(udp_row->dwLocalPort); + new_item->value->owner_process_id = udp_row->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_row->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) { + MIB_TCPROW_OWNER_MODULE *tcp_row = &tcp_table->table[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_row->dwLocalAddr)); + new_item->value->source_port = htons(tcp_row->dwLocalPort); + new_item->value->dest_addr = + g_strdup(inet_ntoa(*(struct in_addr *)&tcp_row->dwRemoteAddr)); + new_item->value->dest_port = htons(tcp_row->dwRemotePort); + new_item->value->owner_process_id = tcp_row->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_row->liCreateTimestamp.QuadPart; + + switch (tcp_row->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/qapi-schema.json b/qga/qapi-schema.json index 61f8a5a..9df985d 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -971,3 +971,102 @@ { 'command': 'guest-uptime', 'returns': 'uint32' } +## +# @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.4 +## +{ '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.4 +## +{ 'command': 'guest-get-process-list', + 'returns': ['GuestProcessInfo'] } + +## +# @GuestIpProtocol: +# +# An enumeration of supported IP protocols +# +# @tcp: TCP +# +# @udp: UDP +# +# Since: 2.4 +## +{ '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.4 +## +{ '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: ipv4 / ipv6 +# @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.4 +## +{ '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.4 +## +{ '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..c4f0a2b --- /dev/null +++ b/qga/win32-definitions.h @@ -0,0 +1,110 @@ + +#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..ec55300 --- /dev/null +++ b/qga/win32-iptypes.h @@ -0,0 +1,412 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + iptypes.h + +--*/ + +#ifndef IP_TYPES_INCLUDED +#define IP_TYPES_INCLUDED + +#include + +#define INET_ADDRSTRLEN (16) +#define INET6_ADDRSTRLEN (48) + +/* Definitions and structures used by getnetworkparams and + getadaptersinfo apis */ +#define MAX_ADAPTER_DESCRIPTION_LENGTH 128 +#define MAX_ADAPTER_NAME_LENGTH 256 +#define MAX_ADAPTER_ADDRESS_LENGTH 8 +#define DEFAULT_MINIMUM_ENTITIES 32 +#define MAX_HOSTNAME_LEN 128 +#define MAX_DOMAIN_NAME_LEN 128 +#define MAX_SCOPE_ID_LEN 256 + +/* + 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_UNKNOWN = 0, + 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 */ +