| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "NetdClient.h" |
| |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <math.h> |
| #include <resolv.h> |
| #include <stdlib.h> |
| #include <sys/socket.h> |
| #include <sys/system_properties.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| |
| #include <atomic> |
| #include <string> |
| #include <vector> |
| |
| #include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS |
| #include <android-base/parseint.h> |
| #include <android-base/unique_fd.h> |
| |
| #include "Fwmark.h" |
| #include "FwmarkClient.h" |
| #include "FwmarkCommand.h" |
| #include "netdclient_priv.h" |
| #include "netdutils/ResponseCode.h" |
| #include "netdutils/Stopwatch.h" |
| #include "netid_client.h" |
| |
| using android::base::ParseInt; |
| using android::base::unique_fd; |
| using android::netdutils::ResponseCode; |
| using android::netdutils::Stopwatch; |
| |
| namespace { |
| |
| // Keep this in sync with CMD_BUF_SIZE in FrameworkListener.cpp. |
| constexpr size_t MAX_CMD_SIZE = 4096; |
| // Whether sendto(), sendmsg(), sendmmsg() in libc are shimmed or not. This property is evaluated at |
| // process start time and cannot change at runtime on a given device. |
| constexpr char PROPERTY_REDIRECT_SOCKET_CALLS[] = "ro.vendor.redirect_socket_calls"; |
| // Whether some shimmed functions dispatch FwmarkCommand or not. The property can be changed by |
| // System Server at runtime. Note: accept4(), socket(), connect() are always shimmed. |
| constexpr char PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED[] = "net.redirect_socket_calls.hooked"; |
| |
| std::atomic_uint netIdForProcess(NETID_UNSET); |
| std::atomic_uint netIdForResolv(NETID_UNSET); |
| std::atomic_bool allowNetworkingForProcess(true); |
| |
| typedef int (*Accept4FunctionType)(int, sockaddr*, socklen_t*, int); |
| typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t); |
| typedef int (*SocketFunctionType)(int, int, int); |
| typedef unsigned (*NetIdForResolvFunctionType)(unsigned); |
| typedef int (*DnsOpenProxyType)(); |
| typedef int (*SendmmsgFunctionType)(int, const mmsghdr*, unsigned int, int); |
| typedef ssize_t (*SendmsgFunctionType)(int, const msghdr*, unsigned int); |
| typedef int (*SendtoFunctionType)(int, const void*, size_t, int, const sockaddr*, socklen_t); |
| |
| // These variables are only modified at startup (when libc.so is loaded) and never afterwards, so |
| // it's okay that they are read later at runtime without a lock. |
| Accept4FunctionType libcAccept4 = nullptr; |
| ConnectFunctionType libcConnect = nullptr; |
| SocketFunctionType libcSocket = nullptr; |
| SendmmsgFunctionType libcSendmmsg = nullptr; |
| SendmsgFunctionType libcSendmsg = nullptr; |
| SendtoFunctionType libcSendto = nullptr; |
| |
| static bool propertyValueIsTrue(const char* prop_name) { |
| char prop_value[PROP_VALUE_MAX] = {0}; |
| if (__system_property_get(prop_name, prop_value) > 0) { |
| if (strcmp(prop_value, "true") == 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static bool redirectSocketCallsIsTrue() { |
| static bool cached_result = propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS); |
| return cached_result; |
| } |
| |
| int checkSocket(int socketFd) { |
| if (socketFd < 0) { |
| return -EBADF; |
| } |
| int family; |
| socklen_t familyLen = sizeof(family); |
| if (getsockopt(socketFd, SOL_SOCKET, SO_DOMAIN, &family, &familyLen) == -1) { |
| return -errno; |
| } |
| if (!FwmarkClient::shouldSetFwmark(family)) { |
| return -EAFNOSUPPORT; |
| } |
| return 0; |
| } |
| |
| bool shouldMarkSocket(int socketFd, const sockaddr* dst) { |
| // Only mark inet sockets that are connecting to inet destinations. This excludes, for example, |
| // inet sockets connecting to AF_UNSPEC (i.e., being disconnected), and non-inet sockets that |
| // for some reason the caller wants to attempt to connect to an inet destination. |
| return dst && FwmarkClient::shouldSetFwmark(dst->sa_family) && (checkSocket(socketFd) == 0); |
| } |
| |
| int closeFdAndSetErrno(int fd, int error) { |
| close(fd); |
| errno = -error; |
| return -1; |
| } |
| |
| int netdClientAccept4(int sockfd, sockaddr* addr, socklen_t* addrlen, int flags) { |
| int acceptedSocket = libcAccept4(sockfd, addr, addrlen, flags); |
| if (acceptedSocket == -1) { |
| return -1; |
| } |
| int family; |
| if (addr) { |
| family = addr->sa_family; |
| } else { |
| socklen_t familyLen = sizeof(family); |
| if (getsockopt(acceptedSocket, SOL_SOCKET, SO_DOMAIN, &family, &familyLen) == -1) { |
| return closeFdAndSetErrno(acceptedSocket, -errno); |
| } |
| } |
| if (FwmarkClient::shouldSetFwmark(family)) { |
| FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0, 0, 0}; |
| if (int error = FwmarkClient().send(&command, acceptedSocket, nullptr)) { |
| return closeFdAndSetErrno(acceptedSocket, error); |
| } |
| } |
| return acceptedSocket; |
| } |
| |
| int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) { |
| const bool shouldSetFwmark = shouldMarkSocket(sockfd, addr); |
| if (shouldSetFwmark) { |
| FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0, 0}; |
| int error; |
| if (redirectSocketCallsIsTrue()) { |
| FwmarkConnectInfo connectInfo(0, 0, addr); |
| error = FwmarkClient().send(&command, sockfd, &connectInfo); |
| } else { |
| error = FwmarkClient().send(&command, sockfd, nullptr); |
| } |
| |
| if (error) { |
| errno = -error; |
| return -1; |
| } |
| } |
| // Latency measurement does not include time of sending commands to Fwmark |
| Stopwatch s; |
| const int ret = libcConnect(sockfd, addr, addrlen); |
| // Save errno so it isn't clobbered by sending ON_CONNECT_COMPLETE |
| const int connectErrno = errno; |
| const auto latencyMs = static_cast<unsigned>(s.timeTakenUs() / 1000); |
| // Send an ON_CONNECT_COMPLETE command that includes sockaddr and connect latency for reporting |
| if (shouldSetFwmark) { |
| FwmarkConnectInfo connectInfo(ret == 0 ? 0 : connectErrno, latencyMs, addr); |
| // TODO: get the netId from the socket mark once we have continuous benchmark runs |
| FwmarkCommand command = {FwmarkCommand::ON_CONNECT_COMPLETE, /* netId (ignored) */ 0, |
| /* uid (filled in by the server) */ 0, 0}; |
| // Ignore return value since it's only used for logging |
| FwmarkClient().send(&command, sockfd, &connectInfo); |
| } |
| errno = connectErrno; |
| return ret; |
| } |
| |
| int netdClientSocket(int domain, int type, int protocol) { |
| // Block creating AF_INET/AF_INET6 socket if networking is not allowed. |
| if (FwmarkCommand::isSupportedFamily(domain) && !allowNetworkingForProcess.load()) { |
| errno = EPERM; |
| return -1; |
| } |
| int socketFd = libcSocket(domain, type, protocol); |
| if (socketFd == -1) { |
| return -1; |
| } |
| unsigned netId = netIdForProcess & ~NETID_USE_LOCAL_NAMESERVERS; |
| if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) { |
| if (int error = setNetworkForSocket(netId, socketFd)) { |
| return closeFdAndSetErrno(socketFd, error); |
| } |
| } |
| return socketFd; |
| } |
| |
| int netdClientSendmmsg(int sockfd, const mmsghdr* msgs, unsigned int msgcount, int flags) { |
| if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) { |
| const sockaddr* addr = nullptr; |
| if ((msgcount > 0) && (msgs != nullptr) && (msgs[0].msg_hdr.msg_name != nullptr)) { |
| addr = reinterpret_cast<const sockaddr*>(msgs[0].msg_hdr.msg_name); |
| if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) { |
| FwmarkConnectInfo sendmmsgInfo(0, 0, addr); |
| FwmarkCommand command = {FwmarkCommand::ON_SENDMMSG, 0, 0, 0}; |
| FwmarkClient().send(&command, sockfd, &sendmmsgInfo); |
| } |
| } |
| } |
| return libcSendmmsg(sockfd, msgs, msgcount, flags); |
| } |
| |
| ssize_t netdClientSendmsg(int sockfd, const msghdr* msg, unsigned int flags) { |
| if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) { |
| const sockaddr* addr = nullptr; |
| if ((msg != nullptr) && (msg->msg_name != nullptr)) { |
| addr = reinterpret_cast<const sockaddr*>(msg->msg_name); |
| if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) { |
| FwmarkConnectInfo sendmsgInfo(0, 0, addr); |
| FwmarkCommand command = {FwmarkCommand::ON_SENDMSG, 0, 0, 0}; |
| FwmarkClient().send(&command, sockfd, &sendmsgInfo); |
| } |
| } |
| } |
| return libcSendmsg(sockfd, msg, flags); |
| } |
| |
| int netdClientSendto(int sockfd, const void* buf, size_t bufsize, int flags, const sockaddr* addr, |
| socklen_t addrlen) { |
| if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) { |
| if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) { |
| FwmarkConnectInfo sendtoInfo(0, 0, addr); |
| FwmarkCommand command = {FwmarkCommand::ON_SENDTO, 0, 0, 0}; |
| FwmarkClient().send(&command, sockfd, &sendtoInfo); |
| } |
| } |
| return libcSendto(sockfd, buf, bufsize, flags, addr, addrlen); |
| } |
| |
| unsigned getNetworkForResolv(unsigned netId) { |
| if (netId != NETID_UNSET) { |
| return netId; |
| } |
| // Special case for DNS-over-TLS bypass; b/72345192 . |
| if ((netIdForResolv & ~NETID_USE_LOCAL_NAMESERVERS) != NETID_UNSET) { |
| return netIdForResolv; |
| } |
| netId = netIdForProcess; |
| if (netId != NETID_UNSET) { |
| return netId; |
| } |
| return netIdForResolv; |
| } |
| |
| int setNetworkForTarget(unsigned netId, std::atomic_uint* target) { |
| const unsigned requestedNetId = netId; |
| netId &= ~NETID_USE_LOCAL_NAMESERVERS; |
| |
| if (netId == NETID_UNSET) { |
| *target = netId; |
| return 0; |
| } |
| // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked |
| // with the netId. Call libcSocket() directly; else the socket creation (via netdClientSocket()) |
| // might itself cause another check with the fwmark server, which would be wasteful. |
| |
| const auto socketFunc = libcSocket ? libcSocket : socket; |
| int socketFd = socketFunc(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); |
| if (socketFd < 0) { |
| return -errno; |
| } |
| int error = setNetworkForSocket(netId, socketFd); |
| if (!error) { |
| *target = requestedNetId; |
| } |
| close(socketFd); |
| return error; |
| } |
| |
| int dns_open_proxy() { |
| const char* cache_mode = getenv("ANDROID_DNS_MODE"); |
| const bool use_proxy = (cache_mode == NULL || strcmp(cache_mode, "local") != 0); |
| if (!use_proxy) { |
| errno = ENOSYS; |
| return -1; |
| } |
| |
| // If networking is not allowed, dns_open_proxy should just fail here. |
| // Then eventually, the DNS related functions in local mode will get |
| // EPERM while creating socket. |
| if (!allowNetworkingForProcess.load()) { |
| errno = EPERM; |
| return -1; |
| } |
| const auto socketFunc = libcSocket ? libcSocket : socket; |
| int s = socketFunc(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); |
| if (s == -1) { |
| return -1; |
| } |
| const int one = 1; |
| setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); |
| |
| static const struct sockaddr_un proxy_addr = { |
| .sun_family = AF_UNIX, |
| .sun_path = "/dev/socket/dnsproxyd", |
| }; |
| |
| const auto connectFunc = libcConnect ? libcConnect : connect; |
| if (TEMP_FAILURE_RETRY( |
| connectFunc(s, (const struct sockaddr*) &proxy_addr, sizeof(proxy_addr))) != 0) { |
| // Store the errno for connect because we only care about why we can't connect to dnsproxyd |
| int storedErrno = errno; |
| close(s); |
| errno = storedErrno; |
| return -1; |
| } |
| |
| return s; |
| } |
| |
| auto divCeil(size_t dividend, size_t divisor) { |
| return ((dividend + divisor - 1) / divisor); |
| } |
| |
| // FrameworkListener only does only read() call, and fails if the read doesn't contain \0 |
| // Do single write here |
| int sendData(int fd, const void* buf, size_t size) { |
| if (fd < 0) { |
| return -EBADF; |
| } |
| |
| ssize_t rc = TEMP_FAILURE_RETRY(write(fd, (char*) buf, size)); |
| if (rc > 0) { |
| return rc; |
| } else if (rc == 0) { |
| return -EIO; |
| } else { |
| return -errno; |
| } |
| } |
| |
| int readData(int fd, void* buf, size_t size) { |
| if (fd < 0) { |
| return -EBADF; |
| } |
| |
| size_t current = 0; |
| for (;;) { |
| ssize_t rc = TEMP_FAILURE_RETRY(read(fd, (char*) buf + current, size - current)); |
| if (rc > 0) { |
| current += rc; |
| if (current == size) { |
| break; |
| } |
| } else if (rc == 0) { |
| return -EIO; |
| } else { |
| return -errno; |
| } |
| } |
| return 0; |
| } |
| |
| bool readBE32(int fd, int32_t* result) { |
| int32_t tmp; |
| ssize_t n = TEMP_FAILURE_RETRY(read(fd, &tmp, sizeof(tmp))); |
| if (n < static_cast<ssize_t>(sizeof(tmp))) { |
| return false; |
| } |
| *result = ntohl(tmp); |
| return true; |
| } |
| |
| bool readResponseCode(int fd, int* result) { |
| char buf[4]; |
| ssize_t n = TEMP_FAILURE_RETRY(read(fd, &buf, sizeof(buf))); |
| if (n < static_cast<ssize_t>(sizeof(buf))) { |
| return false; |
| } |
| |
| // The format of response code is 3 bytes followed by a space. |
| buf[3] = '\0'; |
| if (!ParseInt(buf, result)) { |
| errno = EINVAL; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| #define CHECK_SOCKET_IS_MARKABLE(sock) \ |
| do { \ |
| int err = checkSocket(sock); \ |
| if (err) return err; \ |
| } while (false) |
| |
| #define HOOK_ON_FUNC(remoteFunc, nativeFunc, localFunc) \ |
| do { \ |
| if ((remoteFunc) && *(remoteFunc)) { \ |
| (nativeFunc) = *(remoteFunc); \ |
| *(remoteFunc) = (localFunc); \ |
| } \ |
| } while (false) |
| |
| // accept() just calls accept4(..., 0), so there's no need to handle accept() separately. |
| extern "C" void netdClientInitAccept4(Accept4FunctionType* function) { |
| HOOK_ON_FUNC(function, libcAccept4, netdClientAccept4); |
| } |
| |
| extern "C" void netdClientInitConnect(ConnectFunctionType* function) { |
| HOOK_ON_FUNC(function, libcConnect, netdClientConnect); |
| } |
| |
| extern "C" void netdClientInitSocket(SocketFunctionType* function) { |
| HOOK_ON_FUNC(function, libcSocket, netdClientSocket); |
| } |
| |
| extern "C" void netdClientInitSendmmsg(SendmmsgFunctionType* function) { |
| if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) { |
| return; |
| } |
| HOOK_ON_FUNC(function, libcSendmmsg, netdClientSendmmsg); |
| } |
| |
| extern "C" void netdClientInitSendmsg(SendmsgFunctionType* function) { |
| if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) { |
| return; |
| } |
| HOOK_ON_FUNC(function, libcSendmsg, netdClientSendmsg); |
| } |
| |
| extern "C" void netdClientInitSendto(SendtoFunctionType* function) { |
| if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) { |
| return; |
| } |
| HOOK_ON_FUNC(function, libcSendto, netdClientSendto); |
| } |
| |
| extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) { |
| if (function) { |
| *function = getNetworkForResolv; |
| } |
| } |
| |
| extern "C" void netdClientInitDnsOpenProxy(DnsOpenProxyType* function) { |
| if (function) { |
| *function = dns_open_proxy; |
| } |
| } |
| |
| extern "C" int getNetworkForSocket(unsigned* netId, int socketFd) { |
| if (!netId || socketFd < 0) { |
| return -EBADF; |
| } |
| Fwmark fwmark; |
| socklen_t fwmarkLen = sizeof(fwmark.intValue); |
| if (getsockopt(socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen) == -1) { |
| return -errno; |
| } |
| *netId = fwmark.netId; |
| return 0; |
| } |
| |
| extern "C" unsigned getNetworkForProcess() { |
| return netIdForProcess & ~NETID_USE_LOCAL_NAMESERVERS; |
| } |
| |
| extern "C" int setNetworkForSocket(unsigned netId, int socketFd) { |
| CHECK_SOCKET_IS_MARKABLE(socketFd); |
| FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0, 0}; |
| return FwmarkClient().send(&command, socketFd, nullptr); |
| } |
| |
| extern "C" int setNetworkForProcess(unsigned netId) { |
| return setNetworkForTarget(netId, &netIdForProcess); |
| } |
| |
| extern "C" int setNetworkForResolv(unsigned netId) { |
| return setNetworkForTarget(netId, &netIdForResolv); |
| } |
| |
| extern "C" int protectFromVpn(int socketFd) { |
| CHECK_SOCKET_IS_MARKABLE(socketFd); |
| FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0, 0}; |
| return FwmarkClient().send(&command, socketFd, nullptr); |
| } |
| |
| extern "C" int setNetworkForUser(uid_t uid, int socketFd) { |
| CHECK_SOCKET_IS_MARKABLE(socketFd); |
| FwmarkCommand command = {FwmarkCommand::SELECT_FOR_USER, 0, uid, 0}; |
| return FwmarkClient().send(&command, socketFd, nullptr); |
| } |
| |
| extern "C" int queryUserAccess(uid_t uid, unsigned netId) { |
| FwmarkCommand command = {FwmarkCommand::QUERY_USER_ACCESS, netId, uid, 0}; |
| return FwmarkClient().send(&command, -1, nullptr); |
| } |
| |
| extern "C" int tagSocket(int socketFd, uint32_t tag, uid_t uid) { |
| CHECK_SOCKET_IS_MARKABLE(socketFd); |
| FwmarkCommand command = {FwmarkCommand::TAG_SOCKET, 0, uid, tag}; |
| return FwmarkClient().send(&command, socketFd, nullptr); |
| } |
| |
| extern "C" int untagSocket(int socketFd) { |
| CHECK_SOCKET_IS_MARKABLE(socketFd); |
| FwmarkCommand command = {FwmarkCommand::UNTAG_SOCKET, 0, 0, 0}; |
| return FwmarkClient().send(&command, socketFd, nullptr); |
| } |
| |
| extern "C" int setCounterSet(uint32_t counterSet, uid_t uid) { |
| FwmarkCommand command = {FwmarkCommand::SET_COUNTERSET, 0, uid, counterSet}; |
| return FwmarkClient().send(&command, -1, nullptr); |
| } |
| |
| extern "C" int deleteTagData(uint32_t tag, uid_t uid) { |
| FwmarkCommand command = {FwmarkCommand::DELETE_TAGDATA, 0, uid, tag}; |
| return FwmarkClient().send(&command, -1, nullptr); |
| } |
| |
| extern "C" int resNetworkQuery(unsigned netId, const char* dname, int ns_class, int ns_type, |
| uint32_t flags) { |
| std::vector<uint8_t> buf(MAX_CMD_SIZE, 0); |
| int len = res_mkquery(ns_o_query, dname, ns_class, ns_type, nullptr, 0, nullptr, buf.data(), |
| MAX_CMD_SIZE); |
| |
| return resNetworkSend(netId, buf.data(), len, flags); |
| } |
| |
| extern "C" int resNetworkSend(unsigned netId, const uint8_t* msg, size_t msglen, uint32_t flags) { |
| // Encode |
| // Base 64 encodes every 3 bytes into 4 characters, but then adds padding to the next |
| // multiple of 4 and a \0 |
| const size_t encodedLen = divCeil(msglen, 3) * 4 + 1; |
| std::string encodedQuery(encodedLen - 1, 0); |
| int enLen = b64_ntop(msg, msglen, encodedQuery.data(), encodedLen); |
| |
| if (enLen < 0) { |
| // Unexpected behavior, encode failed |
| // b64_ntop only fails when size is too long. |
| return -EMSGSIZE; |
| } |
| // Send |
| netId = getNetworkForResolv(netId); |
| const std::string cmd = "resnsend " + std::to_string(netId) + " " + std::to_string(flags) + |
| " " + encodedQuery + '\0'; |
| if (cmd.size() > MAX_CMD_SIZE) { |
| // Cmd size must less than buffer size of FrameworkListener |
| return -EMSGSIZE; |
| } |
| int fd = dns_open_proxy(); |
| if (fd == -1) { |
| return -errno; |
| } |
| ssize_t rc = sendData(fd, cmd.c_str(), cmd.size()); |
| if (rc < 0) { |
| close(fd); |
| return rc; |
| } |
| shutdown(fd, SHUT_WR); |
| return fd; |
| } |
| |
| extern "C" int resNetworkResult(int fd, int* rcode, uint8_t* answer, size_t anslen) { |
| int32_t result = 0; |
| unique_fd ufd(fd); |
| // Read -errno/rcode |
| if (!readBE32(fd, &result)) { |
| // Unexpected behavior, read -errno/rcode fail |
| return -errno; |
| } |
| if (result < 0) { |
| // result < 0, it's -errno |
| return result; |
| } |
| // result >= 0, it's rcode |
| *rcode = result; |
| |
| // Read answer |
| int32_t size = 0; |
| if (!readBE32(fd, &size)) { |
| // Unexpected behavior, read ans len fail |
| return -EREMOTEIO; |
| } |
| if (anslen < static_cast<size_t>(size)) { |
| // Answer buffer is too small |
| return -EMSGSIZE; |
| } |
| int rc = readData(fd, answer, size); |
| if (rc < 0) { |
| // Reading the answer failed. |
| return rc; |
| } |
| return size; |
| } |
| |
| extern "C" void resNetworkCancel(int fd) { |
| close(fd); |
| } |
| |
| extern "C" void setAllowNetworkingForProcess(bool allowNetworking) { |
| allowNetworkingForProcess.store(allowNetworking); |
| } |
| |
| extern "C" int getNetworkForDns(unsigned* dnsNetId) { |
| if (dnsNetId == nullptr) return -EFAULT; |
| int fd = dns_open_proxy(); |
| if (fd == -1) { |
| return -errno; |
| } |
| unique_fd ufd(fd); |
| return getNetworkForDnsInternal(fd, dnsNetId); |
| } |
| |
| int getNetworkForDnsInternal(int fd, unsigned* dnsNetId) { |
| if (fd == -1) { |
| return -EBADF; |
| } |
| |
| unsigned resolvNetId = getNetworkForResolv(NETID_UNSET); |
| |
| const std::string cmd = "getdnsnetid " + std::to_string(resolvNetId); |
| ssize_t rc = sendData(fd, cmd.c_str(), cmd.size() + 1); |
| if (rc < 0) { |
| return rc; |
| } |
| |
| int responseCode = 0; |
| // Read responseCode |
| if (!readResponseCode(fd, &responseCode)) { |
| // Unexpected behavior, read responseCode fail |
| return -errno; |
| } |
| |
| if (responseCode != ResponseCode::DnsProxyQueryResult) { |
| return -EOPNOTSUPP; |
| } |
| |
| int32_t result = 0; |
| // Read -errno/dnsnetid |
| if (!readBE32(fd, &result)) { |
| // Unexpected behavior, read -errno/dnsnetid fail |
| return -errno; |
| } |
| |
| *dnsNetId = result; |
| |
| return 0; |
| } |