| // 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; | |
| } | |
| DART_WARN_UNUSED_RESULT static bool SetAddresses(struct ifaddrs* ifaddr, | |
| int family, | |
| int index, | |
| void* data, | |
| size_t len) { | |
| if (family == AF_INET6) { | |
| // The attribute length comes from the netlink message and is not guaranteed | |
| // to match the address family, so reject anything that doesn't fit. | |
| if (len != sizeof(sockaddr_in6::sin6_addr)) { | |
| return false; | |
| } | |
| 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 true; | |
| } | |
| ASSERT(family == AF_INET); | |
| if (len != sizeof(sockaddr_in::sin_addr)) { | |
| return false; | |
| } | |
| sockaddr_in* socketaddr = new sockaddr_in; | |
| socketaddr->sin_family = AF_INET; | |
| memmove(&socketaddr->sin_addr, data, len); | |
| ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(socketaddr); | |
| return true; | |
| } | |
| DART_WARN_UNUSED_RESULT static bool SetNetmask(struct ifaddrs* ifaddr, | |
| int family, | |
| int prefixlen) { | |
| if (family == AF_INET6) { | |
| if (prefixlen < 0 || prefixlen > 128) { | |
| return false; | |
| } | |
| sockaddr_in6* mask = new sockaddr_in6; | |
| mask->sin6_family = AF_INET6; | |
| memset(&mask->sin6_addr, 0, sizeof(mask->sin6_addr)); | |
| uint8_t* m = mask->sin6_addr.s6_addr; | |
| for (int i = 0; i < prefixlen / 8; ++i) { | |
| m[i] = 0xFF; | |
| } | |
| if (prefixlen % 8) { | |
| m[prefixlen / 8] = 0xFF << (8 - prefixlen % 8); | |
| } | |
| ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask); | |
| return true; | |
| } | |
| ASSERT(family == AF_INET); | |
| if (prefixlen < 0 || prefixlen > 32) { | |
| return false; | |
| } | |
| sockaddr_in* mask = new sockaddr_in; | |
| mask->sin_family = AF_INET; | |
| uint32_t m = prefixlen ? (~0u << (32 - prefixlen)) : 0; | |
| mask->sin_addr.s_addr = htonl(m); | |
| ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask); | |
| return true; | |
| } | |
| static bool SetIfAddrsFromAddrMsg(struct ifaddrs* ifaddr, | |
| ifaddrmsg* msg, | |
| void* bytes, | |
| size_t len, | |
| nlmsghdr* header) { | |
| if (!SetAddresses(ifaddr, msg->ifa_family, msg->ifa_index, bytes, len)) { | |
| return false; | |
| } | |
| if (!SetNetmask(ifaddr, msg->ifa_family, msg->ifa_prefixlen)) { | |
| return false; | |
| } | |
| 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) { | |
| if (!SetAddresses(ifaddr, ifi->ifi_family, ifi->ifi_index, bytes, len)) { | |
| return false; | |
| } | |
| if (!SetNetmask(ifaddr, ifi->ifi_family, 0)) { | |
| return false; | |
| } | |
| 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 |