OSDN Git Service

Merge "dns cache per interface iteration 2"
[android-x86/system-netd.git] / DnsProxyListener.cpp
index f40866c..b3e60a0 100644 (file)
 #include <stdlib.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <string.h>
+#include <pthread.h>
+#include <resolv_iface.h>
+#include <net/if.h>
 
 #define LOG_TAG "DnsProxyListener"
 #define DBG 0
+#define VDBG 0
 
 #include <cutils/log.h>
 #include <sysutils/SocketClient.h>
 
 #include "DnsProxyListener.h"
+#include "ResponseCode.h"
 
 DnsProxyListener::DnsProxyListener() :
                  FrameworkListener("dnsproxyd") {
     registerCmd(new GetAddrInfoCmd());
+    registerCmd(new GetHostByAddrCmd());
+    registerCmd(new GetHostByNameCmd());
+}
+
+DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient *c,
+                                                         char* host,
+                                                         char* service,
+                                                         struct addrinfo* hints,
+                                                         char* iface,
+                                                         int pid)
+        : mClient(c),
+          mHost(host),
+          mService(service),
+          mHints(hints),
+          mIface(iface),
+          mPid(pid) {
 }
 
 DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() {
     free(mHost);
     free(mService);
     free(mHints);
+    free(mIface);
 }
 
 void DnsProxyListener::GetAddrInfoHandler::start() {
-    pthread_create(&mThread, NULL,
+    pthread_t thread;
+    pthread_create(&thread, NULL,
                    DnsProxyListener::GetAddrInfoHandler::threadStart, this);
+    pthread_detach(thread);
 }
 
 void* DnsProxyListener::GetAddrInfoHandler::threadStart(void* obj) {
@@ -64,15 +89,52 @@ static bool sendLenAndData(SocketClient *c, const int len, const void* data) {
         (len == 0 || c->sendData(data, len) == 0);
 }
 
+// Returns true on success
+static bool sendhostent(SocketClient *c, struct hostent *hp) {
+    bool success = true;
+    int i;
+    if (hp->h_name != NULL) {
+        success &= sendLenAndData(c, strlen(hp->h_name)+1, hp->h_name);
+    } else {
+        success &= sendLenAndData(c, 0, "") == 0;
+    }
+
+    for (i=0; hp->h_aliases[i] != NULL; i++) {
+        success &= sendLenAndData(c, strlen(hp->h_aliases[i])+1, hp->h_aliases[i]);
+    }
+    success &= sendLenAndData(c, 0, ""); // null to indicate we're done
+
+    uint32_t buf = htonl(hp->h_addrtype);
+    success &= c->sendData(&buf, sizeof(buf)) == 0;
+
+    buf = htonl(hp->h_length);
+    success &= c->sendData(&buf, sizeof(buf)) == 0;
+
+    for (i=0; hp->h_addr_list[i] != NULL; i++) {
+        success &= sendLenAndData(c, 16, hp->h_addr_list[i]);
+    }
+    success &= sendLenAndData(c, 0, ""); // null to indicate we're done
+    return success;
+}
+
 void DnsProxyListener::GetAddrInfoHandler::run() {
     if (DBG) {
-        LOGD("GetAddrInfoHandler, now for %s / %s", mHost, mService);
+        ALOGD("GetAddrInfoHandler, now for %s / %s / %s", mHost, mService, mIface);
+    }
+
+    char tmp[IF_NAMESIZE + 1];
+    if (mIface == NULL) {
+        _resolv_get_pids_associated_interface(mPid, tmp, sizeof(tmp));
     }
 
     struct addrinfo* result = NULL;
-    int rv = getaddrinfo(mHost, mService, mHints, &result);
-    bool success = mClient->sendData(&rv, sizeof(rv));
-    if (rv == 0) {
+    uint32_t rv = android_getaddrinfoforiface(mHost, mService, mHints, mIface ? mIface : tmp,
+            &result);
+    if (rv) {
+        // getaddrinfo failed
+        mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
+    } else {
+        bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
         struct addrinfo* ai = result;
         while (ai && success) {
             success = sendLenAndData(mClient, sizeof(struct addrinfo), ai)
@@ -83,13 +145,14 @@ void DnsProxyListener::GetAddrInfoHandler::run() {
             ai = ai->ai_next;
         }
         success = success && sendLenAndData(mClient, 0, "");
+        if (!success) {
+            ALOGW("Error writing DNS result to client");
+        }
     }
     if (result) {
         freeaddrinfo(result);
     }
-    if (!success) {
-        LOGW("Error writing DNS result to client");
-    }
+    mClient->decRef();
 }
 
 DnsProxyListener::GetAddrInfoCmd::GetAddrInfoCmd() :
@@ -98,9 +161,18 @@ DnsProxyListener::GetAddrInfoCmd::GetAddrInfoCmd() :
 
 int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,
                                             int argc, char **argv) {
-    if (argc != 7) {
-        LOGW("Invalid number of arguments to getaddrinfo");
-        return 0;
+    if (DBG) {
+        for (int i = 0; i < argc; i++) {
+            ALOGD("argv[%i]=%s", i, argv[i]);
+        }
+    }
+    if (argc != 9) {
+        char* msg = NULL;
+        asprintf( &msg, "Invalid number of arguments to getaddrinfo: %i", argc);
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
+        return -1;
     }
 
     char* name = argv[1];
@@ -117,11 +189,20 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,
         service = strdup(service);
     }
 
+    char* iface = argv[7];
+    if (strcmp(iface, "^") == 0) {
+        iface = NULL;
+    } else {
+        iface = strdup(iface);
+    }
+
     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]);
+    int pid = atoi(argv[8]);
+
     if (ai_flags != -1 || ai_family != -1 ||
         ai_socktype != -1 || ai_protocol != -1) {
         hints = (struct addrinfo*) calloc(1, sizeof(struct addrinfo));
@@ -132,15 +213,260 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,
     }
 
     if (DBG) {
-        LOGD("GetAddrInfoHandler for %s / %s",
+        ALOGD("GetAddrInfoHandler for %s / %s / %s / %d",
              name ? name : "[nullhost]",
-             service ? service : "[nullservice]");
+             service ? service : "[nullservice]",
+             iface ? iface : "[nulliface]",
+             pid);
     }
 
+    cli->incRef();
     DnsProxyListener::GetAddrInfoHandler* handler =
-        new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints);
+        new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, iface, pid);
+    handler->start();
+
+    return 0;
+}
+
+/*******************************************************
+ *                  GetHostByName                      *
+ *******************************************************/
+DnsProxyListener::GetHostByNameCmd::GetHostByNameCmd() :
+        NetdCommand("gethostbyname") {
+}
+
+int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    if (DBG) {
+        for (int i = 0; i < argc; i++) {
+            ALOGD("argv[%i]=%s", i, argv[i]);
+        }
+    }
+    if (argc != 5) {
+        char* msg = NULL;
+        asprintf(&msg, "Invalid number of arguments to gethostbyname: %i", argc);
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
+        return -1;
+    }
+
+    int pid = atoi(argv[1]);
+    char* iface = argv[2];
+    char* name = argv[3];
+    int af = atoi(argv[4]);
+
+    if (strcmp(iface, "^") == 0) {
+        iface = NULL;
+    } else {
+        iface = strdup(iface);
+    }
+
+    if (strcmp(name, "^") == 0) {
+        name = NULL;
+    } else {
+        name = strdup(name);
+    }
+
+    cli->incRef();
+    DnsProxyListener::GetHostByNameHandler* handler =
+            new DnsProxyListener::GetHostByNameHandler(cli, pid, iface, name, af);
     handler->start();
 
