Merge "Introduce netd_client, a dynamic library that talks to netd."
diff --git a/include/netd_client/FwmarkCommands.h b/include/netd_client/FwmarkCommands.h
new file mode 100644
index 0000000..0d22f02
--- /dev/null
+++ b/include/netd_client/FwmarkCommands.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef NETD_CLIENT_FWMARK_COMMANDS_H
+#define NETD_CLIENT_FWMARK_COMMANDS_H
+
+#include <stdint.h>
+
+// Commands sent from clients to the fwmark server to mark sockets (i.e., set their SO_MARK).
+const uint8_t FWMARK_COMMAND_ON_CREATE        = 0;
+const uint8_t FWMARK_COMMAND_ON_CONNECT       = 1;
+const uint8_t FWMARK_COMMAND_ON_ACCEPT        = 2;
+const uint8_t FWMARK_COMMAND_SELECT_NETWORK   = 3;
+const uint8_t FWMARK_COMMAND_PROTECT_FROM_VPN = 4;
+
+#endif  // NETD_CLIENT_FWMARK_COMMANDS_H
diff --git a/libnetd_client/Android.mk b/libnetd_client/Android.mk
new file mode 100644
index 0000000..2b75626
--- /dev/null
+++ b/libnetd_client/Android.mk
@@ -0,0 +1,22 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libnetd_client
+LOCAL_SRC_FILES := FwmarkClient.cpp NetdClient.cpp
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libnetd_client/FwmarkClient.cpp b/libnetd_client/FwmarkClient.cpp
new file mode 100644
index 0000000..e360b4e
--- /dev/null
+++ b/libnetd_client/FwmarkClient.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "FwmarkClient.h"
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+namespace {
+
+const sockaddr_un FWMARK_SERVER_PATH = {AF_UNIX, "/dev/socket/fwmarkd"};
+
+}  // namespace
+
+bool FwmarkClient::shouldSetFwmark(int sockfd, const sockaddr* addr) {
+    return sockfd >= 0 && addr && (addr->sa_family == AF_INET || addr->sa_family == AF_INET6) &&
+           !getenv("ANDROID_NO_USE_FWMARK_CLIENT");
+}
+
+FwmarkClient::FwmarkClient() : mChannel(-1) {
+}
+
+FwmarkClient::~FwmarkClient() {
+    if (mChannel >= 0) {
+        // We don't care about errors while closing the channel, so restore any previous error.
+        int error = errno;
+        close(mChannel);
+        errno = error;
+    }
+}
+
+bool FwmarkClient::send(void* data, size_t len, int fd) {
+    mChannel = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (mChannel == -1) {
+        return false;
+    }
+
+    if (TEMP_FAILURE_RETRY(connect(mChannel, reinterpret_cast<const sockaddr*>(&FWMARK_SERVER_PATH),
+                                   sizeof(FWMARK_SERVER_PATH))) == -1) {
+        // If we are unable to connect to the fwmark server, assume there's no error. This protects
+        // against future changes if the fwmark server goes away.
+        errno = 0;
+        return true;
+    }
+
+    iovec iov;
+    iov.iov_base = data;
+    iov.iov_len = len;
+
+    msghdr message;
+    memset(&message, 0, sizeof(message));
+    message.msg_iov = &iov;
+    message.msg_iovlen = 1;
+
+    union {
+        cmsghdr cmh;
+        char cmsg[CMSG_SPACE(sizeof(fd))];
+    } cmsgu;
+
+    memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
+    message.msg_control = cmsgu.cmsg;
+    message.msg_controllen = sizeof(cmsgu.cmsg);
+
+    cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
+    cmsgh->cmsg_len = CMSG_LEN(sizeof(fd));
+    cmsgh->cmsg_level = SOL_SOCKET;
+    cmsgh->cmsg_type = SCM_RIGHTS;
+    memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd));
+
+    if (TEMP_FAILURE_RETRY(sendmsg(mChannel, &message, 0)) == -1) {
+        return false;
+    }
+
+    int error = 0;
+    if (TEMP_FAILURE_RETRY(recv(mChannel, &error, sizeof(error), 0)) == -1) {
+        return false;
+    }
+
+    errno = error;
+    return !error;
+}
diff --git a/libnetd_client/FwmarkClient.h b/libnetd_client/FwmarkClient.h
new file mode 100644
index 0000000..4cf0cc0
--- /dev/null
+++ b/libnetd_client/FwmarkClient.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef NETD_CLIENT_FWMARK_CLIENT_H
+#define NETD_CLIENT_FWMARK_CLIENT_H
+
+#include <sys/socket.h>
+
+class FwmarkClient {
+public:
+    // Returns true if |sockfd| should be sent to the fwmark server to have its SO_MARK set.
+    static bool shouldSetFwmark(int sockfd, const sockaddr* addr);
+
+    FwmarkClient();
+    ~FwmarkClient();
+
+    // Sends |data| to the fwmark server, along with |fd| as ancillary data using cmsg(3).
+    // Returns true on success.
+    bool send(void* data, size_t len, int fd);
+
+private:
+    int mChannel;
+};
+
+#endif  // NETD_CLIENT_INCLUDE_FWMARK_CLIENT_H
diff --git a/libnetd_client/NetdClient.cpp b/libnetd_client/NetdClient.cpp
new file mode 100644
index 0000000..1d8501a
--- /dev/null
+++ b/libnetd_client/NetdClient.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "FwmarkClient.h"
+#include "netd_client/FwmarkCommands.h"
+
+#include <sys/socket.h>
+
+namespace {
+
+typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t);
+
+ConnectFunctionType libcConnect = 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)) {
+            return -1;
+        }
+    }
+    return libcConnect(sockfd, addr, addrlen);
+}
+
+}  // namespace
+
+extern "C" void netdClientInitConnect(ConnectFunctionType* function) {
+    if (function && *function) {
+        libcConnect = *function;
+        *function = netdClientConnect;
+    }
+}