| /* Copyright (C) 2003,2006,2011 Juan Lang | |
| * | |
| * 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 <stdarg.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #ifdef HAVE_UNISTD_H | |
| #include <unistd.h> | |
| #endif | |
| #include <sys/types.h> | |
| #ifdef HAVE_SYS_PARAM_H | |
| #include <sys/param.h> | |
| #endif | |
| #ifdef HAVE_SYS_SOCKET_H | |
| #include <sys/socket.h> | |
| #endif | |
| #ifdef HAVE_NETINET_IN_H | |
| #include <netinet/in.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_ARP_H | |
| #include <net/if_arp.h> | |
| #endif | |
| #ifdef HAVE_NET_ROUTE_H | |
| #include <net/route.h> | |
| #endif | |
| #ifdef HAVE_SYS_IOCTL_H | |
| #include <sys/ioctl.h> | |
| #endif | |
| #ifdef HAVE_SYS_SYSCTL_H | |
| #include <sys/sysctl.h> | |
| #endif | |
| #ifdef HAVE_SYS_SOCKIO_H | |
| #include <sys/sockio.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_IFADDRS_H | |
| #include <ifaddrs.h> | |
| #endif | |
| #ifdef HAVE_LINUX_RTNETLINK_H | |
| #include <linux/rtnetlink.h> | |
| #endif | |
| #include "ifenum.h" | |
| #include "ws2ipdef.h" | |
| #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN | |
| #define ifreq_len(ifr) \ | |
| max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len) | |
| #else | |
| #define ifreq_len(ifr) sizeof(struct ifreq) | |
| #endif | |
| #ifndef ETH_ALEN | |
| #define ETH_ALEN 6 | |
| #endif | |
| #ifndef IF_NAMESIZE | |
| #define IF_NAMESIZE 16 | |
| #endif | |
| #ifndef INADDR_NONE | |
| #define INADDR_NONE (~0U) | |
| #endif | |
| #define INITIAL_INTERFACES_ASSUMED 4 | |
| /* Functions */ | |
| static BOOL isLoopbackInterface(int fd, const char *name) | |
| { | |
| BOOL ret = FALSE; | |
| if (name) { | |
| struct ifreq ifr; | |
| lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); | |
| if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0) | |
| ret = ifr.ifr_flags & IFF_LOOPBACK; | |
| } | |
| return !!ret; | |
| } | |
| /* The comments say MAX_ADAPTER_NAME is required, but really only IF_NAMESIZE | |
| * bytes are necessary. | |
| */ | |
| char *getInterfaceNameByIndex(IF_INDEX index, char *name) | |
| { | |
| return if_indextoname(index, name); | |
| } | |
| DWORD getInterfaceIndexByName(const char *name, IF_INDEX *index) | |
| { | |
| DWORD ret; | |
| unsigned int idx; | |
| if (!name) | |
| return ERROR_INVALID_PARAMETER; | |
| if (!index) | |
| return ERROR_INVALID_PARAMETER; | |
| idx = if_nametoindex(name); | |
| if (idx) { | |
| *index = idx; | |
| ret = NO_ERROR; | |
| } | |
| else | |
| ret = ERROR_INVALID_DATA; | |
| return ret; | |
| } | |
| BOOL isIfIndexLoopback(ULONG idx) | |
| { | |
| BOOL ret = FALSE; | |
| char name[IFNAMSIZ]; | |
| int fd; | |
| getInterfaceNameByIndex(idx, name); | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| ret = isLoopbackInterface(fd, name); | |
| close(fd); | |
| } | |
| return ret; | |
| } | |
| #ifdef HAVE_IF_NAMEINDEX | |
| DWORD get_interface_indices( BOOL skip_loopback, InterfaceIndexTable **table ) | |
| { | |
| DWORD count = 0, i; | |
| struct if_nameindex *p, *indices = if_nameindex(); | |
| InterfaceIndexTable *ret; | |
| if (table) *table = NULL; | |
| if (!indices) return 0; | |
| for (p = indices; p->if_name; p++) | |
| { | |
| if (skip_loopback && isIfIndexLoopback( p->if_index )) continue; | |
| count++; | |
| } | |
| if (table) | |
| { | |
| ret = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET(InterfaceIndexTable, indexes[count]) ); | |
| if (!ret) | |
| { | |
| count = 0; | |
| goto end; | |
| } | |
| for (p = indices, i = 0; p->if_name && i < count; p++) | |
| { | |
| if (skip_loopback && isIfIndexLoopback( p->if_index )) continue; | |
| ret->indexes[i++] = p->if_index; | |
| } | |
| ret->numIndexes = count = i; | |
| *table = ret; | |
| } | |
| end: | |
| if_freenameindex( indices ); | |
| return count; | |
| } | |
| #elif defined(HAVE_LINUX_RTNETLINK_H) | |
| static int open_netlink( int *pid ) | |
| { | |
| int fd = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); | |
| struct sockaddr_nl addr; | |
| socklen_t len; | |
| if (fd < 0) return fd; | |
| memset( &addr, 0, sizeof(addr) ); | |
| addr.nl_family = AF_NETLINK; | |
| if (bind( fd, (struct sockaddr *)&addr, sizeof(addr) ) < 0) | |
| goto fail; | |
| len = sizeof(addr); | |
| if (getsockname( fd, (struct sockaddr *)&addr, &len ) < 0) | |
| goto fail; | |
| *pid = addr.nl_pid; | |
| return fd; | |
| fail: | |
| close( fd ); | |
| return -1; | |
| } | |
| static int send_netlink_req( int fd, int pid, int type, int *seq_no ) | |
| { | |
| static LONG seq; | |
| struct request | |
| { | |
| struct nlmsghdr hdr; | |
| struct rtgenmsg gen; | |
| } req; | |
| struct sockaddr_nl addr; | |
| req.hdr.nlmsg_len = sizeof(req); | |
| req.hdr.nlmsg_type = type; | |
| req.hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; | |
| req.hdr.nlmsg_pid = pid; | |
| req.hdr.nlmsg_seq = InterlockedIncrement( &seq ); | |
| req.gen.rtgen_family = AF_UNSPEC; | |
| memset( &addr, 0, sizeof(addr) ); | |
| addr.nl_family = AF_NETLINK; | |
| if (sendto( fd, &req, sizeof(req), 0, (struct sockaddr *)&addr, sizeof(addr) ) != sizeof(req)) | |
| return -1; | |
| *seq_no = req.hdr.nlmsg_seq; | |
| return 0; | |
| } | |
| struct netlink_reply | |
| { | |
| struct netlink_reply *next; | |
| int size; | |
| struct nlmsghdr *hdr; | |
| }; | |
| static void free_netlink_reply( struct netlink_reply *data ) | |
| { | |
| struct netlink_reply *ptr; | |
| while( data ) | |
| { | |
| ptr = data->next; | |
| HeapFree( GetProcessHeap(), 0, data ); | |
| data = ptr; | |
| } | |
| } | |
| static int recv_netlink_reply( int fd, int pid, int seq, struct netlink_reply **data ) | |
| { | |
| int bufsize = getpagesize(); | |
| int left, read; | |
| BOOL done = FALSE; | |
| socklen_t sa_len; | |
| struct sockaddr_nl addr; | |
| struct netlink_reply *cur, *last = NULL; | |
| struct nlmsghdr *hdr; | |
| char *buf; | |
| *data = NULL; | |
| buf = HeapAlloc( GetProcessHeap(), 0, bufsize ); | |
| if (!buf) return -1; | |
| do | |
| { | |
| left = read = recvfrom( fd, buf, bufsize, 0, (struct sockaddr *)&addr, &sa_len ); | |
| if (read < 0) goto fail; | |
| if (addr.nl_pid != 0) continue; /* not from kernel */ | |
| for (hdr = (struct nlmsghdr *)buf; NLMSG_OK(hdr, left); hdr = NLMSG_NEXT(hdr, left)) | |
| { | |
| if (hdr->nlmsg_pid != pid || hdr->nlmsg_seq != seq) continue; | |
| if (hdr->nlmsg_type == NLMSG_DONE) | |
| { | |
| done = TRUE; | |
| break; | |
| } | |
| } | |
| cur = HeapAlloc( GetProcessHeap(), 0, sizeof(*cur) + read ); | |
| if (!cur) goto fail; | |
| cur->next = NULL; | |
| cur->size = read; | |
| cur->hdr = (struct nlmsghdr *)(cur + 1); | |
| memcpy( cur->hdr, buf, read ); | |
| if (last) last->next = cur; | |
| else *data = cur; | |
| last = cur; | |
| } while (!done); | |
| HeapFree( GetProcessHeap(), 0, buf ); | |
| return 0; | |
| fail: | |
| free_netlink_reply( *data ); | |
| HeapFree( GetProcessHeap(), 0, buf ); | |
| return -1; | |
| } | |
| static DWORD get_indices_from_reply( struct netlink_reply *reply, int pid, int seq, | |
| BOOL skip_loopback, InterfaceIndexTable *table ) | |
| { | |
| struct nlmsghdr *hdr; | |
| struct netlink_reply *r; | |
| int count = 0; | |
| for (r = reply; r; r = r->next) | |
| { | |
| int size = r->size; | |
| for (hdr = r->hdr; NLMSG_OK(hdr, size); hdr = NLMSG_NEXT(hdr, size)) | |
| { | |
| if (hdr->nlmsg_pid != pid || hdr->nlmsg_seq != seq) continue; | |
| if (hdr->nlmsg_type == NLMSG_DONE) break; | |
| if (hdr->nlmsg_type == RTM_NEWLINK) | |
| { | |
| struct ifinfomsg *info = NLMSG_DATA(hdr); | |
| if (skip_loopback && (info->ifi_flags & IFF_LOOPBACK)) continue; | |
| if (table) table->indexes[count] = info->ifi_index; | |
| count++; | |
| } | |
| } | |
| } | |
| return count; | |
| } | |
| DWORD get_interface_indices( BOOL skip_loopback, InterfaceIndexTable **table ) | |
| { | |
| int fd, pid, seq; | |
| struct netlink_reply *reply = NULL; | |
| DWORD count = 0; | |
| if (table) *table = NULL; | |
| fd = open_netlink( &pid ); | |
| if (fd < 0) return 0; | |
| if (send_netlink_req( fd, pid, RTM_GETLINK, &seq ) < 0) | |
| goto end; | |
| if (recv_netlink_reply( fd, pid, seq, &reply ) < 0) | |
| goto end; | |
| count = get_indices_from_reply( reply, pid, seq, skip_loopback, NULL ); | |
| if (table) | |
| { | |
| InterfaceIndexTable *ret = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET(InterfaceIndexTable, indexes[count]) ); | |
| if (!ret) | |
| { | |
| count = 0; | |
| goto end; | |
| } | |
| ret->numIndexes = count; | |
| get_indices_from_reply( reply, pid, seq, skip_loopback, ret ); | |
| *table = ret; | |
| } | |
| end: | |
| free_netlink_reply( reply ); | |
| close( fd ); | |
| return count; | |
| } | |
| #else | |
| DWORD get_interface_indices( BOOL skip_loopback, InterfaceIndexTable **table ) | |
| { | |
| if (table) *table = NULL; | |
| return 0; | |
| } | |
| #endif | |
| static DWORD getInterfaceBCastAddrByName(const char *name) | |
| { | |
| DWORD ret = INADDR_ANY; | |
| if (name) { | |
| int fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); | |
| if (ioctl(fd, SIOCGIFBRDADDR, &ifr) == 0) | |
| memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD)); | |
| close(fd); | |
| } | |
| } | |
| return ret; | |
| } | |
| static DWORD getInterfaceMaskByName(const char *name) | |
| { | |
| DWORD ret = INADDR_NONE; | |
| if (name) { | |
| int fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); | |
| if (ioctl(fd, SIOCGIFNETMASK, &ifr) == 0) | |
| memcpy(&ret, ifr.ifr_addr.sa_data + 2, sizeof(DWORD)); | |
| close(fd); | |
| } | |
| } | |
| return ret; | |
| } | |
| #if defined (SIOCGIFHWADDR) && defined (HAVE_STRUCT_IFREQ_IFR_HWADDR) | |
| static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, | |
| PDWORD type) | |
| { | |
| DWORD ret; | |
| int fd; | |
| if (!name || !len || !addr || !type) | |
| return ERROR_INVALID_PARAMETER; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| memset(&ifr, 0, sizeof(struct ifreq)); | |
| lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); | |
| if ((ioctl(fd, SIOCGIFHWADDR, &ifr))) | |
| ret = ERROR_INVALID_DATA; | |
| else { | |
| unsigned int addrLen; | |
| switch (ifr.ifr_hwaddr.sa_family) | |
| { | |
| #ifdef ARPHRD_LOOPBACK | |
| case ARPHRD_LOOPBACK: | |
| addrLen = 0; | |
| *type = MIB_IF_TYPE_LOOPBACK; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_ETHER | |
| case ARPHRD_ETHER: | |
| addrLen = ETH_ALEN; | |
| *type = MIB_IF_TYPE_ETHERNET; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_FDDI | |
| case ARPHRD_FDDI: | |
| addrLen = ETH_ALEN; | |
| *type = MIB_IF_TYPE_FDDI; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_IEEE802 | |
| case ARPHRD_IEEE802: /* 802.2 Ethernet && Token Ring, guess TR? */ | |
| addrLen = ETH_ALEN; | |
| *type = MIB_IF_TYPE_TOKENRING; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_IEEE802_TR | |
| case ARPHRD_IEEE802_TR: /* also Token Ring? */ | |
| addrLen = ETH_ALEN; | |
| *type = MIB_IF_TYPE_TOKENRING; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_SLIP | |
| case ARPHRD_SLIP: | |
| addrLen = 0; | |
| *type = MIB_IF_TYPE_SLIP; | |
| break; | |
| #endif | |
| #ifdef ARPHRD_PPP | |
| case ARPHRD_PPP: | |
| addrLen = 0; | |
| *type = MIB_IF_TYPE_PPP; | |
| break; | |
| #endif | |
| default: | |
| addrLen = min(MAX_INTERFACE_PHYSADDR, sizeof(ifr.ifr_hwaddr.sa_data)); | |
| *type = MIB_IF_TYPE_OTHER; | |
| } | |
| if (addrLen > *len) { | |
| ret = ERROR_INSUFFICIENT_BUFFER; | |
| *len = addrLen; | |
| } | |
| else { | |
| if (addrLen > 0) | |
| memcpy(addr, ifr.ifr_hwaddr.sa_data, addrLen); | |
| /* zero out remaining bytes for broken implementations */ | |
| memset(addr + addrLen, 0, *len - addrLen); | |
| *len = addrLen; | |
| ret = NO_ERROR; | |
| } | |
| } | |
| close(fd); | |
| } | |
| else | |
| ret = ERROR_NO_MORE_FILES; | |
| return ret; | |
| } | |
| #elif defined (SIOCGARP) | |
| static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, | |
| PDWORD type) | |
| { | |
| DWORD ret; | |
| int fd; | |
| if (!name || !len || !addr || !type) | |
| return ERROR_INVALID_PARAMETER; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| if (isLoopbackInterface(fd, name)) { | |
| *type = MIB_IF_TYPE_LOOPBACK; | |
| memset(addr, 0, *len); | |
| *len = 0; | |
| ret=NOERROR; | |
| } | |
| else { | |
| struct arpreq arp; | |
| struct sockaddr_in *saddr; | |
| struct ifreq ifr; | |
| /* get IP addr */ | |
| lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); | |
| ioctl(fd, SIOCGIFADDR, &ifr); | |
| memset(&arp, 0, sizeof(struct arpreq)); | |
| arp.arp_pa.sa_family = AF_INET; | |
| saddr = (struct sockaddr_in *)&arp; /* proto addr is first member */ | |
| saddr->sin_family = AF_INET; | |
| memcpy(&saddr->sin_addr.s_addr, ifr.ifr_addr.sa_data + 2, sizeof(DWORD)); | |
| if ((ioctl(fd, SIOCGARP, &arp))) | |
| ret = ERROR_INVALID_DATA; | |
| else { | |
| /* FIXME: heh: who said it was ethernet? */ | |
| int addrLen = ETH_ALEN; | |
| if (addrLen > *len) { | |
| ret = ERROR_INSUFFICIENT_BUFFER; | |
| *len = addrLen; | |
| } | |
| else { | |
| if (addrLen > 0) | |
| memcpy(addr, &arp.arp_ha.sa_data[0], addrLen); | |
| /* zero out remaining bytes for broken implementations */ | |
| memset(addr + addrLen, 0, *len - addrLen); | |
| *len = addrLen; | |
| *type = MIB_IF_TYPE_ETHERNET; | |
| ret = NO_ERROR; | |
| } | |
| } | |
| } | |
| close(fd); | |
| } | |
| else | |
| ret = ERROR_NO_MORE_FILES; | |
| return ret; | |
| } | |
| #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H) | |
| static DWORD getInterfacePhysicalByName(const char *name, PDWORD len, PBYTE addr, | |
| PDWORD type) | |
| { | |
| DWORD ret; | |
| struct if_msghdr *ifm; | |
| struct sockaddr_dl *sdl; | |
| u_char *p, *buf; | |
| size_t mibLen; | |
| int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; | |
| unsigned addrLen; | |
| BOOL found = FALSE; | |
| if (!name || !len || !addr || !type) | |
| return ERROR_INVALID_PARAMETER; | |
| if (sysctl(mib, 6, NULL, &mibLen, NULL, 0) < 0) | |
| return ERROR_NO_MORE_FILES; | |
| buf = HeapAlloc(GetProcessHeap(), 0, mibLen); | |
| if (!buf) | |
| return ERROR_NOT_ENOUGH_MEMORY; | |
| if (sysctl(mib, 6, buf, &mibLen, NULL, 0) < 0) { | |
| HeapFree(GetProcessHeap(), 0, buf); | |
| return ERROR_NO_MORE_FILES; | |
| } | |
| ret = ERROR_INVALID_DATA; | |
| for (p = buf; !found && p < buf + mibLen; p += ifm->ifm_msglen) { | |
| ifm = (struct if_msghdr *)p; | |
| sdl = (struct sockaddr_dl *)(ifm + 1); | |
| if (ifm->ifm_type != RTM_IFINFO || (ifm->ifm_addrs & RTA_IFP) == 0) | |
| continue; | |
| if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || | |
| memcmp(sdl->sdl_data, name, max(sdl->sdl_nlen, strlen(name))) != 0) | |
| continue; | |
| found = TRUE; | |
| addrLen = min(MAX_INTERFACE_PHYSADDR, sdl->sdl_alen); | |
| if (addrLen > *len) { | |
| ret = ERROR_INSUFFICIENT_BUFFER; | |
| *len = addrLen; | |
| } | |
| else { | |
| if (addrLen > 0) | |
| memcpy(addr, LLADDR(sdl), addrLen); | |
| /* zero out remaining bytes for broken implementations */ | |
| memset(addr + addrLen, 0, *len - addrLen); | |
| *len = addrLen; | |
| #if defined(HAVE_NET_IF_TYPES_H) | |
| switch (sdl->sdl_type) | |
| { | |
| case IFT_ETHER: | |
| *type = MIB_IF_TYPE_ETHERNET; | |
| break; | |
| case IFT_FDDI: | |
| *type = MIB_IF_TYPE_FDDI; | |
| break; | |
| case IFT_ISO88024: /* Token Bus */ | |
| *type = MIB_IF_TYPE_TOKENRING; | |
| break; | |
| case IFT_ISO88025: /* Token Ring */ | |
| *type = MIB_IF_TYPE_TOKENRING; | |
| break; | |
| case IFT_PPP: | |
| *type = MIB_IF_TYPE_PPP; | |
| break; | |
| case IFT_SLIP: | |
| *type = MIB_IF_TYPE_SLIP; | |
| break; | |
| case IFT_LOOP: | |
| *type = MIB_IF_TYPE_LOOPBACK; | |
| break; | |
| default: | |
| *type = MIB_IF_TYPE_OTHER; | |
| } | |
| #else | |
| /* default if we don't know */ | |
| *type = MIB_IF_TYPE_ETHERNET; | |
| #endif | |
| ret = NO_ERROR; | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, buf); | |
| return ret; | |
| } | |
| #endif | |
| DWORD getInterfacePhysicalByIndex(IF_INDEX index, PDWORD len, PBYTE addr, | |
| PDWORD type) | |
| { | |
| char nameBuf[IF_NAMESIZE]; | |
| char *name = getInterfaceNameByIndex(index, nameBuf); | |
| if (name) | |
| return getInterfacePhysicalByName(name, len, addr, type); | |
| else | |
| return ERROR_INVALID_DATA; | |
| } | |
| DWORD getInterfaceMtuByName(const char *name, PDWORD mtu) | |
| { | |
| DWORD ret; | |
| int fd; | |
| if (!name) | |
| return ERROR_INVALID_PARAMETER; | |
| if (!mtu) | |
| return ERROR_INVALID_PARAMETER; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); | |
| if ((ioctl(fd, SIOCGIFMTU, &ifr))) | |
| ret = ERROR_INVALID_DATA; | |
| else { | |
| #ifndef __sun | |
| *mtu = ifr.ifr_mtu; | |
| #else | |
| *mtu = ifr.ifr_metric; | |
| #endif | |
| ret = NO_ERROR; | |
| } | |
| close(fd); | |
| } | |
| else | |
| ret = ERROR_NO_MORE_FILES; | |
| return ret; | |
| } | |
| DWORD getInterfaceStatusByName(const char *name, INTERNAL_IF_OPER_STATUS *status) | |
| { | |
| DWORD ret; | |
| int fd; | |
| if (!name) | |
| return ERROR_INVALID_PARAMETER; | |
| if (!status) | |
| return ERROR_INVALID_PARAMETER; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| struct ifreq ifr; | |
| lstrcpynA(ifr.ifr_name, name, IFNAMSIZ); | |
| if ((ioctl(fd, SIOCGIFFLAGS, &ifr))) | |
| ret = ERROR_INVALID_DATA; | |
| else { | |
| if (ifr.ifr_flags & IFF_UP) | |
| *status = MIB_IF_OPER_STATUS_OPERATIONAL; | |
| else | |
| *status = MIB_IF_OPER_STATUS_NON_OPERATIONAL; | |
| ret = NO_ERROR; | |
| } | |
| close(fd); | |
| } | |
| else | |
| ret = ERROR_NO_MORE_FILES; | |
| return ret; | |
| } | |
| DWORD getInterfaceEntryByName(const char *name, PMIB_IFROW entry) | |
| { | |
| BYTE addr[MAX_INTERFACE_PHYSADDR]; | |
| DWORD ret, len = sizeof(addr), type; | |
| if (!name) | |
| return ERROR_INVALID_PARAMETER; | |
| if (!entry) | |
| return ERROR_INVALID_PARAMETER; | |
| if (getInterfacePhysicalByName(name, &len, addr, &type) == NO_ERROR) { | |
| WCHAR *assigner; | |
| const char *walker; | |
| memset(entry, 0, sizeof(MIB_IFROW)); | |
| for (assigner = entry->wszName, walker = name; *walker; | |
| walker++, assigner++) | |
| *assigner = *walker; | |
| *assigner = 0; | |
| getInterfaceIndexByName(name, &entry->dwIndex); | |
| entry->dwPhysAddrLen = len; | |
| memcpy(entry->bPhysAddr, addr, len); | |
| memset(entry->bPhysAddr + len, 0, sizeof(entry->bPhysAddr) - len); | |
| entry->dwType = type; | |
| /* FIXME: how to calculate real speed? */ | |
| getInterfaceMtuByName(name, &entry->dwMtu); | |
| /* lie, there's no "administratively down" here */ | |
| entry->dwAdminStatus = MIB_IF_ADMIN_STATUS_UP; | |
| getInterfaceStatusByName(name, &entry->dwOperStatus); | |
| /* punt on dwLastChange? */ | |
| entry->dwDescrLen = min(strlen(name), MAX_INTERFACE_DESCRIPTION - 1); | |
| memcpy(entry->bDescr, name, entry->dwDescrLen); | |
| entry->bDescr[entry->dwDescrLen] = '\0'; | |
| entry->dwDescrLen++; | |
| ret = NO_ERROR; | |
| } | |
| else | |
| ret = ERROR_INVALID_DATA; | |
| return ret; | |
| } | |
| static DWORD getIPAddrRowByName(PMIB_IPADDRROW ipAddrRow, const char *ifName, | |
| const struct sockaddr *sa) | |
| { | |
| DWORD ret, bcast; | |
| ret = getInterfaceIndexByName(ifName, &ipAddrRow->dwIndex); | |
| memcpy(&ipAddrRow->dwAddr, sa->sa_data + 2, sizeof(DWORD)); | |
| ipAddrRow->dwMask = getInterfaceMaskByName(ifName); | |
| /* the dwBCastAddr member isn't the broadcast address, it indicates whether | |
| * the interface uses the 1's broadcast address (1) or the 0's broadcast | |
| * address (0). | |
| */ | |
| bcast = getInterfaceBCastAddrByName(ifName); | |
| ipAddrRow->dwBCastAddr = (bcast & ipAddrRow->dwMask) ? 1 : 0; | |
| /* FIXME: hardcoded reasm size, not sure where to get it */ | |
| ipAddrRow->dwReasmSize = 65535; | |
| ipAddrRow->unused1 = 0; | |
| /* wType is a bit field composed of MIB_IPADDR_* flags. Windows <= XP seems | |
| * to like returning undocumented values 0x20 + 0x02 but for our current | |
| * needs returning MIB_IPADDR_PRIMARY is enough. | |
| */ | |
| ipAddrRow->wType = MIB_IPADDR_PRIMARY; | |
| return ret; | |
| } | |
| #if defined(HAVE_IFADDRS_H) && defined(HAVE_GETIFADDRS) | |
| /* Counts the IPv4 addresses in the system using the return value from | |
| * getifaddrs, returning the count. | |
| */ | |
| static DWORD countIPv4Addresses(struct ifaddrs *ifa) | |
| { | |
| DWORD numAddresses = 0; | |
| for (; ifa; ifa = ifa->ifa_next) | |
| if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) | |
| numAddresses++; | |
| return numAddresses; | |
| } | |
| DWORD getNumIPAddresses(void) | |
| { | |
| DWORD numAddresses = 0; | |
| struct ifaddrs *ifa; | |
| if (!getifaddrs(&ifa)) | |
| { | |
| numAddresses = countIPv4Addresses(ifa); | |
| freeifaddrs(ifa); | |
| } | |
| return numAddresses; | |
| } | |
| DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags) | |
| { | |
| DWORD ret; | |
| if (!ppIpAddrTable) | |
| ret = ERROR_INVALID_PARAMETER; | |
| else | |
| { | |
| struct ifaddrs *ifa; | |
| if (!getifaddrs(&ifa)) | |
| { | |
| DWORD size = sizeof(MIB_IPADDRTABLE); | |
| DWORD numAddresses = countIPv4Addresses(ifa); | |
| if (numAddresses > 1) | |
| size += (numAddresses - 1) * sizeof(MIB_IPADDRROW); | |
| *ppIpAddrTable = HeapAlloc(heap, flags, size); | |
| if (*ppIpAddrTable) | |
| { | |
| DWORD i = 0; | |
| struct ifaddrs *ifp; | |
| ret = NO_ERROR; | |
| (*ppIpAddrTable)->dwNumEntries = numAddresses; | |
| for (ifp = ifa; !ret && ifp; ifp = ifp->ifa_next) | |
| { | |
| if (!ifp->ifa_addr || ifp->ifa_addr->sa_family != AF_INET) | |
| continue; | |
| ret = getIPAddrRowByName(&(*ppIpAddrTable)->table[i], ifp->ifa_name, | |
| ifp->ifa_addr); | |
| i++; | |
| } | |
| if (ret) | |
| HeapFree(GetProcessHeap(), 0, *ppIpAddrTable); | |
| } | |
| else | |
| ret = ERROR_OUTOFMEMORY; | |
| freeifaddrs(ifa); | |
| } | |
| else | |
| ret = ERROR_INVALID_PARAMETER; | |
| } | |
| return ret; | |
| } | |
| ULONG v6addressesFromIndex(IF_INDEX index, SOCKET_ADDRESS **addrs, ULONG *num_addrs, SOCKET_ADDRESS **masks) | |
| { | |
| struct ifaddrs *ifa; | |
| ULONG ret; | |
| if (!getifaddrs(&ifa)) | |
| { | |
| struct ifaddrs *p; | |
| ULONG n; | |
| char name[IFNAMSIZ]; | |
| getInterfaceNameByIndex(index, name); | |
| for (p = ifa, n = 0; p; p = p->ifa_next) | |
| if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET6 && | |
| !strcmp(name, p->ifa_name)) | |
| n++; | |
| if (n) | |
| { | |
| *addrs = HeapAlloc(GetProcessHeap(), 0, n * (sizeof(SOCKET_ADDRESS) + | |
| sizeof(struct WS_sockaddr_in6))); | |
| *masks = HeapAlloc(GetProcessHeap(), 0, n * (sizeof(SOCKET_ADDRESS) + | |
| sizeof(struct WS_sockaddr_in6))); | |
| if (*addrs && *masks) | |
| { | |
| struct WS_sockaddr_in6 *next_addr = (struct WS_sockaddr_in6 *)( | |
| (BYTE *)*addrs + n * sizeof(SOCKET_ADDRESS)); | |
| struct WS_sockaddr_in6 *mask_addr = (struct WS_sockaddr_in6 *)( | |
| (BYTE *)*masks + n * sizeof(SOCKET_ADDRESS)); | |
| for (p = ifa, n = 0; p; p = p->ifa_next) | |
| { | |
| if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET6 && | |
| !strcmp(name, p->ifa_name)) | |
| { | |
| struct sockaddr_in6 *addr = (struct sockaddr_in6 *)p->ifa_addr; | |
| struct sockaddr_in6 *mask = (struct sockaddr_in6 *)p->ifa_netmask; | |
| next_addr->sin6_family = WS_AF_INET6; | |
| next_addr->sin6_port = addr->sin6_port; | |
| next_addr->sin6_flowinfo = addr->sin6_flowinfo; | |
| memcpy(&next_addr->sin6_addr, &addr->sin6_addr, | |
| sizeof(next_addr->sin6_addr)); | |
| next_addr->sin6_scope_id = addr->sin6_scope_id; | |
| (*addrs)[n].lpSockaddr = (LPSOCKADDR)next_addr; | |
| (*addrs)[n].iSockaddrLength = sizeof(struct WS_sockaddr_in6); | |
| next_addr++; | |
| mask_addr->sin6_family = WS_AF_INET6; | |
| mask_addr->sin6_port = mask->sin6_port; | |
| mask_addr->sin6_flowinfo = mask->sin6_flowinfo; | |
| memcpy(&mask_addr->sin6_addr, &mask->sin6_addr, | |
| sizeof(mask_addr->sin6_addr)); | |
| mask_addr->sin6_scope_id = mask->sin6_scope_id; | |
| (*masks)[n].lpSockaddr = (LPSOCKADDR)mask_addr; | |
| (*masks)[n].iSockaddrLength = sizeof(struct WS_sockaddr_in6); | |
| mask_addr++; | |
| n++; | |
| } | |
| } | |
| *num_addrs = n; | |
| ret = ERROR_SUCCESS; | |
| } | |
| else | |
| { | |
| HeapFree(GetProcessHeap(), 0, *addrs); | |
| HeapFree(GetProcessHeap(), 0, *masks); | |
| ret = ERROR_OUTOFMEMORY; | |
| } | |
| } | |
| else | |
| { | |
| *addrs = NULL; | |
| *num_addrs = 0; | |
| *masks = NULL; | |
| ret = ERROR_SUCCESS; | |
| } | |
| freeifaddrs(ifa); | |
| } | |
| else | |
| ret = ERROR_NO_DATA; | |
| return ret; | |
| } | |
| #else | |
| /* Enumerates the IP addresses in the system using SIOCGIFCONF, returning | |
| * the count to you in *pcAddresses. It also returns to you the struct ifconf | |
| * used by the call to ioctl, so that you may process the addresses further. | |
| * Free ifc->ifc_buf using HeapFree. | |
| * Returns NO_ERROR on success, something else on failure. | |
| */ | |
| static DWORD enumIPAddresses(PDWORD pcAddresses, struct ifconf *ifc) | |
| { | |
| DWORD ret; | |
| int fd; | |
| fd = socket(PF_INET, SOCK_DGRAM, 0); | |
| if (fd != -1) { | |
| int ioctlRet = 0; | |
| DWORD guessedNumAddresses = 0, numAddresses = 0; | |
| caddr_t ifPtr; | |
| int lastlen; | |
| ret = NO_ERROR; | |
| ifc->ifc_len = 0; | |
| ifc->ifc_buf = NULL; | |
| /* there is no way to know the interface count beforehand, | |
| so we need to loop again and again upping our max each time | |
| until returned is constant across 2 calls */ | |
| do { | |
| lastlen = ifc->ifc_len; | |
| HeapFree(GetProcessHeap(), 0, ifc->ifc_buf); | |
| if (guessedNumAddresses == 0) | |
| guessedNumAddresses = INITIAL_INTERFACES_ASSUMED; | |
| else | |
| guessedNumAddresses *= 2; | |
| ifc->ifc_len = sizeof(struct ifreq) * guessedNumAddresses; | |
| ifc->ifc_buf = HeapAlloc(GetProcessHeap(), 0, ifc->ifc_len); | |
| ioctlRet = ioctl(fd, SIOCGIFCONF, ifc); | |
| } while ((ioctlRet == 0) && (ifc->ifc_len != lastlen)); | |
| if (ioctlRet == 0) { | |
| ifPtr = ifc->ifc_buf; | |
| while (ifPtr && ifPtr < ifc->ifc_buf + ifc->ifc_len) { | |
| struct ifreq *ifr = (struct ifreq *)ifPtr; | |
| if (ifr->ifr_addr.sa_family == AF_INET) | |
| numAddresses++; | |
| ifPtr += ifreq_len((struct ifreq *)ifPtr); | |
| } | |
| } | |
| else | |
| ret = ERROR_INVALID_PARAMETER; /* FIXME: map from errno to Win32 */ | |
| if (!ret) | |
| *pcAddresses = numAddresses; | |
| else | |
| { | |
| HeapFree(GetProcessHeap(), 0, ifc->ifc_buf); | |
| ifc->ifc_buf = NULL; | |
| } | |
| close(fd); | |
| } | |
| else | |
| ret = ERROR_NO_SYSTEM_RESOURCES; | |
| return ret; | |
| } | |
| DWORD getNumIPAddresses(void) | |
| { | |
| DWORD numAddresses = 0; | |
| struct ifconf ifc; | |
| if (!enumIPAddresses(&numAddresses, &ifc)) | |
| HeapFree(GetProcessHeap(), 0, ifc.ifc_buf); | |
| return numAddresses; | |
| } | |
| DWORD getIPAddrTable(PMIB_IPADDRTABLE *ppIpAddrTable, HANDLE heap, DWORD flags) | |
| { | |
| DWORD ret; | |
| if (!ppIpAddrTable) | |
| ret = ERROR_INVALID_PARAMETER; | |
| else | |
| { | |
| DWORD numAddresses = 0; | |
| struct ifconf ifc; | |
| ret = enumIPAddresses(&numAddresses, &ifc); | |
| if (!ret) | |
| { | |
| DWORD size = sizeof(MIB_IPADDRTABLE); | |
| if (numAddresses > 1) | |
| size += (numAddresses - 1) * sizeof(MIB_IPADDRROW); | |
| *ppIpAddrTable = HeapAlloc(heap, flags, size); | |
| if (*ppIpAddrTable) { | |
| DWORD i = 0; | |
| caddr_t ifPtr; | |
| ret = NO_ERROR; | |
| (*ppIpAddrTable)->dwNumEntries = numAddresses; | |
| ifPtr = ifc.ifc_buf; | |
| while (!ret && ifPtr && ifPtr < ifc.ifc_buf + ifc.ifc_len) { | |
| struct ifreq *ifr = (struct ifreq *)ifPtr; | |
| ifPtr += ifreq_len(ifr); | |
| if (ifr->ifr_addr.sa_family != AF_INET) | |
| continue; | |
| ret = getIPAddrRowByName(&(*ppIpAddrTable)->table[i], ifr->ifr_name, | |
| &ifr->ifr_addr); | |
| i++; | |
| } | |
| if (ret) | |
| HeapFree(GetProcessHeap(), 0, *ppIpAddrTable); | |
| } | |
| else | |
| ret = ERROR_OUTOFMEMORY; | |
| HeapFree(GetProcessHeap(), 0, ifc.ifc_buf); | |
| } | |
| } | |
| return ret; | |
| } | |
| ULONG v6addressesFromIndex(IF_INDEX index, SOCKET_ADDRESS **addrs, ULONG *num_addrs, SOCKET_ADDRESS **masks) | |
| { | |
| *addrs = NULL; | |
| *num_addrs = 0; | |
| *masks = NULL; | |
| return ERROR_SUCCESS; | |
| } | |
| #endif | |
| char *toIPAddressString(unsigned int addr, char string[16]) | |
| { | |
| if (string) { | |
| struct in_addr iAddr; | |
| iAddr.s_addr = addr; | |
| /* extra-anal, just to make auditors happy */ | |
| lstrcpynA(string, inet_ntoa(iAddr), 16); | |
| } | |
| return string; | |
| } |