+    return 0;
+}
+
+DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(SocketClient* c,
+                                                             int pid,
+                                                             char* iface,
+                                                             char* name,
+                                                             int af)
+        : mClient(c),
+          mPid(pid),
+          mIface(iface),
+          mName(name),
+          mAf(af) {
+}
+
+DnsProxyListener::GetHostByNameHandler::~GetHostByNameHandler() {
+    free(mIface);
+    free(mName);
+}
+
+void DnsProxyListener::GetHostByNameHandler::start() {
+    pthread_t thread;
+    pthread_create(&thread, NULL,
+            DnsProxyListener::GetHostByNameHandler::threadStart, this);
+    pthread_detach(thread);
+}
+
+void* DnsProxyListener::GetHostByNameHandler::threadStart(void* obj) {
+    GetHostByNameHandler* handler = reinterpret_cast<GetHostByNameHandler*>(obj);
+    handler->run();
+    delete handler;
+    pthread_exit(NULL);
+    return NULL;
+}
+
+void DnsProxyListener::GetHostByNameHandler::run() {
+    if (DBG) {
+        ALOGD("DnsProxyListener::GetHostByNameHandler::run\n");
+    }
+
+    char iface[IF_NAMESIZE + 1];
+    if (mIface == NULL) {
+        _resolv_get_pids_associated_interface(mPid, iface, sizeof(iface));
+    }
+
+    struct hostent* hp;
+
+    hp = android_gethostbynameforiface(mName, mAf, mIface ? mIface : iface);
+
+    if (DBG) {
+        ALOGD("GetHostByNameHandler::run gethostbyname errno: %s hp->h_name = %s, name_len = %d\n",
+                hp ? "success" : strerror(errno),
+                (hp && hp->h_name) ? hp->h_name: "null",
+                (hp && hp->h_name) ? strlen(hp->h_name)+ 1 : 0);
+    }
+
+    bool success = true;
+    if (hp) {
+        success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
+        success &= sendhostent(mClient, hp);
+    } else {
+        success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, NULL, 0) == 0;
+    }
+
+    if (!success) {
+        ALOGW("GetHostByNameHandler: Error writing DNS result to client\n");
+    }
+    mClient->decRef();
+}
+
+
+/*******************************************************
+ *                  GetHostByAddr                      *
+ *******************************************************/
+DnsProxyListener::GetHostByAddrCmd::GetHostByAddrCmd() :
+        NetdCommand("gethostbyaddr") {
+}
+
+int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    if (DBG) {
+        for (int i = 0; i < argc; i++) {
+            ALOGD("argv[%i]=%s", i, argv[i]);
+        }
+    }
+    if (argc != 6) {
+        char* msg = NULL;
+        asprintf(&msg, "Invalid number of arguments to gethostbyaddr: %i", argc);
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
+        return -1;
+    }
+
+    char* addrStr = argv[1];
+    int addrLen = atoi(argv[2]);
+    int addrFamily = atoi(argv[3]);
+    int pid = atoi(argv[4]);
+    char* iface = argv[5];
+
+    if (strcmp(iface, "^") == 0) {
+        iface = NULL;
+    } else {
+        iface = strdup(iface);
+    }
+
+    void* addr = malloc(sizeof(struct in6_addr));
+    errno = 0;
+    int result = inet_pton(addrFamily, addrStr, addr);
+    if (result <= 0) {
+        char* msg = NULL;
+        asprintf(&msg, "inet_pton(\"%s\") failed %s", addrStr, strerror(errno));
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::OperationFailed, msg, false);
+        free(addr);
+        free(msg);
+        return -1;
+    }
+
+    cli->incRef();
+    DnsProxyListener::GetHostByAddrHandler* handler =
+            new DnsProxyListener::GetHostByAddrHandler(cli, addr, addrLen, addrFamily, iface ,pid);
+    handler->start();
 
     return 0;
 }
