/* * Copyright (C) 2003,2006 Juan Lang * Copyright (C) 2007 TransGaming Technologies Inc. * Copyright (C) 2009 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #ifdef HAVE_DIRENT_H #include <dirent.h> #endif #ifdef HAVE_ALIAS_H #include <alias.h> #endif #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif #ifdef HAVE_SYS_SOCKETVAR_H #include <sys/socketvar.h> #endif #ifdef HAVE_SYS_TIMEOUT_H #include <sys/timeout.h> #endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include <netinet/in_systm.h> #endif #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif #ifdef HAVE_NET_IF_H #include <net/if.h> #endif #ifdef HAVE_NET_IF_DL_H #include <net/if_dl.h> #endif #ifdef HAVE_NET_IF_TYPES_H #include <net/if_types.h> #endif #ifdef HAVE_NET_ROUTE_H #include <net/route.h> #endif #ifdef HAVE_NET_IF_ARP_H #include <net/if_arp.h> #endif #ifdef HAVE_NETINET_IF_ETHER_H #include <netinet/if_ether.h> #endif #ifdef HAVE_NETINET_IF_INARP_H #include <netinet/if_inarp.h> #endif #ifdef HAVE_NETINET_IP_H #include <netinet/ip.h> #endif #ifdef HAVE_NETINET_TCP_H #include <netinet/tcp.h> #endif #ifdef HAVE_NETINET_IP_VAR_H #include <netinet/ip_var.h> #endif #ifdef HAVE_NETINET_TCP_FSM_H #include <netinet/tcp_fsm.h> #endif #ifdef HAVE_NETINET_IN_PCB_H #include <netinet/in_pcb.h> #endif #ifdef HAVE_NETINET_TCP_TIMER_H #include <netinet/tcp_timer.h> #endif #ifdef HAVE_NETINET_TCP_VAR_H #include <netinet/tcp_var.h> #endif #ifdef HAVE_NETINET_IP_ICMP_H #include <netinet/ip_icmp.h> #endif #ifdef HAVE_NETINET_ICMP_VAR_H #include <netinet/icmp_var.h> #endif #ifdef HAVE_NETINET_UDP_H #include <netinet/udp.h> #endif #ifdef HAVE_NETINET_UDP_VAR_H #include <netinet/udp_var.h> #endif #ifdef HAVE_SYS_PROTOSW_H #include <sys/protosw.h> #endif #ifdef HAVE_SYS_SYSCTL_H #include <sys/sysctl.h> #endif #ifdef HAVE_KSTAT_H #include <kstat.h> #endif #ifdef HAVE_INET_MIB2_H #include <inet/mib2.h> #endif #ifdef HAVE_STROPTS_H #include <stropts.h> #endif #ifdef HAVE_SYS_TIHDR_H #include <sys/tihdr.h> #endif #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> #endif #ifdef HAVE_SYS_QUEUE_H #include <sys/queue.h> #endif #ifdef HAVE_SYS_USER_H /* Make sure the definitions of struct kinfo_proc are the same. */ #include <sys/user.h> #endif #ifdef HAVE_LIBPROCSTAT_H #include <libprocstat.h> #endif #ifdef HAVE_LIBPROC_H #include <libproc.h> #endif #ifdef HAVE_IFADDRS_H #include <ifaddrs.h> #endif #ifndef ROUNDUP #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(int) - 1))) : sizeof(int)) #endif #ifndef ADVANCE #define ADVANCE(x, n) (x += ROUNDUP(((struct sockaddr *)n)->sa_len)) #endif #include "ntstatus.h" #define WIN32_NO_STATUS #define NONAMELESSUNION #define USE_WS_PREFIX #include "winsock2.h" #include "ws2ipdef.h" #include "ifenum.h" #include "ipstats.h" #include "iphlpapi.h" #include "wine/debug.h" #include "wine/server.h" #include "wine/unicode.h" #ifndef HAVE_NETINET_TCP_FSM_H #define TCPS_ESTABLISHED 1 #define TCPS_SYN_SENT 2 #define TCPS_SYN_RECEIVED 3 #define TCPS_FIN_WAIT_1 4 #define TCPS_FIN_WAIT_2 5 #define TCPS_TIME_WAIT 6 #define TCPS_CLOSED 7 #define TCPS_CLOSE_WAIT 8 #define TCPS_LAST_ACK 9 #define TCPS_LISTEN 10 #define TCPS_CLOSING 11 #endif #ifndef RTF_MULTICAST #define RTF_MULTICAST 0 /* Not available on NetBSD/OpenBSD */ #endif #ifndef RTF_LLINFO #define RTF_LLINFO 0 /* Not available on FreeBSD 8 and above */ #endif WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi); #ifdef HAVE_LIBKSTAT static DWORD kstat_get_ui32( kstat_t *ksp, const char *name ) { unsigned int i; kstat_named_t *data = ksp->ks_data; for (i = 0; i < ksp->ks_ndata; i++) if (!strcmp( data[i].name, name )) return data[i].value.ui32; return 0; } #endif #if defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK) static int open_streams_mib( const char *proto ) { int fd; struct strbuf buf; struct request { struct T_optmgmt_req req_header; struct opthdr opt_header; } request; if ((fd = open( "/dev/arp", O_RDWR )) == -1) { WARN( "could not open /dev/arp: %s\n", strerror(errno) ); return -1; } if (proto) ioctl( fd, I_PUSH, proto ); request.req_header.PRIM_type = T_SVR4_OPTMGMT_REQ; request.req_header.OPT_length = sizeof(request.opt_header); request.req_header.OPT_offset = FIELD_OFFSET( struct request, opt_header ); request.req_header.MGMT_flags = T_CURRENT; request.opt_header.level = MIB2_IP; request.opt_header.name = 0; request.opt_header.len = 0; buf.len = sizeof(request); buf.buf = (caddr_t)&request; if (putmsg( fd, &buf, NULL, 0 ) == -1) { WARN( "putmsg: %s\n", strerror(errno) ); close( fd ); fd = -1; } return fd; } static void *read_mib_entry( int fd, int level, int name, int *len ) { struct strbuf buf; void *data; int ret, flags = 0; struct reply { struct T_optmgmt_ack ack_header; struct opthdr opt_header; } reply; for (;;) { buf.maxlen = sizeof(reply); buf.buf = (caddr_t)&reply; if ((ret = getmsg( fd, &buf, NULL, &flags )) < 0) return NULL; if (!(ret & MOREDATA)) return NULL; if (reply.ack_header.PRIM_type != T_OPTMGMT_ACK) return NULL; if (buf.len < sizeof(reply.ack_header)) return NULL; if (reply.ack_header.OPT_length < sizeof(reply.opt_header)) return NULL; if (!(data = HeapAlloc( GetProcessHeap(), 0, reply.opt_header.len ))) return NULL; buf.maxlen = reply.opt_header.len; buf.buf = (caddr_t)data; flags = 0; if (getmsg( fd, NULL, &buf, &flags ) >= 0 && reply.opt_header.level == level && reply.opt_header.name == name) { *len = buf.len; return data; } HeapFree( GetProcessHeap(), 0, data ); } } #endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */ /****************************************************************** * GetUdpStatistics (IPHLPAPI.@) * * Get the IPv4 and IPv6 UDP statistics for the local computer. * * PARAMS * stats [Out] buffer for UDP statistics * family [In] specifies whether IPv4 or IPv6 statistics are returned * * RETURNS * Success: NO_ERROR * Failure: error code from winerror.h */ DWORD WINAPI GetUdpStatisticsEx(PMIB_UDPSTATS stats, DWORD family) { DWORD ret = ERROR_NOT_SUPPORTED; if (!stats) return ERROR_INVALID_PARAMETER; if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER; memset( stats, 0, sizeof(*stats) ); stats->dwNumAddrs = get_interface_indices( FALSE, NULL ); if (family == WS_AF_INET6) { #ifdef __linux__ { FILE *fp; if ((fp = fopen("/proc/net/snmp6", "r"))) { struct { const char *name; DWORD *elem; } udpstatlist[] = { { "Udp6InDatagrams", &stats->dwInDatagrams }, { "Udp6NoPorts", &stats->dwNoPorts }, { "Udp6InErrors", &stats->dwInErrors }, { "Udp6OutDatagrams", &stats->dwOutDatagrams }, }; char buf[512], *ptr, *value; DWORD res, i; while ((ptr = fgets(buf, sizeof(buf), fp))) { if (!(value = strchr(buf, ' '))) continue; /* terminate the valuename */ ptr = value - 1; *(ptr + 1) = '\0'; /* and strip leading spaces from value */ value += 1; while (*value==' ') value++; if ((ptr = strchr(value, '\n'))) *ptr='\0'; for (i = 0; i < ARRAY_SIZE(udpstatlist); i++) if (!_strnicmp(buf, udpstatlist[i].name, -1) && sscanf(value, "%d", &res)) *udpstatlist[i].elem = res; } fclose(fp); ret = NO_ERROR; } } #else FIXME( "unimplemented for IPv6\n" ); #endif return ret; } #ifdef __linux__ { FILE *fp; if ((fp = fopen("/proc/net/snmp", "r"))) { static const char hdr[] = "Udp:"; char buf[512], *ptr; while ((ptr = fgets(buf, sizeof(buf), fp))) { if (_strnicmp(buf, hdr, sizeof(hdr) - 1)) continue; /* last line was a header, get another */ if (!(ptr = fgets(buf, sizeof(buf), fp))) break; if (!_strnicmp(buf, hdr, sizeof(hdr) - 1)) { ptr += sizeof(hdr); sscanf( ptr, "%u %u %u %u %u", &stats->dwInDatagrams, &stats->dwNoPorts, &stats->dwInErrors, &stats->dwOutDatagrams, &stats->dwNumAddrs ); break; } } fclose(fp); ret = NO_ERROR; } } #elif defined(HAVE_LIBKSTAT) { static char udp[] = "udp"; kstat_ctl_t *kc; kstat_t *ksp; MIB_UDPTABLE *udp_table; if ((kc = kstat_open()) && (ksp = kstat_lookup( kc, udp, 0, udp )) && kstat_read( kc, ksp, NULL ) != -1 && ksp->ks_type == KSTAT_TYPE_NAMED) { stats->dwInDatagrams = kstat_get_ui32( ksp, "inDatagrams" ); stats->dwNoPorts = 0; /* FIXME */ stats->dwInErrors = kstat_get_ui32( ksp, "inErrors" ); stats->dwOutDatagrams = kstat_get_ui32( ksp, "outDatagrams" ); if (!AllocateAndGetUdpTableFromStack( &udp_table, FALSE, GetProcessHeap(), 0 )) { stats->dwNumAddrs = udp_table->dwNumEntries; HeapFree( GetProcessHeap(), 0, udp_table ); } ret = NO_ERROR; } if (kc) kstat_close( kc ); } #elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS) && defined(HAVE_STRUCT_UDPSTAT_UDPS_IPACKETS) { int mib[] = {CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_STATS}; struct udpstat udp_stat; MIB_UDPTABLE *udp_table; size_t needed = sizeof(udp_stat); if(sysctl(mib, ARRAY_SIZE(mib), &udp_stat, &needed, NULL, 0) != -1) { stats->dwInDatagrams = udp_stat.udps_ipackets; stats->dwOutDatagrams = udp_stat.udps_opackets; stats->dwNoPorts = udp_stat.udps_noport; stats->dwInErrors = udp_stat.udps_hdrops + udp_stat.udps_badsum + udp_stat.udps_fullsock + udp_stat.udps_badlen; if (!AllocateAndGetUdpTableFromStack( &udp_table, FALSE, GetProcessHeap(), 0 )) { stats->dwNumAddrs = udp_table->dwNumEntries; HeapFree( GetProcessHeap(), 0, udp_table ); } ret = NO_ERROR; } else ERR ("failed to get udpstat\n"); } #else FIXME( "unimplemented for IPv4\n" ); #endif return ret; } /****************************************************************** * GetUdpStatistics (IPHLPAPI.@) * * Get the UDP statistics for the local computer. * * PARAMS * stats [Out] buffer for UDP statistics * * RETURNS * Success: NO_ERROR * Failure: error code from winerror.h */ DWORD WINAPI GetUdpStatistics(PMIB_UDPSTATS stats) { return GetUdpStatisticsEx(stats, WS_AF_INET); } static void *append_table_row( HANDLE heap, DWORD flags, void *table, DWORD *table_size, DWORD *table_capacity, const void *row, DWORD row_size ) { DWORD *num_entries = table; /* this must be the first field */ if (*num_entries == *table_capacity) { void *new_table; *table_size += *table_capacity * row_size; if (!(new_table = HeapReAlloc( heap, flags, table, *table_size ))) { HeapFree( heap, 0, table ); return NULL; } num_entries = table = new_table; *table_capacity *= 2; } memcpy( (char *)table + *table_size - (*table_capacity - *num_entries) * row_size, row, row_size ); (*num_entries)++; return table; } struct pid_map { unsigned int pid; unsigned int unix_pid; }; static struct pid_map *get_pid_map( unsigned int *num_entries ) { struct pid_map *map; unsigned int i = 0, buffer_len = 4096, process_count, pos = 0; NTSTATUS ret; char *buffer = NULL, *new_buffer; if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_len ))) return NULL; for (;;) { SERVER_START_REQ( list_processes ) { wine_server_set_reply( req, buffer, buffer_len ); ret = wine_server_call( req ); buffer_len = reply->info_size; process_count = reply->process_count; } SERVER_END_REQ; if (ret != STATUS_INFO_LENGTH_MISMATCH) break; if (!(new_buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, buffer_len ))) { HeapFree( GetProcessHeap(), 0, buffer ); return NULL; } buffer = new_buffer; } if (!(map = HeapAlloc( GetProcessHeap(), 0, process_count * sizeof(*map) ))) { HeapFree( GetProcessHeap(), 0, buffer ); return NULL; } for (i = 0; i < process_count; ++i) { const struct process_info *process; pos = (pos + 7) & ~7; process = (const struct process_info *)(buffer + pos); map[i].pid = process->pid; map[i].unix_pid = process->unix_pid; pos += sizeof(struct process_info) + process->name_len; pos = (pos + 7) & ~7; pos += process->thread_count * sizeof(struct thread_info); } HeapFree( GetProcessHeap(), 0, buffer ); *num_entries = process_count; return map; } static unsigned int find_owning_pid( struct pid_map *map, unsigned int num_entries, UINT_PTR inode ) { #ifdef __linux__ unsigned int i, len_socket; char socket[32]; sprintf( socket, "socket:[%lu]", inode ); len_socket = strlen( socket ); for (i = 0; i < num_entries; i++) { char dir[32]; struct dirent *dirent; DIR *dirfd; sprintf( dir, "/proc/%u/fd", map[i].unix_pid ); if ((dirfd = opendir( dir ))) { while ((dirent = readdir( dirfd ))) { char link[sizeof(dirent->d_name) + 32], name[32]; int len; sprintf( link, "/proc/%u/fd/%s", map[i].unix_pid, dirent->d_name ); if ((len = readlink( link, name, sizeof(name) - 1 )) > 0) name[len] = 0; if (len == len_socket && !strcmp( socket, name )) { closedir( dirfd ); return map[i].pid; } } closedir( dirfd ); } } return 0; #elif defined(HAVE_LIBPROCSTAT) struct procstat *pstat; struct kinfo_proc *proc; struct filestat_list *fds; struct filestat *fd; struct sockstat sock; unsigned int i, proc_count; pstat = procstat_open_sysctl(); if (!pstat) return 0; for (i = 0; i < num_entries; i++) { proc = procstat_getprocs( pstat, KERN_PROC_PID, map[i].unix_pid, &proc_count ); if (!proc || proc_count < 1) continue; fds = procstat_getfiles( pstat, proc, 0 ); if (!fds) { procstat_freeprocs( pstat, proc ); continue; } STAILQ_FOREACH( fd, fds, next ) { char errbuf[_POSIX2_LINE_MAX]; if (fd->fs_type != PS_FST_TYPE_SOCKET) continue; procstat_get_socket_info( pstat, fd, &sock, errbuf ); if (sock.so_pcb == inode) { procstat_freefiles( pstat, fds ); procstat_freeprocs( pstat, proc ); procstat_close( pstat ); return map[i].pid; } } procstat_freefiles( pstat, fds ); procstat_freeprocs( pstat, proc ); } procstat_close( pstat ); return 0; #elif defined(HAVE_PROC_PIDINFO) struct proc_fdinfo *fds; struct socket_fdinfo sock; unsigned int i, j, n; for (i = 0; i < num_entries; i++) { int fd_len = proc_pidinfo( map[i].unix_pid, PROC_PIDLISTFDS, 0, NULL, 0 ); if (fd_len <= 0) continue; fds = HeapAlloc( GetProcessHeap(), 0, fd_len ); if (!fds) continue; proc_pidinfo( map[i].unix_pid, PROC_PIDLISTFDS, 0, fds, fd_len ); n = fd_len / sizeof(struct proc_fdinfo); for (j = 0; j < n; j++) { if (fds[j].proc_fdtype != PROX_FDTYPE_SOCKET) continue; proc_pidfdinfo( map[i].unix_pid, fds[j].proc_fd, PROC_PIDFDSOCKETINFO, &sock, sizeof(sock) ); if (sock.psi.soi_pcb == inode) { HeapFree( GetProcessHeap(), 0, fds ); return map[i].pid; } } HeapFree( GetProcessHeap(), 0, fds ); } return 0; #else FIXME( "not implemented\n" ); return 0; #endif } static DWORD get_udp_table_sizes( UDP_TABLE_CLASS class, DWORD row_count, DWORD *row_size ) { DWORD table_size; switch (class) { case UDP_TABLE_BASIC: { table_size = FIELD_OFFSET(MIB_UDPTABLE, table[row_count]); if (row_size) *row_size = sizeof(MIB_UDPROW); break; } case UDP_TABLE_OWNER_PID: { table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_PID, table[row_count]); if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_PID); break; } case UDP_TABLE_OWNER_MODULE: { table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_MODULE, table[row_count]); if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_MODULE); break; } default: ERR("unhandled class %u\n", class); return 0; } return table_size; } static int compare_udp_rows(const void *a, const void *b) { const MIB_UDPROW *rowA = a; const MIB_UDPROW *rowB = b; int ret; if ((ret = rowA->dwLocalAddr - rowB->dwLocalAddr) != 0) return ret; return rowA->dwLocalPort - rowB->dwLocalPort; } DWORD build_udp_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags, DWORD *size ) { MIB_UDPTABLE *table; MIB_UDPROW_OWNER_MODULE row; DWORD ret = NO_ERROR, count = 16, table_size, row_size; if (!(table_size = get_udp_table_sizes( class, count, &row_size ))) return ERROR_INVALID_PARAMETER; if (!(table = HeapAlloc( heap, flags, table_size ))) return ERROR_OUTOFMEMORY; table->dwNumEntries = 0; memset( &row, 0, sizeof(row) ); #ifdef __linux__ { FILE *fp; if ((fp = fopen( "/proc/net/udp", "r" ))) { char buf[512], *ptr; struct pid_map *map = NULL; unsigned int num_entries = 0; int inode; if (class >= UDP_TABLE_OWNER_PID) map = get_pid_map( &num_entries ); /* skip header line */ ptr = fgets( buf, sizeof(buf), fp ); while ((ptr = fgets( buf, sizeof(buf), fp ))) { if (sscanf( ptr, "%*u: %x:%x %*s %*s %*s %*s %*s %*s %*s %d", &row.dwLocalAddr, &row.dwLocalPort, &inode ) != 3) continue; row.dwLocalPort = htons( row.dwLocalPort ); if (class >= UDP_TABLE_OWNER_PID) row.dwOwningPid = find_owning_pid( map, num_entries, inode ); if (class >= UDP_TABLE_OWNER_MODULE) { row.liCreateTimestamp.QuadPart = 0; /* FIXME */ row.dwFlags = 0; memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) ); } if (!(table = append_table_row( heap, flags, table, &table_size, &count, &row, row_size ))) break; } HeapFree( GetProcessHeap(), 0, map ); fclose( fp ); } else ret = ERROR_NOT_SUPPORTED; } #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK) { void *data; int fd, len; mib2_udpEntry_t *entry; if ((fd = open_streams_mib( "udp" )) != -1) { if ((data = read_mib_entry( fd, MIB2_UDP, MIB2_UDP_ENTRY, &len ))) { for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++) { row.dwLocalAddr = entry->udpLocalAddress; row.dwLocalPort = htons( entry->udpLocalPort ); if (!(table = append_table_row( heap, flags, table, &table_size, &count, &row, row_size ))) break; } HeapFree( GetProcessHeap(), 0, data ); } close( fd ); } else ret = ERROR_NOT_SUPPORTED; } #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN) { size_t Len = 0; char *Buf = NULL; struct xinpgen *pXIG, *pOrigXIG; struct pid_map *pMap = NULL; unsigned NumEntries; if (sysctlbyname ("net.inet.udp.pcblist", NULL, &Len, NULL, 0) < 0) { ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n"); ret = ERROR_NOT_SUPPORTED; goto done; } Buf = HeapAlloc (GetProcessHeap (), 0, Len); if (!Buf) { ret = ERROR_OUTOFMEMORY; goto done; } if (sysctlbyname ("net.inet.udp.pcblist", Buf, &Len, NULL, 0) < 0) { ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n"); ret = ERROR_NOT_SUPPORTED; goto done; } if (class >= UDP_TABLE_OWNER_PID) pMap = get_pid_map( &NumEntries ); /* Might be nothing here; first entry is just a header it seems */ if (Len <= sizeof (struct xinpgen)) goto done; pOrigXIG = (struct xinpgen *)Buf; pXIG = pOrigXIG; for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len); pXIG->xig_len > sizeof (struct xinpgen); pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len)) { #if __FreeBSD_version >= 1200026 struct xinpcb *pINData = (struct xinpcb *)pXIG; struct xsocket *pSockData = &pINData->xi_socket; #else struct inpcb *pINData = &((struct xinpcb *)pXIG)->xi_inp; struct xsocket *pSockData = &((struct xinpcb *)pXIG)->xi_socket; #endif /* Ignore sockets for other protocols */ if (pSockData->xso_protocol != IPPROTO_UDP) continue; /* Ignore PCBs that were freed while generating the data */ if (pINData->inp_gencnt > pOrigXIG->xig_gen) continue; /* we're only interested in IPv4 addresses */ if (!(pINData->inp_vflag & INP_IPV4) || (pINData->inp_vflag & INP_IPV6)) continue; /* If all 0's, skip it */ if (!pINData->inp_laddr.s_addr && !pINData->inp_lport) continue; /* Fill in structure details */ row.dwLocalAddr = pINData->inp_laddr.s_addr; row.dwLocalPort = pINData->inp_lport; if (class >= UDP_TABLE_OWNER_PID) row.dwOwningPid = find_owning_pid( pMap, NumEntries, (UINT_PTR)pSockData->so_pcb ); if (class >= UDP_TABLE_OWNER_MODULE) { row.liCreateTimestamp.QuadPart = 0; /* FIXME */ row.dwFlags = 0; row.SpecificPortBind = !(pINData->inp_flags & INP_ANONPORT); memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) ); } if (!(table = append_table_row( heap, flags, table, &table_size, &count, &row, row_size ))) break; } done: HeapFree( GetProcessHeap(), 0, pMap ); HeapFree (GetProcessHeap (), 0, Buf); } #else FIXME( "not implemented\n" ); ret = ERROR_NOT_SUPPORTED; #endif if (!table) return ERROR_OUTOFMEMORY; if (!ret) { if (order && table->dwNumEntries) qsort( table->table, table->dwNumEntries, row_size, compare_udp_rows ); *tablep = table; } else HeapFree( heap, flags, table ); if (size) *size = get_udp_table_sizes( class, count, NULL ); TRACE( "returning ret %u table %p\n", ret, table ); return ret; } static DWORD get_udp6_table_sizes( UDP_TABLE_CLASS class, DWORD row_count, DWORD *row_size ) { DWORD table_size; switch (class) { case UDP_TABLE_BASIC: { table_size = FIELD_OFFSET(MIB_UDP6TABLE, table[row_count]); if (row_size) *row_size = sizeof(MIB_UDP6ROW); break; } case UDP_TABLE_OWNER_PID: { table_size = FIELD_OFFSET(MIB_UDP6TABLE_OWNER_PID, table[row_count]); if (row_size) *row_size = sizeof(MIB_UDP6ROW_OWNER_PID); break; } case UDP_TABLE_OWNER_MODULE: { table_size = FIELD_OFFSET(MIB_UDP6TABLE_OWNER_MODULE, table[row_count]); if (row_size) *row_size = sizeof(MIB_UDP6ROW_OWNER_MODULE); break; } default: ERR("unhandled class %u\n", class); return 0; } return table_size; } static int compare_udp6_rows(const void *a, const void *b) { const MIB_UDP6ROW *rowA = a; const MIB_UDP6ROW *rowB = b; int ret; if ((ret = memcmp(&rowA->dwLocalAddr, &rowB->dwLocalAddr, sizeof(rowA->dwLocalAddr))) != 0) return ret; if ((ret = rowA->dwLocalScopeId - rowB->dwLocalScopeId) != 0) return ret; return rowA->dwLocalPort - rowB->dwLocalPort; } #if defined(__linux__) || (defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)) struct ipv6_addr_scope { IN6_ADDR addr; DWORD scope; }; static struct ipv6_addr_scope *get_ipv6_addr_scope_table(unsigned int *size) { struct ipv6_addr_scope *table = NULL; unsigned int table_size = 0; #ifdef __linux__ char buf[512], *ptr; FILE *fp; #elif defined(HAVE_GETIFADDRS) struct ifaddrs *addrs, *cur; #endif if (!(table = HeapAlloc( GetProcessHeap(), 0, sizeof(table[0]) ))) return NULL; #ifdef __linux__ if (!(fp = fopen( "/proc/net/if_inet6", "r" ))) goto failed; while ((ptr = fgets( buf, sizeof(buf), fp ))) { WORD a[8]; DWORD scope; struct ipv6_addr_scope *new_table; struct ipv6_addr_scope *entry; unsigned int i; if (sscanf( ptr, "%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx %*s %*s %x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &scope ) != 9) continue; table_size++; if (!(new_table = HeapReAlloc( GetProcessHeap(), 0, table, table_size * sizeof(table[0]) ))) { fclose(fp); goto failed; } table = new_table; entry = &table[table_size - 1]; i = 0; while (i < 8) { entry->addr.u.Word[i] = htons(a[i]); i++; } entry->scope = htons(scope); } fclose(fp); #elif defined(HAVE_GETIFADDRS) if (getifaddrs(&addrs) == -1) goto failed; for (cur = addrs; cur; cur = cur->ifa_next) { struct sockaddr_in6 *sin6; struct ipv6_addr_scope *new_table; struct ipv6_addr_scope *entry; if (cur->ifa_addr->sa_family != AF_INET6) continue; sin6 = (struct sockaddr_in6 *)cur->ifa_addr; table_size++; if (!(new_table = HeapReAlloc( GetProcessHeap(), 0, table, table_size * sizeof(table[0]) ))) { freeifaddrs(addrs); goto failed; } table = new_table; entry = &table[table_size - 1]; memcpy(&entry->addr, &sin6->sin6_addr, sizeof(entry->addr)); entry->scope = sin6->sin6_scope_id; } freeifaddrs(addrs); #else FIXME( "not implemented\n" ); goto failed; #endif *size = table_size; return table; failed: HeapFree( GetProcessHeap(), 0, table ); return NULL; } static DWORD find_ipv6_addr_scope(const IN6_ADDR *addr, const struct ipv6_addr_scope *table, unsigned int size) { const BYTE multicast_scope_mask = 0x0F; const BYTE multicast_scope_shift = 0; unsigned int i = 0; if (WS_IN6_IS_ADDR_UNSPECIFIED(addr)) return 0; if (WS_IN6_IS_ADDR_MULTICAST(addr)) return htons((addr->u.Byte[1] & multicast_scope_mask) >> multicast_scope_shift); if (!table) return -1; while (i < size) { if (memcmp(&table[i].addr, addr, sizeof(table[i].addr)) == 0) return table[i].scope; i++; } return -1; } #endif DWORD build_udp6_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags, DWORD *size ) { MIB_UDP6TABLE *table; MIB_UDP6ROW_OWNER_MODULE row; DWORD ret = NO_ERROR, count = 16, table_size, row_size; if (!(table_size = get_udp6_table_sizes( class, count, &row_size ))) return ERROR_INVALID_PARAMETER; if (!(table = HeapAlloc( heap, flags, table_size ))) return ERROR_OUTOFMEMORY; table->dwNumEntries = 0; memset( &row, 0, sizeof(row) ); #ifdef __linux__ { FILE *fp; if ((fp = fopen( "/proc/net/udp6", "r" ))) { char buf[512], *ptr; struct pid_map *map = NULL; unsigned int num_entries = 0; struct ipv6_addr_scope *addr_scopes; unsigned int addr_scopes_size = 0; int inode; addr_scopes = get_ipv6_addr_scope_table(&addr_scopes_size); if (class >= UDP_TABLE_OWNER_PID) map = get_pid_map( &num_entries ); /* skip header line */ ptr = fgets( buf, sizeof(buf), fp ); while ((ptr = fgets( buf, sizeof(buf), fp ))) { DWORD *local_addr = (DWORD *)&row.ucLocalAddr; if (sscanf( ptr, "%*u: %8x%8x%8x%8x:%x %*s %*s %*s %*s %*s %*s %*s %d", &local_addr[0], &local_addr[1], &local_addr[2], &local_addr[3], &row.dwLocalPort, &inode ) != 6) continue; row.dwLocalScopeId = find_ipv6_addr_scope((const IN6_ADDR *)&row.ucLocalAddr, addr_scopes, addr_scopes_size); row.dwLocalPort = htons( row.dwLocalPort ); if (class >= UDP_TABLE_OWNER_PID) row.dwOwningPid = find_owning_pid( map, num_entries, inode ); if (class >= UDP_TABLE_OWNER_MODULE) { row.liCreateTimestamp.QuadPart = 0; /* FIXME */ row.dwFlags = 0; memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) ); } if (!(table = append_table_row( heap, flags, table, &table_size, &count, &row, row_size ))) break; } HeapFree( GetProcessHeap(), 0, map ); HeapFree( GetProcessHeap(), 0, addr_scopes ); fclose( fp ); } else ret = ERROR_NOT_SUPPORTED; } #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN) { static const char zero[sizeof(IN6_ADDR)] = {0}; size_t len = 0; char *buf = NULL; struct xinpgen *xig, *orig_xig; struct pid_map *map = NULL; unsigned num_entries; struct ipv6_addr_scope *addr_scopes = NULL; unsigned int addr_scopes_size = 0; if (sysctlbyname( "net.inet.udp.pcblist", NULL, &len, NULL, 0 ) < 0) { ERR( "Failure to read net.inet.udp.pcblist via sysctlbyname!\n" ); ret = ERROR_NOT_SUPPORTED; goto done; } buf = HeapAlloc( GetProcessHeap(), 0, len ); if (!buf) { ret = ERROR_OUTOFMEMORY; goto done; } if (sysctlbyname( "net.inet.udp.pcblist", buf, &len, NULL, 0 ) < 0) { ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n"); ret = ERROR_NOT_SUPPORTED; goto done; } addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); if (!addr_scopes) { ret = ERROR_OUTOFMEMORY; goto done; } if (class >= UDP_TABLE_OWNER_PID) map = get_pid_map( &num_entries ); /* Might be nothing here; first entry is just a header it seems */ if (len <= sizeof (struct xinpgen)) goto done; orig_xig = (struct xinpgen *)buf; xig = orig_xig; for (xig = (struct xinpgen *)((char *)xig + xig->xig_len); xig->xig_len > sizeof (struct xinpgen); xig = (struct xinpgen *)((char *)xig + xig->xig_len)) { #if __FreeBSD_version >= 1200026 struct xinpcb *in = (struct xinpcb *)xig; struct xsocket *sock = &in->xi_socket; #else struct inpcb *in = &((struct xinpcb *)xig)->xi_inp; struct xsocket *sock = &((struct xinpcb *)xig)->xi_socket; #endif /* Ignore sockets for other protocols */ if (sock->xso_protocol != IPPROTO_UDP) continue; /* Ignore PCBs that were freed while generating the data */ if (in->inp_gencnt > orig_xig->xig_gen) continue; /* we're only interested in IPv6 addresses */ if (!(in->inp_vflag & INP_IPV6) || (in->inp_vflag & INP_IPV4)) continue; /* If all 0's, skip it */ if (!memcmp( &in->in6p_laddr.s6_addr, zero, sizeof(zero) ) && !in->inp_lport) continue; /* Fill in structure details */ memcpy(row.ucLocalAddr, &in->in6p_laddr.s6_addr, sizeof(row.ucLocalAddr)); row.dwLocalPort = in->inp_lport; row.dwLocalScopeId = find_ipv6_addr_scope((const IN6_ADDR *)&row.ucLocalAddr, addr_scopes, addr_scopes_size); if (class >= UDP_TABLE_OWNER_PID) row.dwOwningPid = find_owning_pid( map, num_entries, (UINT_PTR)sock->so_pcb ); if (class >= UDP_TABLE_OWNER_MODULE) { row.liCreateTimestamp.QuadPart = 0; /* FIXME */ row.dwFlags = 0; row.SpecificPortBind = !(in->inp_flags & INP_ANONPORT); memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) ); } if (!(table = append_table_row( heap, flags, table, &table_size, &count, &row, row_size ))) break; } done: HeapFree( GetProcessHeap(), 0, map ); HeapFree( GetProcessHeap(), 0, buf ); HeapFree( GetProcessHeap(), 0, addr_scopes ); } #else FIXME( "not implemented\n" ); ret = ERROR_NOT_SUPPORTED; #endif if (!table) return ERROR_OUTOFMEMORY; if (!ret) { if (order && table->dwNumEntries) qsort( table->table, table->dwNumEntries, row_size, compare_udp6_rows ); *tablep = table; } else HeapFree( heap, flags, table ); if (size) *size = get_udp6_table_sizes( class, count, NULL ); TRACE( "returning ret %u table %p\n", ret, table ); return ret; } /****************************************************************** * AllocateAndGetUdpTableFromStack (IPHLPAPI.@) * * Get the UDP listener table. * Like GetUdpTable(), but allocate the returned table from heap. * * PARAMS * ppUdpTable [Out] pointer into which the MIB_UDPTABLE is * allocated and returned. * bOrder [In] whether to sort the table * heap [In] heap from which the table is allocated * flags [In] flags to HeapAlloc * * RETURNS * ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable() * returns otherwise. */ DWORD WINAPI AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE *ppUdpTable, BOOL bOrder, HANDLE heap, DWORD flags) { TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable, bOrder, heap, flags); if (!ppUdpTable) return ERROR_INVALID_PARAMETER; return build_udp_table( UDP_TABLE_BASIC, (void **)ppUdpTable, bOrder, heap, flags, NULL ); }