blob: c0acdc0f87b290ddd73f1e0eac13312701bc1214 [file] [log] [blame]
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -070017#include "NetdClient.h"
18
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070019#include "FwmarkClient.h"
20#include "FwmarkCommand.h"
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -070021#include "resolv_netid.h"
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070022
23#include <sys/socket.h>
24#include <unistd.h>
25
26namespace {
27
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070028// TODO: Convert to C++11 std::atomic<unsigned>.
29volatile sig_atomic_t netIdForProcess = NETID_UNSET;
30volatile sig_atomic_t netIdForResolv = NETID_UNSET;
31
32typedef int (*Accept4FunctionType)(int, sockaddr*, socklen_t*, int);
33typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t);
34typedef int (*SocketFunctionType)(int, int, int);
35typedef unsigned (*NetIdForResolvFunctionType)(unsigned);
36
37// These variables are only modified at startup (when libc.so is loaded) and never afterwards, so
38// it's okay that they are read later at runtime without a lock.
39Accept4FunctionType libcAccept4 = 0;
40ConnectFunctionType libcConnect = 0;
41SocketFunctionType libcSocket = 0;
42
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070043int closeFdAndRestoreErrno(int fd) {
44 int error = errno;
45 close(fd);
46 errno = error;
47 return -1;
48}
49
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070050int netdClientAccept4(int sockfd, sockaddr* addr, socklen_t* addrlen, int flags) {
51 int acceptedSocket = libcAccept4(sockfd, addr, addrlen, flags);
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070052 if (acceptedSocket == -1) {
53 return -1;
54 }
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070055 int family;
56 if (addr) {
57 family = addr->sa_family;
58 } else {
59 socklen_t familyLen = sizeof(family);
60 if (getsockopt(acceptedSocket, SOL_SOCKET, SO_DOMAIN, &family, &familyLen) == -1) {
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070061 return closeFdAndRestoreErrno(acceptedSocket);
62 }
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070063 }
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070064 if (FwmarkClient::shouldSetFwmark(family)) {
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -070065 FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0};
66 if (!FwmarkClient().send(&command, sizeof(command), acceptedSocket)) {
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070067 return closeFdAndRestoreErrno(acceptedSocket);
68 }
69 }
70 return acceptedSocket;
71}
72
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070073int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
74 if (sockfd >= 0 && addr && FwmarkClient::shouldSetFwmark(addr->sa_family)) {
75 FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0};
76 if (!FwmarkClient().send(&command, sizeof(command), sockfd)) {
77 return -1;
78 }
79 }
80 return libcConnect(sockfd, addr, addrlen);
81}
82
83int netdClientSocket(int domain, int type, int protocol) {
84 int socketFd = libcSocket(domain, type, protocol);
85 if (socketFd == -1) {
86 return -1;
87 }
88 unsigned netId = netIdForProcess;
89 if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) {
90 if (!setNetworkForSocket(netId, socketFd)) {
91 return closeFdAndRestoreErrno(socketFd);
92 }
93 }
94 return socketFd;
95}
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -070096
97unsigned getNetworkForResolv(unsigned netId) {
98 if (netId != NETID_UNSET) {
99 return netId;
100 }
101 netId = netIdForProcess;
102 if (netId != NETID_UNSET) {
103 return netId;
104 }
105 return netIdForResolv;
106}
107
108bool setNetworkForTarget(unsigned netId, volatile sig_atomic_t* target) {
109 if (netId == NETID_UNSET) {
110 *target = netId;
111 return true;
112 }
113 // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
114 // with the netId. Don't create an AF_INET socket, because then the creation itself might cause
115 // another check with the fwmark server (see netdClientSocket()), which would be wasteful.
116 int socketFd = socket(AF_UNIX, SOCK_DGRAM, 0);
117 if (socketFd < 0) {
118 return false;
119 }
120 bool status = setNetworkForSocket(netId, socketFd);
121 closeFdAndRestoreErrno(socketFd);
122 if (status) {
123 *target = netId;
124 }
125 return status;
126}
127
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -0700128} // namespace
129
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -0700130// accept() just calls accept4(..., 0), so there's no need to handle accept() separately.
131extern "C" void netdClientInitAccept4(Accept4FunctionType* function) {
132 if (function && *function) {
133 libcAccept4 = *function;
134 *function = netdClientAccept4;
135 }
136}
137
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -0700138extern "C" void netdClientInitConnect(ConnectFunctionType* function) {
139 if (function && *function) {
140 libcConnect = *function;
141 *function = netdClientConnect;
142 }
143}
144
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -0700145extern "C" void netdClientInitSocket(SocketFunctionType* function) {
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -0700146 if (function && *function) {
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -0700147 libcSocket = *function;
148 *function = netdClientSocket;
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -0700149 }
150}
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700151
152extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) {
153 if (function) {
154 *function = getNetworkForResolv;
155 }
156}
157
158extern "C" unsigned getNetworkForProcess() {
159 return netIdForProcess;
160}
161
162extern "C" bool setNetworkForSocket(unsigned netId, int socketFd) {
163 if (socketFd < 0) {
164 errno = EBADF;
165 return false;
166 }
167 FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId};
168 return FwmarkClient().send(&command, sizeof(command), socketFd);
169}
170
171extern "C" bool setNetworkForProcess(unsigned netId) {
172 return setNetworkForTarget(netId, &netIdForProcess);
173}
174
175extern "C" bool setNetworkForResolv(unsigned netId) {
176 return setNetworkForTarget(netId, &netIdForResolv);
177}