+
+DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c,
+                                                             void* address,
+                                                             int   addressLen,
+                                                             int   addressFamily,
+                                                             char* iface,
+                                                             int   pid)
+        : mClient(c),
+          mAddress(address),
+          mAddressLen(addressLen),
+          mAddressFamily(addressFamily),
+          mIface(iface),
+          mPid(pid) {
+}
+
+DnsProxyListener::GetHostByAddrHandler::~GetHostByAddrHandler() {
+    free(mAddress);
+    free(mIface);
+}
+
+void DnsProxyListener::GetHostByAddrHandler::start() {
+    pthread_t thread;
+    pthread_create(&thread, NULL,
+                   DnsProxyListener::GetHostByAddrHandler::threadStart, this);
+    pthread_detach(thread);
+}
+
+void* DnsProxyListener::GetHostByAddrHandler::threadStart(void* obj) {
+    GetHostByAddrHandler* handler = reinterpret_cast<GetHostByAddrHandler*>(obj);
+    handler->run();
+    delete handler;
+    pthread_exit(NULL);
+    return NULL;
+}
+
+void DnsProxyListener::GetHostByAddrHandler::run() {
+    if (DBG) {
+        ALOGD("DnsProxyListener::GetHostByAddrHandler::run\n");
+    }
+
+    char tmp[IF_NAMESIZE + 1];
+    if (mIface == NULL) {
+        _resolv_get_pids_associated_interface(mPid, tmp, sizeof(tmp));
+    }
+
+    struct hostent* hp;
+
+    // NOTE gethostbyaddr should take a void* but bionic thinks it should be char*
+    hp = android_gethostbyaddrforiface((char*)mAddress, mAddressLen, mAddressFamily,
+            mIface ? mIface : tmp);
+
+    if (DBG) {
+        ALOGD("GetHostByAddrHandler::run gethostbyaddr errno: %s hp->h_name = %s, name_len = %d\n",
+                hp ? "success" : strerror(errno),
+                (hp && hp->h_name) ? hp->h_name: "null",
+                (hp && hp->h_name) ? strlen(hp->h_name)+ 1 : 0);
+    }
+
+    bool success = true;
+    if (hp) {
+        success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
+        success &= sendhostent(mClient, hp);
+    } else {
+        success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, NULL, 0) == 0;
+    }
+
+    if (!success) {
+        ALOGW("GetHostByAddrHandler: Error writing DNS result to client\n");
+    }
+    mClient->decRef();
+}