OSDN Git Service

DNS proxy thread in netd.
authorBrad Fitzpatrick <bradfitz@android.com>
Wed, 27 Oct 2010 18:39:52 +0000 (11:39 -0700)
committerBrad Fitzpatrick <bradfitz@android.com>
Mon, 1 Nov 2010 23:00:10 +0000 (16:00 -0700)
New thread in the netd process which listens on the
/dev/socket/dnsproxyd socket (group owned by 'inet', so only useable
by apps with the INTERNET permission), and does getaddrinfo requests
for other processes, caching in one place, rather than per-app.

Still remaining: proper caching based on DNS-requested lengths, upping
the cache size, getnameinfo, stats, flush, etc, etc.

Change-Id: I1d65af7d87876e508c718656bd81217cd961e20a

Android.mk
DnsProxyListener.cpp [new file with mode: 0644]
DnsProxyListener.h [new file with mode: 0644]
main.cpp

index 600f36e..a75c5ff 100644 (file)
@@ -12,6 +12,7 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES:=                                      \
                   main.cpp                             \
                   CommandListener.cpp                  \
+                  DnsProxyListener.cpp                 \
                   NetdCommand.cpp                      \
                   NetlinkManager.cpp                   \
                   NetlinkHandler.cpp                   \
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
new file mode 100644 (file)
index 0000000..bda5665
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 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 <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#define LOG_TAG "DnsProxyListener"
+#define DBG 0
+
+#include <cutils/log.h>
+#include <sysutils/SocketClient.h>
+
+#include "DnsProxyListener.h"
+
+DnsProxyListener::DnsProxyListener() :
+                 FrameworkListener("dnsproxyd") {
+    registerCmd(new GetAddrInfoCmd());
+}
+
+DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() {
+    free(mHost);
+    free(mService);
+    free(mHints);
+}
+
+void DnsProxyListener::GetAddrInfoHandler::start() {
+    pthread_create(&mThread, NULL,
+                   DnsProxyListener::GetAddrInfoHandler::threadStart, this);
+}
+
+void* DnsProxyListener::GetAddrInfoHandler::threadStart(void* obj) {
+    GetAddrInfoHandler* handler = reinterpret_cast<GetAddrInfoHandler*>(obj);
+    handler->run();
+    delete handler;
+    pthread_exit(NULL);
+    return NULL;
+}
+
+// Sends 4 bytes of big-endian length, followed by the data.
+// Returns true on success.
+static bool sendLenAndData(SocketClient *c, const int len, const void* data) {
+    uint32_t len_be = htonl(len);
+    return c->sendData(&len_be, 4) == 0 && c->sendData(data, len) == 0;
+}
+
+void DnsProxyListener::GetAddrInfoHandler::run() {
+    if (DBG) {
+        LOGD("GetAddrInfoHandler, now for %s / %s", mHost, mService);
+    }
+
+    struct addrinfo* result = NULL;
+    int rv = getaddrinfo(mHost, mService, mHints, &result);
+    bool success = mClient->sendData(&rv, sizeof(rv));
+    if (rv == 0) {
+        struct addrinfo* ai = result;
+        while (ai && success) {
+            success = sendLenAndData(mClient, sizeof(struct addrinfo), ai)
+                && sendLenAndData(mClient, ai->ai_addrlen, ai->ai_addr)
+                && sendLenAndData(mClient,
+                                  ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0,
+                                  ai->ai_canonname);
+            ai = ai->ai_next;
+        }
+        success = success && sendLenAndData(mClient, 0, "");
+    }
+    if (result) {
+        freeaddrinfo(result);
+    }
+    if (!success) {
+        LOGW("Error writing DNS result to client");
+    }
+}
+
+DnsProxyListener::GetAddrInfoCmd::GetAddrInfoCmd() :
+    NetdCommand("getaddrinfo") {
+}
+
+int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    if (argc != 7) {
+        LOGW("Invalid number of arguments to getaddrinfo");
+        return 0;
+    }
+
+    char* name = argv[1];
+    if (strcmp("^", name) == 0) {
+        name = NULL;
+    } else {
+        name = strdup(name);
+    }
+
+    char* service = argv[2];
+    if (strcmp("^", service) == 0) {
+        service = NULL;
+    } else {
+        service = strdup(service);
+    }
+
+    struct addrinfo* hints = NULL;
+    int ai_flags = atoi(argv[3]);
+    int ai_family = atoi(argv[4]);
+    int ai_socktype = atoi(argv[5]);
+    int ai_protocol = atoi(argv[6]);
+    if (ai_flags != -1 || ai_family != -1 ||
+        ai_socktype != -1 || ai_protocol != -1) {
+        hints = (struct addrinfo*) calloc(1, sizeof(struct addrinfo));
+        hints->ai_flags = ai_flags;
+        hints->ai_family = ai_family;
+        hints->ai_socktype = ai_socktype;
+        hints->ai_protocol = ai_protocol;
+    }
+
+    if (DBG) {
+        LOGD("GetAddrInfoHandler for %s / %s",
+             name ? name : "[nullhost]",
+             service ? service : "[nullservice]");
+    }
+
+    DnsProxyListener::GetAddrInfoHandler* handler =
+        new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints);
+    handler->start();
+
+
+    return 0;
+}
diff --git a/DnsProxyListener.h b/DnsProxyListener.h
new file mode 100644 (file)
index 0000000..2d796d9
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 _DNSPROXYLISTENER_H__
+#define _DNSPROXYLISTENER_H__
+
+#include <pthread.h>
+#include <sysutils/FrameworkListener.h>
+
+#include "NetdCommand.h"
+
+class DnsProxyListener : public FrameworkListener {
+public:
+    DnsProxyListener();
+    virtual ~DnsProxyListener() {}
+
+private:
+    class GetAddrInfoCmd : public NetdCommand {
+    public:
+        GetAddrInfoCmd();
+        virtual ~GetAddrInfoCmd() {}
+        int runCommand(SocketClient *c, int argc, char** argv);
+    };
+
+    class GetAddrInfoHandler {
+    public:
+        // Note: All of host, service, and hints may be NULL
+        GetAddrInfoHandler(SocketClient *c,
+                           char* host,
+                           char* service,
+                           struct addrinfo* hints)
+            : mClient(c),
+              mHost(host),
+              mService(service),
+              mHints(hints) {}
+        ~GetAddrInfoHandler();
+
+        static void* threadStart(void* handler);
+        void start();
+
+    private:
+        void run();
+        pthread_t mThread;
+        SocketClient* mClient;  // not owned
+        char* mHost;    // owned
+        char* mService; // owned
+        struct addrinfo* mHints;  // owned
+    };
+};
+
+#endif
index 73cbd9c..36712a4 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -32,6 +32,7 @@
 
 #include "CommandListener.h"
 #include "NetlinkManager.h"
+#include "DnsProxyListener.h"
 
 static void coldboot(const char *path);
 static void sigchld_handler(int sig);
@@ -40,6 +41,7 @@ int main() {
 
     CommandListener *cl;
     NetlinkManager *nm;
+    DnsProxyListener *dpl;
 
     LOGI("Netd 1.0 starting");
 
@@ -59,6 +61,15 @@ int main() {
         exit(1);
     }
 
+    // Set local DNS mode, to prevent bionic from proxying
+    // back to this service, recursively.
+    setenv("ANDROID_DNS_MODE", "local", 1);
+    dpl = new DnsProxyListener();
+    if (dpl->startListener()) {
+        LOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
+        exit(1);
+    }
+
     /*
      * Now that we're up, we can respond to commands
      */