Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* |
michael@0 | 4 | * Copyright (C) 2009 The Android Open Source Project |
michael@0 | 5 | * |
michael@0 | 6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
michael@0 | 7 | * you may not use this file except in compliance with the License. |
michael@0 | 8 | * You may obtain a copy of the License at |
michael@0 | 9 | * |
michael@0 | 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
michael@0 | 11 | * |
michael@0 | 12 | * Unless required by applicable law or agreed to in writing, software |
michael@0 | 13 | * distributed under the License is distributed on an "AS IS" BASIS, |
michael@0 | 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
michael@0 | 15 | * See the License for the specific language governing permissions and |
michael@0 | 16 | * limitations under the License. |
michael@0 | 17 | */ |
michael@0 | 18 | |
michael@0 | 19 | #include "ifaddrs-android-ext.h" |
michael@0 | 20 | |
michael@0 | 21 | #include <stdlib.h> |
michael@0 | 22 | #include <string.h> |
michael@0 | 23 | #include "ScopedFd.h" |
michael@0 | 24 | #include "LocalArray.h" |
michael@0 | 25 | |
michael@0 | 26 | // Returns a pointer to the first byte in the address data (which is |
michael@0 | 27 | // stored in network byte order). |
michael@0 | 28 | uint8_t* sockaddrBytes(int family, sockaddr_storage* ss) { |
michael@0 | 29 | if (family == AF_INET) { |
michael@0 | 30 | sockaddr_in* ss4 = reinterpret_cast<sockaddr_in*>(ss); |
michael@0 | 31 | return reinterpret_cast<uint8_t*>(&ss4->sin_addr); |
michael@0 | 32 | } else if (family == AF_INET6) { |
michael@0 | 33 | sockaddr_in6* ss6 = reinterpret_cast<sockaddr_in6*>(ss); |
michael@0 | 34 | return reinterpret_cast<uint8_t*>(&ss6->sin6_addr); |
michael@0 | 35 | } |
michael@0 | 36 | return NULL; |
michael@0 | 37 | } |
michael@0 | 38 | |
michael@0 | 39 | // Sadly, we can't keep the interface index for portability with BSD. |
michael@0 | 40 | // We'll have to keep the name instead, and re-query the index when |
michael@0 | 41 | // we need it later. |
michael@0 | 42 | bool ifa_setNameAndFlagsByIndex(ifaddrs *self, int interfaceIndex) { |
michael@0 | 43 | // Get the name. |
michael@0 | 44 | char buf[IFNAMSIZ]; |
michael@0 | 45 | char* name = if_indextoname(interfaceIndex, buf); |
michael@0 | 46 | if (name == NULL) { |
michael@0 | 47 | return false; |
michael@0 | 48 | } |
michael@0 | 49 | self->ifa_name = new char[strlen(name) + 1]; |
michael@0 | 50 | strcpy(self->ifa_name, name); |
michael@0 | 51 | |
michael@0 | 52 | // Get the flags. |
michael@0 | 53 | ScopedFd fd(socket(AF_INET, SOCK_DGRAM, 0)); |
michael@0 | 54 | if (fd.get() == -1) { |
michael@0 | 55 | return false; |
michael@0 | 56 | } |
michael@0 | 57 | ifreq ifr; |
michael@0 | 58 | memset(&ifr, 0, sizeof(ifr)); |
michael@0 | 59 | strcpy(ifr.ifr_name, name); |
michael@0 | 60 | int rc = ioctl(fd.get(), SIOCGIFFLAGS, &ifr); |
michael@0 | 61 | if (rc == -1) { |
michael@0 | 62 | return false; |
michael@0 | 63 | } |
michael@0 | 64 | self->ifa_flags = ifr.ifr_flags; |
michael@0 | 65 | return true; |
michael@0 | 66 | } |
michael@0 | 67 | |
michael@0 | 68 | // Netlink gives us the address family in the header, and the |
michael@0 | 69 | // sockaddr_in or sockaddr_in6 bytes as the payload. We need to |
michael@0 | 70 | // stitch the two bits together into the sockaddr that's part of |
michael@0 | 71 | // our portable interface. |
michael@0 | 72 | void ifa_setAddress(ifaddrs *self, int family, void* data, size_t byteCount) { |
michael@0 | 73 | // Set the address proper... |
michael@0 | 74 | sockaddr_storage* ss = new sockaddr_storage; |
michael@0 | 75 | memset(ss, 0, sizeof(*ss)); |
michael@0 | 76 | self->ifa_addr = reinterpret_cast<sockaddr*>(ss); |
michael@0 | 77 | ss->ss_family = family; |
michael@0 | 78 | uint8_t* dst = sockaddrBytes(family, ss); |
michael@0 | 79 | memcpy(dst, data, byteCount); |
michael@0 | 80 | } |
michael@0 | 81 | |
michael@0 | 82 | // Netlink gives us the prefix length as a bit count. We need to turn |
michael@0 | 83 | // that into a BSD-compatible netmask represented by a sockaddr*. |
michael@0 | 84 | void ifa_setNetmask(ifaddrs *self, int family, size_t prefixLength) { |
michael@0 | 85 | // ...and work out the netmask from the prefix length. |
michael@0 | 86 | sockaddr_storage* ss = new sockaddr_storage; |
michael@0 | 87 | memset(ss, 0, sizeof(*ss)); |
michael@0 | 88 | self->ifa_netmask = reinterpret_cast<sockaddr*>(ss); |
michael@0 | 89 | ss->ss_family = family; |
michael@0 | 90 | uint8_t* dst = sockaddrBytes(family, ss); |
michael@0 | 91 | memset(dst, 0xff, prefixLength / 8); |
michael@0 | 92 | if ((prefixLength % 8) != 0) { |
michael@0 | 93 | dst[prefixLength/8] = (0xff << (8 - (prefixLength % 8))); |
michael@0 | 94 | } |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | // FIXME: use iovec instead. |
michael@0 | 98 | struct addrReq_struct { |
michael@0 | 99 | nlmsghdr netlinkHeader; |
michael@0 | 100 | ifaddrmsg msg; |
michael@0 | 101 | }; |
michael@0 | 102 | |
michael@0 | 103 | inline bool sendNetlinkMessage(int fd, const void* data, size_t byteCount) { |
michael@0 | 104 | ssize_t sentByteCount = TEMP_FAILURE_RETRY(send(fd, data, byteCount, 0)); |
michael@0 | 105 | return (sentByteCount == static_cast<ssize_t>(byteCount)); |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | inline ssize_t recvNetlinkMessage(int fd, char* buf, size_t byteCount) { |
michael@0 | 109 | return TEMP_FAILURE_RETRY(recv(fd, buf, byteCount, 0)); |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | // Source-compatible with the BSD function. |
michael@0 | 113 | int getifaddrs(ifaddrs** result) |
michael@0 | 114 | { |
michael@0 | 115 | // Simplify cleanup for callers. |
michael@0 | 116 | *result = NULL; |
michael@0 | 117 | |
michael@0 | 118 | // Create a netlink socket. |
michael@0 | 119 | ScopedFd fd(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)); |
michael@0 | 120 | if (fd.get() < 0) { |
michael@0 | 121 | return -1; |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | // Ask for the address information. |
michael@0 | 125 | addrReq_struct addrRequest; |
michael@0 | 126 | memset(&addrRequest, 0, sizeof(addrRequest)); |
michael@0 | 127 | addrRequest.netlinkHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH; |
michael@0 | 128 | addrRequest.netlinkHeader.nlmsg_type = RTM_GETADDR; |
michael@0 | 129 | addrRequest.netlinkHeader.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(addrRequest))); |
michael@0 | 130 | addrRequest.msg.ifa_family = AF_UNSPEC; // All families. |
michael@0 | 131 | addrRequest.msg.ifa_index = 0; // All interfaces. |
michael@0 | 132 | if (!sendNetlinkMessage(fd.get(), &addrRequest, addrRequest.netlinkHeader.nlmsg_len)) { |
michael@0 | 133 | return -1; |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | // Read the responses. |
michael@0 | 137 | LocalArray<0> buf(65536); // We don't necessarily have std::vector. |
michael@0 | 138 | ssize_t bytesRead; |
michael@0 | 139 | while ((bytesRead = recvNetlinkMessage(fd.get(), &buf[0], buf.size())) > 0) { |
michael@0 | 140 | nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(&buf[0]); |
michael@0 | 141 | for (; NLMSG_OK(hdr, (size_t)bytesRead); hdr = NLMSG_NEXT(hdr, bytesRead)) { |
michael@0 | 142 | switch (hdr->nlmsg_type) { |
michael@0 | 143 | case NLMSG_DONE: |
michael@0 | 144 | return 0; |
michael@0 | 145 | case NLMSG_ERROR: |
michael@0 | 146 | return -1; |
michael@0 | 147 | case RTM_NEWADDR: |
michael@0 | 148 | { |
michael@0 | 149 | ifaddrmsg* address = reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(hdr)); |
michael@0 | 150 | rtattr* rta = IFA_RTA(address); |
michael@0 | 151 | size_t ifaPayloadLength = IFA_PAYLOAD(hdr); |
michael@0 | 152 | while (RTA_OK(rta, ifaPayloadLength)) { |
michael@0 | 153 | if (rta->rta_type == IFA_LOCAL) { |
michael@0 | 154 | int family = address->ifa_family; |
michael@0 | 155 | if (family == AF_INET || family == AF_INET6) { |
michael@0 | 156 | ifaddrs *next = *result; |
michael@0 | 157 | *result = new ifaddrs; |
michael@0 | 158 | memset(*result, 0, sizeof(ifaddrs)); |
michael@0 | 159 | (*result)->ifa_next = next; |
michael@0 | 160 | if (!ifa_setNameAndFlagsByIndex(*result, address->ifa_index)) { |
michael@0 | 161 | return -1; |
michael@0 | 162 | } |
michael@0 | 163 | ifa_setAddress(*result, family, RTA_DATA(rta), RTA_PAYLOAD(rta)); |
michael@0 | 164 | ifa_setNetmask(*result, family, address->ifa_prefixlen); |
michael@0 | 165 | } |
michael@0 | 166 | } |
michael@0 | 167 | rta = RTA_NEXT(rta, ifaPayloadLength); |
michael@0 | 168 | } |
michael@0 | 169 | } |
michael@0 | 170 | break; |
michael@0 | 171 | } |
michael@0 | 172 | } |
michael@0 | 173 | } |
michael@0 | 174 | // We only get here if recv fails before we see a NLMSG_DONE. |
michael@0 | 175 | return -1; |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | // Source-compatible with the BSD function. |
michael@0 | 179 | void freeifaddrs(ifaddrs* addresses) { |
michael@0 | 180 | ifaddrs* self = addresses; |
michael@0 | 181 | while (self != NULL) { |
michael@0 | 182 | delete[] self->ifa_name; |
michael@0 | 183 | delete self->ifa_addr; |
michael@0 | 184 | delete self->ifa_netmask; |
michael@0 | 185 | ifaddrs* next = self->ifa_next; |
michael@0 | 186 | delete self; |
michael@0 | 187 | self = next; |
michael@0 | 188 | } |
michael@0 | 189 | } |