From: Brad Fitzpatrick Date: Wed, 27 Oct 2010 18:39:52 +0000 (-0700) Subject: DNS proxy thread in netd. X-Git-Tag: android-x86-7.1-r1~495^2~5^2~7^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=4e248e5a7a542067fd3f0467680eef2f92b1e195;p=android-x86%2Fsystem-netd.git DNS proxy thread in netd. 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 --- diff --git a/Android.mk b/Android.mk index 600f36e..a75c5ff 100644 --- a/Android.mk +++ b/Android.mk @@ -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 index 0000000..bda5665 --- /dev/null +++ b/DnsProxyListener.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "DnsProxyListener" +#define DBG 0 + +#include +#include + +#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(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 index 0000000..2d796d9 --- /dev/null +++ b/DnsProxyListener.h @@ -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 +#include + +#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 diff --git a/main.cpp b/main.cpp index 73cbd9c..36712a4 100644 --- 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 */