| // Copyright (c) 2024, 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.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 == nullptr) { |
| 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 = nullptr; |
| struct ifaddrs* cur = nullptr; |
| 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 != nullptr) { |
| 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 != nullptr) { |
| 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 = nullptr; |
| while (addrs != nullptr) { |
| 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 |