dart-sdk/runtime/bin/ifaddrs-android.cc
Zichang Guo 78340fdf82 [vm] Support ListInterfaces for android
Support ListInterfaces() for android. If android api level during build time
is greater than or equal to 24, it will use getifaddrs() from android ndk.
Since it didn't support until 24. Otherwise, use system call to query the
network interfaces.

Bug: https://github.com/dart-lang/sdk/issues/36449
Change-Id: I615b31b804b751382b775ad9c6af4411b123d5ed
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98949
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: Zichang Guo <zichangguo@google.com>
2019-04-19 16:38:44 +00:00

233 lines
7.1 KiB
C++

// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#if defined(ANDROID) && __ANDROID_API__ < 24
#include "bin/ifaddrs-android.h"
#include <errno.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "bin/fdutils.h"
#include "platform/signal_blocker.h"
namespace dart {
namespace bin {
const int kMaxReadSize = 2048;
static bool SetIfName(struct ifaddrs* ifaddr, int interface) {
char buf[IFNAMSIZ] = {0};
char* name = if_indextoname(interface, buf);
if (name == NULL) {
return false;
}
ifaddr->ifa_name = new char[strlen(name) + 1];
strncpy(ifaddr->ifa_name, name, strlen(name) + 1);
return true;
}
static void SetFlags(struct ifaddrs* ifaddr, int flag) {
ifaddr->ifa_flags = flag;
}
static void SetAddresses(struct ifaddrs* ifaddr,
int family,
int index,
void* data,
size_t len) {
if (family == AF_INET6) {
sockaddr_in6* socketaddr = new sockaddr_in6;
socketaddr->sin6_family = AF_INET6;
socketaddr->sin6_scope_id = index;
memmove(&socketaddr->sin6_addr, data, len);
ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(socketaddr);
return;
}
ASSERT(family == AF_INET);
sockaddr_in* socketaddr = new sockaddr_in;
socketaddr->sin_family = AF_INET;
memmove(&socketaddr->sin_addr, data, len);
ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(socketaddr);
}
static void SetNetmask(struct ifaddrs* ifaddr, int family) {
if (family == AF_INET6) {
sockaddr_in6* mask = new sockaddr_in6;
mask->sin6_family = AF_INET6;
memset(&mask->sin6_addr, 0, sizeof(mask->sin6_addr));
ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
return;
}
ASSERT(family == AF_INET);
sockaddr_in* mask = new sockaddr_in;
mask->sin_family = AF_INET;
memset(&mask->sin_addr, 0, sizeof(mask->sin_addr));
ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
}
static bool SetIfAddrsFromAddrMsg(struct ifaddrs* ifaddr,
ifaddrmsg* msg,
void* bytes,
size_t len,
nlmsghdr* header) {
SetAddresses(ifaddr, msg->ifa_family, msg->ifa_index, bytes, len);
SetNetmask(ifaddr, msg->ifa_family);
SetFlags(ifaddr, msg->ifa_flags);
return SetIfName(ifaddr, msg->ifa_index);
}
static bool SetIfAddrsFromInfoMsg(struct ifaddrs* ifaddr,
ifinfomsg* ifi,
void* bytes,
size_t len,
nlmsghdr* header) {
SetAddresses(ifaddr, ifi->ifi_family, ifi->ifi_index, bytes, len);
SetNetmask(ifaddr, ifi->ifi_family);
SetFlags(ifaddr, ifi->ifi_flags);
return SetIfName(ifaddr, ifi->ifi_index);
}
static int SendRequest() {
int file_descriptor =
NO_RETRY_EXPECTED(socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
if (file_descriptor < 0) {
return -1;
}
nlmsghdr header;
memset(&header, 0, sizeof(header));
header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
header.nlmsg_type = RTM_GETADDR;
header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg));
ssize_t num =
TEMP_FAILURE_RETRY(send(file_descriptor, &header, header.nlmsg_len, 0));
if (static_cast<size_t>(num) != header.nlmsg_len) {
FDUtils::SaveErrorAndClose(file_descriptor);
return -1;
}
return file_descriptor;
}
static int FailAndExit(int fd, ifaddrs* head) {
FDUtils::SaveErrorAndClose(fd);
freeifaddrs(head);
return -1;
}
int getifaddrs(struct ifaddrs** result) {
int file_descriptor = SendRequest();
if (file_descriptor < 0) {
return -1;
}
struct ifaddrs* head = NULL;
struct ifaddrs* cur = NULL;
char buf[kMaxReadSize];
ssize_t amount_read;
while (true) {
amount_read =
TEMP_FAILURE_RETRY(recv(file_descriptor, &buf, kMaxReadSize, 0));
if (amount_read <= 0) {
break;
}
nlmsghdr* header = reinterpret_cast<nlmsghdr*>(&buf[0]);
size_t header_size = static_cast<size_t>(amount_read);
for (; NLMSG_OK(header, header_size);
header = NLMSG_NEXT(header, header_size)) {
switch (header->nlmsg_type) {
case RTM_NEWADDR: {
ifaddrmsg* address_msg =
reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(header));
ssize_t payload_len = IFA_PAYLOAD(header);
for (rtattr* rta = IFA_RTA(address_msg); RTA_OK(rta, payload_len);
rta = RTA_NEXT(rta, payload_len)) {
if (rta->rta_type != IFA_ADDRESS) {
continue;
}
int family = address_msg->ifa_family;
if (family != AF_INET && family != AF_INET6) {
continue;
}
ifaddrs* next = new ifaddrs;
memset(next, 0, sizeof(*next));
if (cur != NULL) {
cur->ifa_next = next;
} else {
head = next;
}
if (!SetIfAddrsFromAddrMsg(next, address_msg, RTA_DATA(rta),
RTA_PAYLOAD(rta), header)) {
return FailAndExit(file_descriptor, head);
}
cur = next;
}
break;
}
case RTM_NEWLINK: {
ifinfomsg* ifi = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(header));
ssize_t payload_len = IFLA_PAYLOAD(header);
for (rtattr* rta = IFLA_RTA(ifi); RTA_OK(rta, payload_len);
rta = RTA_NEXT(rta, payload_len)) {
if (rta->rta_type != IFA_ADDRESS) {
continue;
}
int family = ifi->ifi_family;
if (family != AF_INET && family != AF_INET6) {
continue;
}
ifaddrs* next = new ifaddrs;
memset(next, 0, sizeof(*next));
if (cur != NULL) {
cur->ifa_next = next;
} else {
head = next;
}
if (!SetIfAddrsFromInfoMsg(next, ifi, RTA_DATA(rta),
RTA_PAYLOAD(rta), header)) {
return FailAndExit(file_descriptor, head);
}
cur = next;
}
break;
}
case NLMSG_DONE:
*result = head;
FDUtils::SaveErrorAndClose(file_descriptor);
return 0;
case NLMSG_ERROR:
return FailAndExit(file_descriptor, head);
}
}
}
return FailAndExit(file_descriptor, head);
}
void freeifaddrs(struct ifaddrs* addrs) {
int err = errno;
struct ifaddrs* previous = NULL;
while (addrs != NULL) {
delete[] addrs->ifa_name;
delete addrs->ifa_addr;
delete addrs->ifa_netmask;
previous = addrs;
addrs = addrs->ifa_next;
delete previous;
}
errno = err;
}
} // namespace bin
} // namespace dart
#endif // defined(ANDROID) && __ANDROID_API__ < 24