New network selection APIs.

Continued from: https://android-review.git.corp.google.com/#/c/94977/

Change-Id: Ie0576888f50a8ce91bbb0a4794708b406eb0aa35
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index 2e8390c..31c2e4d 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+#include "NetdClient.h"
+
 #include "FwmarkClient.h"
 #include "FwmarkCommand.h"
+#include "resolv_netid.h"
 
 #include <sys/socket.h>
 #include <unistd.h>
@@ -31,14 +34,17 @@
 
 typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t);
 typedef int (*AcceptFunctionType)(int, sockaddr*, socklen_t*);
+typedef unsigned (*NetIdForResolvFunctionType)(unsigned);
 
+// 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.
 ConnectFunctionType libcConnect = 0;
 AcceptFunctionType libcAccept = 0;
 
 int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
     if (FwmarkClient::shouldSetFwmark(sockfd, addr)) {
-        char data[] = {FWMARK_COMMAND_ON_CONNECT};
-        if (!FwmarkClient().send(data, sizeof(data), sockfd)) {
+        FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0};
+        if (!FwmarkClient().send(&command, sizeof(command), sockfd)) {
             return -1;
         }
     }
@@ -59,14 +65,49 @@
         addr = &socketAddress;
     }
     if (FwmarkClient::shouldSetFwmark(acceptedSocket, addr)) {
-        char data[] = {FWMARK_COMMAND_ON_ACCEPT};
-        if (!FwmarkClient().send(data, sizeof(data), acceptedSocket)) {
+        FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0};
+        if (!FwmarkClient().send(&command, sizeof(command), acceptedSocket)) {
             return closeFdAndRestoreErrno(acceptedSocket);
         }
     }
     return acceptedSocket;
 }
 
+// TODO: Convert to C++11 std::atomic<unsigned>.
+volatile sig_atomic_t netIdForProcess = NETID_UNSET;
+volatile sig_atomic_t netIdForResolv = NETID_UNSET;
+
+unsigned getNetworkForResolv(unsigned netId) {
+    if (netId != NETID_UNSET) {
+        return netId;
+    }
+    netId = netIdForProcess;
+    if (netId != NETID_UNSET) {
+        return netId;
+    }
+    return netIdForResolv;
+}
+
+bool setNetworkForTarget(unsigned netId, volatile sig_atomic_t* target) {
+    if (netId == NETID_UNSET) {
+        *target = netId;
+        return true;
+    }
+    // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
+    // with the netId. Don't create an AF_INET socket, because then the creation itself might cause
+    // another check with the fwmark server (see netdClientSocket()), which would be wasteful.
+    int socketFd = socket(AF_UNIX, SOCK_DGRAM, 0);
+    if (socketFd < 0) {
+        return false;
+    }
+    bool status = setNetworkForSocket(netId, socketFd);
+    closeFdAndRestoreErrno(socketFd);
+    if (status) {
+        *target = netId;
+    }
+    return status;
+}
+
 }  // namespace
 
 extern "C" void netdClientInitConnect(ConnectFunctionType* function) {
@@ -82,3 +123,30 @@
         *function = netdClientAccept;
     }
 }
+
+extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) {
+    if (function) {
+        *function = getNetworkForResolv;
+    }
+}
+
+extern "C" unsigned getNetworkForProcess() {
+    return netIdForProcess;
+}
+
+extern "C" bool setNetworkForSocket(unsigned netId, int socketFd) {
+    if (socketFd < 0) {
+        errno = EBADF;
+        return false;
+    }
+    FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId};
+    return FwmarkClient().send(&command, sizeof(command), socketFd);
+}
+
+extern "C" bool setNetworkForProcess(unsigned netId) {
+    return setNetworkForTarget(netId, &netIdForProcess);
+}
+
+extern "C" bool setNetworkForResolv(unsigned netId) {
+    return setNetworkForTarget(netId, &netIdForResolv);
+}