diff --git a/.gitignore b/.gitignore index 50281a4..803bb66 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# IDEs/Editors +*.sublime-* diff --git a/Cargo.toml b/Cargo.toml new file mode 100755 index 0000000..73070f7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ifaddrs" +version = "0.1.0" + +[dependencies] +libc = "~0.2.28" +c_linked_list = "~1.1.0" + +[target.'cfg(target_os = "android")'.dependencies.ifaddrs-sys] +version = "0.1.0" +path = "ifaddrs-sys" + +[dev-dependencies] +unwrap = "~1.1.0" \ No newline at end of file diff --git a/ifaddrs-sys/Cargo.toml b/ifaddrs-sys/Cargo.toml new file mode 100755 index 0000000..f478f7c --- /dev/null +++ b/ifaddrs-sys/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ifaddrs-sys" +version = "0.1.0" +links = "ifaddrs" +build = "build.rs" + +[lib] +name = "ifaddrs_sys" +path = "lib.rs" + +[dependencies] +libc = "~0.2.28" + +[build-dependencies] +gcc = "0.3" diff --git a/ifaddrs-sys/build.rs b/ifaddrs-sys/build.rs new file mode 100755 index 0000000..5c75ab2 --- /dev/null +++ b/ifaddrs-sys/build.rs @@ -0,0 +1,12 @@ +extern crate gcc; + +use std::env; + +fn main() { + let mut cfg = gcc::Build::new(); + if env::var("TARGET").unwrap().contains("android") { + cfg.include("native").file("native/ifaddrs.c").compile( + "libifaddrs.a", + ); + } +} diff --git a/ifaddrs-sys/lib.rs b/ifaddrs-sys/lib.rs new file mode 100755 index 0000000..c8d2402 --- /dev/null +++ b/ifaddrs-sys/lib.rs @@ -0,0 +1,19 @@ +#![cfg(target_os = "android")] +extern crate libc; +use libc::*; +#[repr(C)] +#[derive(Debug)] +pub struct ifaddrs { + pub ifa_next: *mut ifaddrs, + pub ifa_name: *mut c_char, + pub ifa_flags: ::c_uint, + pub ifa_addr: *mut ::sockaddr, + pub ifa_netmask: *mut ::sockaddr, + pub ifa_ifu: *mut ::sockaddr, + pub ifa_data: *mut ::c_void, +} + +extern "C" { + pub fn getifaddrs(ifap: *mut *mut ::ifaddrs) -> ::c_int; + pub fn freeifaddrs(ifa: *mut ::ifaddrs); +} diff --git a/ifaddrs-sys/native/ifaddrs.c b/ifaddrs-sys/native/ifaddrs.c new file mode 100755 index 0000000..9d00d5b --- /dev/null +++ b/ifaddrs-sys/native/ifaddrs.c @@ -0,0 +1,660 @@ +/* +Copyright (c) 2013, Kenneth MacKay +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "ifaddrs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct NetlinkList +{ + struct NetlinkList *m_next; + struct nlmsghdr *m_data; + unsigned int m_size; +} NetlinkList; + +static int netlink_socket(void) +{ + int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if(l_socket < 0) + { + return -1; + } + + struct sockaddr_nl l_addr; + memset(&l_addr, 0, sizeof(l_addr)); + l_addr.nl_family = AF_NETLINK; + if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0) + { + close(l_socket); + return -1; + } + + return l_socket; +} + +static int netlink_send(int p_socket, int p_request) +{ + struct + { + struct nlmsghdr m_hdr; + struct rtgenmsg m_msg; + } l_data; + + memset(&l_data, 0, sizeof(l_data)); + + l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + l_data.m_hdr.nlmsg_type = p_request; + l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + l_data.m_hdr.nlmsg_pid = 0; + l_data.m_hdr.nlmsg_seq = p_socket; + l_data.m_msg.rtgen_family = AF_UNSPEC; + + struct sockaddr_nl l_addr; + memset(&l_addr, 0, sizeof(l_addr)); + l_addr.nl_family = AF_NETLINK; + return (sendto(p_socket, &l_data.m_hdr, l_data.m_hdr.nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr))); +} + +static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) +{ + struct msghdr l_msg; + struct iovec l_iov = { p_buffer, p_len }; + struct sockaddr_nl l_addr; + + for(;;) + { + l_msg.msg_name = (void *)&l_addr; + l_msg.msg_namelen = sizeof(l_addr); + l_msg.msg_iov = &l_iov; + l_msg.msg_iovlen = 1; + l_msg.msg_control = NULL; + l_msg.msg_controllen = 0; + l_msg.msg_flags = 0; + int l_result = recvmsg(p_socket, &l_msg, 0); + + if(l_result < 0) + { + if(errno == EINTR) + { + continue; + } + return -2; + } + + if(l_msg.msg_flags & MSG_TRUNC) + { // buffer was too small + return -1; + } + return l_result; + } +} + +static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done) +{ + size_t l_size = 4096; + void *l_buffer = NULL; + + for(;;) + { + free(l_buffer); + l_buffer = malloc(l_size); + if (l_buffer == NULL) + { + return NULL; + } + + int l_read = netlink_recv(p_socket, l_buffer, l_size); + *p_size = l_read; + if(l_read == -2) + { + free(l_buffer); + return NULL; + } + if(l_read >= 0) + { + struct nlmsghdr *l_hdr; + for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read)) + { + if((int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if(l_hdr->nlmsg_type == NLMSG_DONE) + { + *p_done = 1; + break; + } + + if(l_hdr->nlmsg_type == NLMSG_ERROR) + { + free(l_buffer); + return NULL; + } + } + return l_buffer; + } + + l_size *= 2; + } +} + +static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size) +{ + NetlinkList *l_item = malloc(sizeof(NetlinkList)); + if (l_item == NULL) + { + return NULL; + } + + l_item->m_next = NULL; + l_item->m_data = p_data; + l_item->m_size = p_size; + return l_item; +} + +static void freeResultList(NetlinkList *p_list) +{ + NetlinkList *l_cur; + while(p_list) + { + l_cur = p_list; + p_list = p_list->m_next; + free(l_cur->m_data); + free(l_cur); + } +} + +static NetlinkList *getResultList(int p_socket, int p_request) +{ + if(netlink_send(p_socket, p_request) < 0) + { + return NULL; + } + + NetlinkList *l_list = NULL; + NetlinkList *l_end = NULL; + int l_size; + int l_done = 0; + while(!l_done) + { + struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done); + if(!l_hdr) + { // error + freeResultList(l_list); + return NULL; + } + + NetlinkList *l_item = newListItem(l_hdr, l_size); + if (!l_item) + { + freeResultList(l_list); + return NULL; + } + if(!l_list) + { + l_list = l_item; + } + else + { + l_end->m_next = l_item; + } + l_end = l_item; + } + return l_list; +} + +static size_t maxSize(size_t a, size_t b) +{ + return (a > b ? a : b); +} + +static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) +{ + switch(p_family) + { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + case AF_PACKET: + return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); + default: + return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); + } +} + +static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size) +{ + switch(p_family) + { + case AF_INET: + memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size); + break; + case AF_INET6: + memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size); + break; + case AF_PACKET: + memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size); + ((struct sockaddr_ll*)p_dest)->sll_halen = p_size; + break; + default: + memcpy(p_dest->sa_data, p_data, p_size); + break; + } + p_dest->sa_family = p_family; +} + +static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry) +{ + if(!*p_resultList) + { + *p_resultList = p_entry; + } + else + { + struct ifaddrs *l_cur = *p_resultList; + while(l_cur->ifa_next) + { + l_cur = l_cur->ifa_next; + } + l_cur->ifa_next = p_entry; + } +} + +static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) +{ + struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr); + + size_t l_nameSize = 0; + size_t l_addrSize = 0; + size_t l_dataSize = 0; + + size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); + struct rtattr *l_rta; + for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + void *l_rtaData = RTA_DATA(l_rta); + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch(l_rta->rta_type) + { + case IFLA_ADDRESS: + case IFLA_BROADCAST: + l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize)); + break; + case IFLA_IFNAME: + l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); + break; + case IFLA_STATS: + l_dataSize += NLMSG_ALIGN(l_rtaSize); + break; + default: + break; + } + } + + struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize); + if (l_entry == NULL) + { + return -1; + } + memset(l_entry, 0, sizeof(struct ifaddrs)); + l_entry->ifa_name = ""; + + char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs); + char *l_name = l_index + sizeof(int); + char *l_addr = l_name + l_nameSize; + char *l_data = l_addr + l_addrSize; + + // save the interface index so we can look it up when handling the addresses. + memcpy(l_index, &l_info->ifi_index, sizeof(int)); + + l_entry->ifa_flags = l_info->ifi_flags; + + l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); + for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + void *l_rtaData = RTA_DATA(l_rta); + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch(l_rta->rta_type) + { + case IFLA_ADDRESS: + case IFLA_BROADCAST: + { + size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize); + makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); + ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index; + ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type; + if(l_rta->rta_type == IFLA_ADDRESS) + { + l_entry->ifa_addr = (struct sockaddr *)l_addr; + } + else + { + l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; + } + l_addr += NLMSG_ALIGN(l_addrLen); + break; + } + case IFLA_IFNAME: + strncpy(l_name, l_rtaData, l_rtaDataSize); + l_name[l_rtaDataSize] = '\0'; + l_entry->ifa_name = l_name; + break; + case IFLA_STATS: + memcpy(l_data, l_rtaData, l_rtaDataSize); + l_entry->ifa_data = l_data; + break; + default: + break; + } + } + + addToEnd(p_resultList, l_entry); + return 0; +} + +static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks) +{ + int l_num = 0; + struct ifaddrs *l_cur = *p_links; + while(l_cur && l_num < p_numLinks) + { + char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs); + int l_index; + memcpy(&l_index, l_indexPtr, sizeof(int)); + if(l_index == p_index) + { + return l_cur; + } + + l_cur = l_cur->ifa_next; + ++l_num; + } + return NULL; +} + +static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks) +{ + struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr); + struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks); + + if(l_info->ifa_family == AF_PACKET) + { + return 0; + } + + size_t l_nameSize = 0; + size_t l_addrSize = 0; + + int l_addedNetmask = 0; + + size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); + struct rtattr *l_rta; + for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + void *l_rtaData = RTA_DATA(l_rta); + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + + switch(l_rta->rta_type) + { + case IFA_ADDRESS: + case IFA_LOCAL: + if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask) + { // make room for netmask + l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + l_addedNetmask = 1; + } + case IFA_BROADCAST: + l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + break; + case IFA_LABEL: + l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); + break; + default: + break; + } + } + + struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); + if (l_entry == NULL) + { + return -1; + } + memset(l_entry, 0, sizeof(struct ifaddrs)); + l_entry->ifa_name = (l_interface ? l_interface->ifa_name : ""); + + char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs); + char *l_addr = l_name + l_nameSize; + + l_entry->ifa_flags = l_info->ifa_flags; + if(l_interface) + { + l_entry->ifa_flags |= l_interface->ifa_flags; + } + + l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); + for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + void *l_rtaData = RTA_DATA(l_rta); + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch(l_rta->rta_type) + { + case IFA_ADDRESS: + case IFA_BROADCAST: + case IFA_LOCAL: + { + size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize); + makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); + if(l_info->ifa_family == AF_INET6) + { + if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData)) + { + ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index; + } + } + + if(l_rta->rta_type == IFA_ADDRESS) + { // apparently in a point-to-point network IFA_ADDRESS contains the dest address and IFA_LOCAL contains the local address + if(l_entry->ifa_addr) + { + l_entry->ifa_dstaddr = (struct sockaddr *)l_addr; + } + else + { + l_entry->ifa_addr = (struct sockaddr *)l_addr; + } + } + else if(l_rta->rta_type == IFA_LOCAL) + { + if(l_entry->ifa_addr) + { + l_entry->ifa_dstaddr = l_entry->ifa_addr; + } + l_entry->ifa_addr = (struct sockaddr *)l_addr; + } + else + { + l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; + } + l_addr += NLMSG_ALIGN(l_addrLen); + break; + } + case IFA_LABEL: + strncpy(l_name, l_rtaData, l_rtaDataSize); + l_name[l_rtaDataSize] = '\0'; + l_entry->ifa_name = l_name; + break; + default: + break; + } + } + + if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6)) + { + unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128); + unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen); + char l_mask[16] = {0}; + unsigned i; + for(i=0; i<(l_prefix/8); ++i) + { + l_mask[i] = 0xff; + } + if(l_prefix % 8) + { + l_mask[i] = 0xff << (8 - (l_prefix % 8)); + } + + makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8); + l_entry->ifa_netmask = (struct sockaddr *)l_addr; + } + + addToEnd(p_resultList, l_entry); + return 0; +} + +static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList) +{ + int l_numLinks = 0; + for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) + { + unsigned int l_nlsize = p_netlinkList->m_size; + struct nlmsghdr *l_hdr; + for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) + { + if((int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if(l_hdr->nlmsg_type == NLMSG_DONE) + { + break; + } + + if(l_hdr->nlmsg_type == RTM_NEWLINK) + { + if(interpretLink(l_hdr, p_resultList) == -1) + { + return -1; + } + ++l_numLinks; + } + } + } + return l_numLinks; +} + +static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks) +{ + for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) + { + unsigned int l_nlsize = p_netlinkList->m_size; + struct nlmsghdr *l_hdr; + for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) + { + if((int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if(l_hdr->nlmsg_type == NLMSG_DONE) + { + break; + } + + if(l_hdr->nlmsg_type == RTM_NEWADDR) + { + if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1) + { + return -1; + } + } + } + } + return 0; +} + +int getifaddrs(struct ifaddrs **ifap) +{ + if(!ifap) + { + return -1; + } + *ifap = NULL; + + int l_socket = netlink_socket(); + if(l_socket < 0) + { + return -1; + } + + NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK); + if(!l_linkResults) + { + close(l_socket); + return -1; + } + + NetlinkList *l_addrResults = getResultList(l_socket, RTM_GETADDR); + if(!l_addrResults) + { + close(l_socket); + freeResultList(l_linkResults); + return -1; + } + + int l_result = 0; + int l_numLinks = interpretLinks(l_socket, l_linkResults, ifap); + if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) + { + l_result = -1; + } + + freeResultList(l_linkResults); + freeResultList(l_addrResults); + close(l_socket); + return l_result; +} + +void freeifaddrs(struct ifaddrs *ifa) +{ + struct ifaddrs *l_cur; + while(ifa) + { + l_cur = ifa; + ifa = ifa->ifa_next; + free(l_cur); + } +} diff --git a/ifaddrs-sys/native/ifaddrs.h b/ifaddrs-sys/native/ifaddrs.h new file mode 100755 index 0000000..9cd19fe --- /dev/null +++ b/ifaddrs-sys/native/ifaddrs.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995, 1999 + * Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp + */ + +#ifndef _IFADDRS_H_ +#define _IFADDRS_H_ + +struct ifaddrs { + struct ifaddrs *ifa_next; + char *ifa_name; + unsigned int ifa_flags; + struct sockaddr *ifa_addr; + struct sockaddr *ifa_netmask; + struct sockaddr *ifa_dstaddr; + void *ifa_data; +}; + +/* + * This may have been defined in . Note that if is + * to be included it must be included before this header file. + */ +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif + +#include + +__BEGIN_DECLS +extern int getifaddrs(struct ifaddrs **ifap); +extern void freeifaddrs(struct ifaddrs *ifa); +__END_DECLS + +#endif diff --git a/ifaddrs-sys/native/ifaddrs.o b/ifaddrs-sys/native/ifaddrs.o new file mode 100644 index 0000000..bce76c0 Binary files /dev/null and b/ifaddrs-sys/native/ifaddrs.o differ diff --git a/src/lib.rs b/src/lib.rs new file mode 100755 index 0000000..10dd786 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,689 @@ +// Copyright 2016 MaidSafe.net limited. +// +// This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, +// version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which +// licence you accepted on initial access to the Software (the "Licences"). +// +// By contributing code to the SAFE Network Software, or to this project generally, you agree to be +// bound by the terms of the MaidSafe Contributor Agreement. This, along with the Licenses can be +// found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. +// +// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed +// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. +// +// Please review the Licences for the specific language governing permissions and limitations +// relating to use of the SAFE Network Software. + +use std::io; +use std::net::{Ipv4Addr, Ipv6Addr}; +#[cfg(test)] +use std::net::IpAddr; +#[cfg(test)] +#[macro_use] +extern crate unwrap; +extern crate libc; +extern crate c_linked_list; +#[cfg(target_os = "android")] +extern crate ifaddrs_sys; + +/// Details about an interface on this host +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct Interface { + /// The name of the interface. + pub name: String, + /// The address details of the interface. + pub addr: IfAddr, +} + +/// Details about the address of an interface on this host +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub enum IfAddr { + /// This is an Ipv4 interface. + V4(Ifv4Addr), + /// This is an Ipv6 interface. + V6(Ifv6Addr), +} + +/// Details about the ipv4 address of an interface on this host +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct Ifv4Addr { + /// The IP address of the interface. + pub ip: Ipv4Addr, + /// The netmask of the interface. + pub netmask: Ipv4Addr, + /// The broadcast address of the interface. + pub broadcast: Option, +} + +/// Details about the ipv6 address of an interface on this host +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct Ifv6Addr { + /// The IP address of the interface. + pub ip: Ipv6Addr, + /// The netmask of the interface. + pub netmask: Ipv6Addr, + /// The broadcast address of the interface. + pub broadcast: Option, +} + +impl Interface { + /// Check whether this is a loopback interface. + #[cfg(test)] + pub fn is_loopback(&self) -> bool { + self.addr.is_loopback() + } +} + +impl IfAddr { + /// Check whether this is a loopback address. + #[cfg(test)] + pub fn is_loopback(&self) -> bool { + match *self { + IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_loopback(), + IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_loopback(), + } + } + + /// Get the IP address of this interface address. + #[cfg(test)] + pub fn ip(&self) -> IpAddr { + match *self { + IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip), + IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip), + } + } +} + +impl Ifv4Addr { + /// Check whether this is a loopback address. + #[cfg(test)] + pub fn is_loopback(&self) -> bool { + self.ip.octets()[0] == 127 + } +} + +impl Ifv6Addr { + /// Check whether this is a loopback address. + #[cfg(test)] + pub fn is_loopback(&self) -> bool { + self.ip.segments() == [0, 0, 0, 0, 0, 0, 0, 1] + } +} + +#[cfg(not(windows))] +mod getifaddrs_posix { + use c_linked_list::CLinkedListMut; + use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface}; + use libc::{AF_INET, AF_INET6}; + #[cfg(target_os = "android")] + use ifaddrs_sys::ifaddrs as posix_ifaddrs; + #[cfg(not(target_os = "android"))] + use libc::ifaddrs as posix_ifaddrs; + #[cfg(target_os = "android")] + use ifaddrs_sys::getifaddrs as posix_getifaddrs; + #[cfg(not(target_os = "android"))] + use libc::getifaddrs as posix_getifaddrs; + #[cfg(target_os = "android")] + use ifaddrs_sys::freeifaddrs as posix_freeifaddrs; + #[cfg(not(target_os = "android"))] + use libc::freeifaddrs as posix_freeifaddrs; + use libc::sockaddr as posix_sockaddr; + use libc::sockaddr_in as posix_sockaddr_in; + use libc::sockaddr_in6 as posix_sockaddr_in6; + use std::{io, mem}; + use std::ffi::CStr; + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + + #[allow(unsafe_code)] + fn sockaddr_to_ipaddr(sockaddr: *const posix_sockaddr) -> Option { + if sockaddr.is_null() { + return None; + } + if unsafe { *sockaddr }.sa_family as u32 == AF_INET as u32 { + let sa = &unsafe { *(sockaddr as *const posix_sockaddr_in) }; + Some(IpAddr::V4(Ipv4Addr::new( + ((sa.sin_addr.s_addr) & 255) as u8, + ((sa.sin_addr.s_addr >> 8) & 255) as u8, + ((sa.sin_addr.s_addr >> 16) & 255) as u8, + ((sa.sin_addr.s_addr >> 24) & 255) as u8, + ))) + } else if unsafe { *sockaddr }.sa_family as u32 == AF_INET6 as u32 { + let sa = &unsafe { *(sockaddr as *const posix_sockaddr_in6) }; + // Ignore all fe80:: addresses as these are link locals + if sa.sin6_addr.s6_addr[0] == 0xfe && sa.sin6_addr.s6_addr[1] == 0x80 { + return None; + } + Some(IpAddr::V6(Ipv6Addr::new( + ((sa.sin6_addr.s6_addr[0] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[1] as u16, + ((sa.sin6_addr.s6_addr[2] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[3] as u16, + ((sa.sin6_addr.s6_addr[4] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[5] as u16, + ((sa.sin6_addr.s6_addr[6] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[7] as u16, + ((sa.sin6_addr.s6_addr[8] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[9] as u16, + ((sa.sin6_addr.s6_addr[10] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[11] as u16, + ((sa.sin6_addr.s6_addr[12] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[13] as u16, + ((sa.sin6_addr.s6_addr[14] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[15] as u16, + ))) + } else { + None + } + } + + #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))] + fn do_broadcast(ifaddr: &posix_ifaddrs) -> Option { + sockaddr_to_ipaddr(ifaddr.ifa_ifu) + } + + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos", + target_os = "openbsd"))] + fn do_broadcast(ifaddr: &posix_ifaddrs) -> Option { + sockaddr_to_ipaddr(ifaddr.ifa_dstaddr) + } + + /// Return a vector of IP details for all the valid interfaces on this host + #[allow(unsafe_code)] + #[allow(trivial_casts)] + pub fn get_if_addrs() -> io::Result> { + let mut ret = Vec::::new(); + let mut ifaddrs: *mut posix_ifaddrs; + unsafe { + ifaddrs = mem::uninitialized(); + if -1 == posix_getifaddrs(&mut ifaddrs) { + return Err(io::Error::last_os_error()); + } + } + + for ifaddr in unsafe { CLinkedListMut::from_ptr(ifaddrs, |a| a.ifa_next) }.iter() { + if ifaddr.ifa_addr.is_null() { + continue; + } + let name = unsafe { CStr::from_ptr(ifaddr.ifa_name as *const _) } + .to_string_lossy() + .into_owned(); + let addr = match sockaddr_to_ipaddr(ifaddr.ifa_addr) { + None => continue, + Some(IpAddr::V4(ipv4_addr)) => { + let netmask = match sockaddr_to_ipaddr(ifaddr.ifa_netmask) { + Some(IpAddr::V4(netmask)) => netmask, + _ => Ipv4Addr::new(0, 0, 0, 0), + }; + let broadcast = if (ifaddr.ifa_flags & 2) != 0 { + match do_broadcast(ifaddr) { + Some(IpAddr::V4(broadcast)) => Some(broadcast), + _ => None, + } + } else { + None + }; + IfAddr::V4(Ifv4Addr { + ip: ipv4_addr, + netmask: netmask, + broadcast: broadcast, + }) + } + Some(IpAddr::V6(ipv6_addr)) => { + let netmask = match sockaddr_to_ipaddr(ifaddr.ifa_netmask) { + Some(IpAddr::V6(netmask)) => netmask, + _ => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), + }; + let broadcast = if (ifaddr.ifa_flags & 2) != 0 { + match do_broadcast(ifaddr) { + Some(IpAddr::V6(broadcast)) => Some(broadcast), + _ => None, + } + } else { + None + }; + IfAddr::V6(Ifv6Addr { + ip: ipv6_addr, + netmask: netmask, + broadcast: broadcast, + }) + } + }; + ret.push(Interface { + name: name, + addr: addr, + }); + } + unsafe { + posix_freeifaddrs(ifaddrs); + } + Ok(ret) + } +} + +/// Get a list of all the network interfaces on this machine along with their IP info. +#[cfg(not(windows))] +pub fn get_if_addrs() -> io::Result> { + getifaddrs_posix::get_if_addrs() +} + +#[cfg(windows)] +mod getifaddrs_windows { + + use c_linked_list::CLinkedListConst; + use common::get_if_addrs::{IfAddr, Ifv4Addr, Ifv6Addr, Interface}; + use libc; + use libc::{c_char, c_int, c_ulong, c_void, size_t}; + use std::{io, ptr}; + use std::ffi::CStr; + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + use winapi::{AF_INET, AF_INET6, DWORD, ERROR_SUCCESS, sockaddr_in6}; + use winapi::SOCKADDR as sockaddr; + use winapi::SOCKADDR_IN as sockaddr_in; + + #[repr(C)] + struct SocketAddress { + pub lp_socket_address: *const sockaddr, + pub i_socket_address_length: c_int, + } + #[repr(C)] + struct IpAdapterUnicastAddress { + pub length: c_ulong, + pub flags: DWORD, + pub next: *const IpAdapterUnicastAddress, + // Loads more follows, but I'm not bothering to map these for now + pub address: SocketAddress, + } + #[repr(C)] + struct IpAdapterPrefix { + pub length: c_ulong, + pub flags: DWORD, + pub next: *const IpAdapterPrefix, + pub address: SocketAddress, + pub prefix_length: c_ulong, + } + #[repr(C)] + struct IpAdapterAddresses { + pub length: c_ulong, + pub if_index: DWORD, + pub next: *const IpAdapterAddresses, + pub adapter_name: *const c_char, + pub first_unicast_address: *const IpAdapterUnicastAddress, + first_anycast_address: *const c_void, + first_multicast_address: *const c_void, + first_dns_server_address: *const c_void, + dns_suffix: *const c_void, + description: *const c_void, + friendly_name: *const c_void, + physical_address: [c_char; 8], + physical_address_length: DWORD, + flags: DWORD, + mtu: DWORD, + if_type: DWORD, + oper_status: c_int, + ipv6_if_index: DWORD, + zone_indices: [DWORD; 16], + // Loads more follows, but I'm not bothering to map these for now + pub first_prefix: *const IpAdapterPrefix, + } + #[link(name = "Iphlpapi")] + extern "system" { + /// get adapter's addresses + fn GetAdaptersAddresses( + family: c_ulong, + flags: c_ulong, + reserved: *const c_void, + addresses: *const IpAdapterAddresses, + size: *mut c_ulong, + ) -> c_ulong; + } + + #[allow(unsafe_code)] + fn sockaddr_to_ipaddr(sockaddr: *const sockaddr) -> Option { + if sockaddr.is_null() { + return None; + } + if unsafe { *sockaddr }.sa_family as u32 == AF_INET as u32 { + let sa = &unsafe { *(sockaddr as *const sockaddr_in) }; + // Ignore all 169.254.x.x addresses as these are not active interfaces + if sa.sin_addr.S_un & 65535 == 0xfea9 { + return None; + } + Some(IpAddr::V4(Ipv4Addr::new( + ((sa.sin_addr.S_un) & 255) as u8, + ((sa.sin_addr.S_un >> 8) & 255) as u8, + ((sa.sin_addr.S_un >> 16) & 255) as u8, + ((sa.sin_addr.S_un >> 24) & 255) as u8, + ))) + } else if unsafe { *sockaddr }.sa_family as u32 == AF_INET6 as u32 { + let sa = &unsafe { *(sockaddr as *const sockaddr_in6) }; + // Ignore all fe80:: addresses as these are link locals + if sa.sin6_addr.s6_addr[0] == 0xfe && sa.sin6_addr.s6_addr[1] == 0x80 { + return None; + } + Some(IpAddr::V6(Ipv6Addr::new( + ((sa.sin6_addr.s6_addr[0] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[1] as u16, + ((sa.sin6_addr.s6_addr[2] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[3] as u16, + ((sa.sin6_addr.s6_addr[4] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[5] as u16, + ((sa.sin6_addr.s6_addr[6] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[7] as u16, + ((sa.sin6_addr.s6_addr[8] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[9] as u16, + ((sa.sin6_addr.s6_addr[10] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[11] as u16, + ((sa.sin6_addr.s6_addr[12] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[13] as u16, + ((sa.sin6_addr.s6_addr[14] as u16 & 255) << 8) | + sa.sin6_addr.s6_addr[15] as u16, + ))) + } else { + None + } + } + + // trivial_numeric_casts lint may become allow by default. + // Refer: https://github.com/rust-lang/rfcs/issues/1020 + /// Return a vector of IP details for all the valid interfaces on this host + #[allow(unsafe_code, trivial_numeric_casts)] + pub fn get_if_addrs() -> io::Result> { + let mut ret = Vec::::new(); + let mut ifaddrs: *const IpAdapterAddresses; + let mut buffersize: c_ulong = 15000; + loop { + unsafe { + ifaddrs = libc::malloc(buffersize as size_t) as *mut IpAdapterAddresses; + if ifaddrs.is_null() { + panic!("Failed to allocate buffer in get_if_addrs()"); + } + let retcode = GetAdaptersAddresses( + 0, + // GAA_FLAG_SKIP_ANYCAST | + // GAA_FLAG_SKIP_MULTICAST | + // GAA_FLAG_SKIP_DNS_SERVER | + // GAA_FLAG_INCLUDE_PREFIX | + // GAA_FLAG_SKIP_FRIENDLY_NAME + 0x3e, + ptr::null(), + ifaddrs, + &mut buffersize, + ); + match retcode { + ERROR_SUCCESS => break, + 111 => { + libc::free(ifaddrs as *mut c_void); + buffersize *= 2; + continue; + } + _ => return Err(io::Error::last_os_error()), + } + } + } + + for ifaddr in unsafe { CLinkedListConst::from_ptr(ifaddrs, |a| a.next) }.iter() { + for addr in unsafe { + CLinkedListConst::from_ptr(ifaddr.first_unicast_address, |a| a.next) + }.iter() + { + let name = unsafe { CStr::from_ptr(ifaddr.adapter_name) } + .to_string_lossy() + .into_owned(); + + let addr = match sockaddr_to_ipaddr(addr.address.lp_socket_address) { + None => continue, + Some(IpAddr::V4(ipv4_addr)) => { + let mut item_netmask = Ipv4Addr::new(0, 0, 0, 0); + let mut item_broadcast = None; + // Search prefixes for a prefix matching addr + 'prefixloopv4: for prefix in unsafe { + CLinkedListConst::from_ptr(ifaddr.first_prefix, |p| p.next) + }.iter() + { + let ipprefix = sockaddr_to_ipaddr(prefix.address.lp_socket_address); + match ipprefix { + Some(IpAddr::V4(ref a)) => { + let mut netmask: [u8; 4] = [0; 4]; + for (n, netmask_elt) in netmask.iter_mut().enumerate().take( + (prefix.prefix_length as usize + 7) / + 8, + ) + { + let x_byte = ipv4_addr.octets()[n]; + let y_byte = a.octets()[n]; + // Clippy 0.0.128 doesn't handle the label on the `continue` + #[cfg_attr(feature = "cargo-clippy", + allow(needless_continue))] + for m in 0..8 { + if (n * 8) + m > prefix.prefix_length as usize { + break; + } + let bit = 1 << m; + if (x_byte & bit) == (y_byte & bit) { + *netmask_elt |= bit; + } else { + continue 'prefixloopv4; + } + } + } + item_netmask = Ipv4Addr::new( + netmask[0], + netmask[1], + netmask[2], + netmask[3], + ); + let mut broadcast: [u8; 4] = ipv4_addr.octets(); + for n in 0..4 { + broadcast[n] |= !netmask[n]; + } + item_broadcast = Some(Ipv4Addr::new( + broadcast[0], + broadcast[1], + broadcast[2], + broadcast[3], + )); + break 'prefixloopv4; + } + _ => continue, + }; + } + IfAddr::V4(Ifv4Addr { + ip: ipv4_addr, + netmask: item_netmask, + broadcast: item_broadcast, + }) + } + Some(IpAddr::V6(ipv6_addr)) => { + let mut item_netmask = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); + // Search prefixes for a prefix matching addr + 'prefixloopv6: for prefix in unsafe { + CLinkedListConst::from_ptr(ifaddr.first_prefix, |p| p.next) + }.iter() + { + let ipprefix = sockaddr_to_ipaddr(prefix.address.lp_socket_address); + match ipprefix { + Some(IpAddr::V6(ref a)) => { + // Iterate the bits in the prefix, if they all match this prefix + // is the right one, else try the next prefix + let mut netmask: [u16; 8] = [0; 8]; + for (n, netmask_elt) in netmask.iter_mut().enumerate().take( + (prefix.prefix_length as usize + 15) / + 16, + ) + { + let x_word = ipv6_addr.segments()[n]; + let y_word = a.segments()[n]; + // Clippy 0.0.128 doesn't handle the label on the `continue` + #[cfg_attr(feature = "cargo-clippy", + allow(needless_continue))] + for m in 0..16 { + if (n * 16) + m > prefix.prefix_length as usize { + break; + } + let bit = 1 << m; + if (x_word & bit) == (y_word & bit) { + *netmask_elt |= bit; + } else { + continue 'prefixloopv6; + } + } + } + item_netmask = Ipv6Addr::new( + netmask[0], + netmask[1], + netmask[2], + netmask[3], + netmask[4], + netmask[5], + netmask[6], + netmask[7], + ); + break 'prefixloopv6; + } + _ => continue, + }; + } + IfAddr::V6(Ifv6Addr { + ip: ipv6_addr, + netmask: item_netmask, + broadcast: None, + }) + } + }; + ret.push(Interface { + name: name, + addr: addr, + }); + } + } + unsafe { + libc::free(ifaddrs as *mut c_void); + } + Ok(ret) + } +} + +#[cfg(windows)] +/// Get address +pub fn get_if_addrs() -> io::Result> { + getifaddrs_windows::get_if_addrs() +} + +#[cfg(test)] +mod tests { + use super::{Interface, get_if_addrs}; + use std::error::Error; + use std::io::Read; + use std::net::{IpAddr, Ipv4Addr}; + use std::process::{Command, Stdio}; + use std::str::FromStr; + use std::thread; + use std::time::Duration; + + fn list_system_interfaces(cmd: &str, arg: &str) -> String { + let start_cmd = if arg == "" { + Command::new(cmd).stdout(Stdio::piped()).spawn() + } else { + Command::new(cmd).arg(arg).stdout(Stdio::piped()).spawn() + }; + let mut process = match start_cmd { + Err(why) => { + println!("couldn't start cmd {} : {}", cmd, why.description()); + return "".to_string(); + } + Ok(process) => process, + }; + thread::sleep(Duration::from_millis(1000)); + let _ = process.kill(); + let result: Vec = unwrap!(process.stdout) + .bytes() + .map(|x| unwrap!(x)) + .collect(); + unwrap!(String::from_utf8(result)) + } + + #[cfg(windows)] + fn list_system_addrs() -> Vec { + use std::net::Ipv6Addr; + list_system_interfaces("ipconfig", "") + .lines() + .filter_map(|line| { + println!("{}", line); + if line.contains("Address") && !line.contains("Link-local") { + let addr_s: Vec<&str> = line.split(" : ").collect(); + if line.contains("IPv6") { + return Some(IpAddr::V6(unwrap!(Ipv6Addr::from_str(addr_s[1])))); + } else if line.contains("IPv4") { + return Some(IpAddr::V4(unwrap!(Ipv4Addr::from_str(addr_s[1])))); + } + } + None + }) + .collect() + } + + #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))] + fn list_system_addrs() -> Vec { + list_system_interfaces("ip", "addr") + .lines() + .filter_map(|line| { + println!("{}", line); + if line.contains("inet ") { + let addr_s: Vec<&str> = line.split_whitespace().collect(); + let addr: Vec<&str> = addr_s[1].split('/').collect(); + return Some(IpAddr::V4(unwrap!(Ipv4Addr::from_str(addr[0])))); + } + None + }) + .collect() + } + + #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))] + fn list_system_addrs() -> Vec { + list_system_interfaces("ifconfig", "") + .lines() + .filter_map(|line| { + println!("{}", line); + if line.contains("inet ") { + let addr_s: Vec<&str> = line.split_whitespace().collect(); + return Some(IpAddr::V4(unwrap!(Ipv4Addr::from_str(addr_s[1])))); + } + None + }) + .collect() + } + + #[test] + fn test_get_if_addrs() { + let ifaces = unwrap!(get_if_addrs()); + println!("Local interfaces:"); + println!("{:#?}", ifaces); + // at least one loop back address + assert!( + 1 <= + ifaces + .iter() + .filter(|interface| interface.is_loopback()) + .count() + ); + // one address of IpV4(127.0.0.1) + let is_loopback = + |interface: &&Interface| interface.addr.ip() == IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); + assert_eq!(1, ifaces.iter().filter(is_loopback).count()); + + // each system address shall be listed + let system_addrs = list_system_addrs(); + assert!(system_addrs.len() >= 1); + for addr in system_addrs { + let mut listed = false; + println!("\n checking whether {:?} has been properly listed \n", addr); + for interface in &ifaces { + if interface.addr.ip() == addr { + listed = true; + } + } + assert!(listed); + } + } +}