Merge "dns cache per interface iteration 2"
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 8d113e8..702ae63 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -885,6 +885,22 @@
                     "Wrong number of arguments to resolver setdefaultif", false);
             return 0;
         }
+    } else if (!strcmp(argv[1], "setifaceforpid")) { // resolver setifaceforpid <iface> <pid>
+        if (argc == 4) {
+            rc = sResolverCtrl->setDnsInterfaceForPid(argv[2], atoi(argv[3]));
+        } else {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                    "Wrong number of arguments to resolver setifaceforpid", false);
+            return 0;
+        }
+    } else if (!strcmp(argv[1], "clearifaceforpid")) { // resolver clearifaceforpid <pid>
+        if (argc == 3) {
+            rc = sResolverCtrl->clearDnsInterfaceForPid(atoi(argv[2]));
+        } else {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                    "Wrong number of arguments to resolver clearifaceforpid", false);
+            return 0;
+        }
     } else {
         cli->sendMsg(ResponseCode::CommandSyntaxError,"Resolver unknown command", false);
         return 0;
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
index 7da6b43..b3e60a0 100644
--- a/DnsProxyListener.cpp
+++ b/DnsProxyListener.cpp
@@ -25,9 +25,12 @@
 #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>
@@ -39,18 +42,35 @@
                  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_t thread;
     pthread_create(&thread, NULL,
                    DnsProxyListener::GetAddrInfoHandler::threadStart, this);
+    pthread_detach(thread);
 }
 
 void* DnsProxyListener::GetAddrInfoHandler::threadStart(void* obj) {
@@ -69,13 +89,47 @@
         (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) {
-        ALOGD("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;
-    uint32_t rv = getaddrinfo(mHost, mService, mHints, &result);
+    uint32_t rv = android_getaddrinfoforiface(mHost, mService, mHints, mIface ? mIface : tmp,
+            &result);
     if (rv) {
         // getaddrinfo failed
         mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
@@ -112,7 +166,7 @@
             ALOGD("argv[%i]=%s", i, argv[i]);
         }
     }
-    if (argc != 7) {
+    if (argc != 9) {
         char* msg = NULL;
         asprintf( &msg, "Invalid number of arguments to getaddrinfo: %i", argc);
         ALOGW("%s", msg);
@@ -135,11 +189,20 @@
         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));
@@ -150,21 +213,139 @@
     }
 
     if (DBG) {
-        ALOGD("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;
 }
 
 /*******************************************************
- *                  GetHostByAddr                       *
+ *                  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") {
@@ -177,8 +358,7 @@
             ALOGD("argv[%i]=%s", i, argv[i]);
         }
     }
-
-    if (argc != 4) {
+    if (argc != 6) {
         char* msg = NULL;
         asprintf(&msg, "Invalid number of arguments to gethostbyaddr: %i", argc);
         ALOGW("%s", msg);
@@ -190,6 +370,14 @@
     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;
@@ -206,20 +394,36 @@
 
     cli->incRef();
     DnsProxyListener::GetHostByAddrHandler* handler =
-            new DnsProxyListener::GetHostByAddrHandler(cli, addr, addrLen, addrFamily);
+            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) {
@@ -235,10 +439,16 @@
         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 = gethostbyaddr((char*)mAddress, mAddressLen, mAddressFamily);
+    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",
@@ -247,18 +457,15 @@
                 (hp && hp->h_name) ? strlen(hp->h_name)+ 1 : 0);
     }
 
-    bool failed = true;
+    bool success = true;
     if (hp) {
-        failed = mClient->sendBinaryMsg(ResponseCode::DnsProxyQueryResult,
-                                        hp->h_name ? hp->h_name : "",
-                                        hp->h_name ? strlen(hp->h_name)+ 1 : 0);
+        success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
+        success &= sendhostent(mClient, hp);
     } else {
-        uint32_t error = h_errno;
-        failed = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed,
-                                        &error, sizeof(error));
+        success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, NULL, 0) == 0;
     }
 
-    if (failed) {
+    if (!success) {
         ALOGW("GetHostByAddrHandler: Error writing DNS result to client\n");
     }
     mClient->decRef();
diff --git a/DnsProxyListener.h b/DnsProxyListener.h
index 24c3b6a..65bad35 100644
--- a/DnsProxyListener.h
+++ b/DnsProxyListener.h
@@ -40,11 +40,9 @@
         GetAddrInfoHandler(SocketClient *c,
                            char* host,
                            char* service,
-                           struct addrinfo* hints)
-            : mClient(c),
-              mHost(host),
-              mService(service),
-              mHints(hints) {}
+                           struct addrinfo* hints,
+                           char* iface,
+                           int pid);
         ~GetAddrInfoHandler();
 
         static void* threadStart(void* handler);
@@ -56,6 +54,35 @@
         char* mHost;    // owned
         char* mService; // owned
         struct addrinfo* mHints;  // owned
+        char* mIface; // owned
+        int mPid;
+    };
+
+    /* ------ gethostbyname ------*/
+    class GetHostByNameCmd : public NetdCommand {
+    public:
+        GetHostByNameCmd();
+        virtual ~GetHostByNameCmd() {}
+        int runCommand(SocketClient *c, int argc, char** argv);
+    };
+
+    class GetHostByNameHandler {
+    public:
+        GetHostByNameHandler(SocketClient *c,
+                            int pid,
+                            char *iface,
+                            char *name,
+                            int af);
+        ~GetHostByNameHandler();
+        static void* threadStart(void* handler);
+        void start();
+    private:
+        void run();
+        SocketClient* mClient; //ref counted
+        int mPid;
+        char* mIface; // owned
+        char* mName; // owned
+        int mAf;
     };
 
     /* ------ gethostbyaddr ------*/
@@ -71,11 +98,9 @@
         GetHostByAddrHandler(SocketClient *c,
                             void* address,
                             int   addressLen,
-                            int   addressFamily)
-            : mClient(c),
-              mAddress(address),
-              mAddressLen(addressLen),
-              mAddressFamily(addressFamily) {}
+                            int   addressFamily,
+                            char* iface,
+                            int   pid);
         ~GetHostByAddrHandler();
 
         static void* threadStart(void* handler);
@@ -87,6 +112,8 @@
         void* mAddress;    // address to lookup; owned
         int   mAddressLen; // length of address to look up
         int   mAddressFamily;  // address family
+        char* mIface; // owned
+        int   mPid;
     };
 };
 
diff --git a/MDnsSdListener.cpp b/MDnsSdListener.cpp
index 52703df..9a3f4f7 100644
--- a/MDnsSdListener.cpp
+++ b/MDnsSdListener.cpp
@@ -497,6 +497,7 @@
     pthread_mutex_init(&mHeadMutex, NULL);
     socketpair(AF_LOCAL, SOCK_STREAM, 0, mCtrlSocketPair);
     pthread_create(&mThread, NULL, MDnsSdListener::Monitor::threadStart, this);
+    pthread_detach(mThread);
 }
 
 void *MDnsSdListener::Monitor::threadStart(void *obj) {
diff --git a/ResolverController.cpp b/ResolverController.cpp
index 08a54a7..13dd930 100644
--- a/ResolverController.cpp
+++ b/ResolverController.cpp
@@ -76,3 +76,23 @@
 
     return 0;
 }
+
+int ResolverController::setDnsInterfaceForPid(const char* iface, int pid) {
+    if (DBG) {
+        ALOGD("setDnsIfaceForPid iface = %s, pid = %d\n", iface, pid);
+    }
+
+    _resolv_set_iface_for_pid(iface, pid);
+
+    return 0;
+}
+
+int ResolverController::clearDnsInterfaceForPid(int pid) {
+    if (DBG) {
+        ALOGD("clearDnsIfaceForPid pid = %d\n", pid);
+    }
+
+    _resolv_clear_iface_for_pid(pid);
+
+    return 0;
+}
diff --git a/ResolverController.h b/ResolverController.h
index 9cb88ab..a21e077 100644
--- a/ResolverController.h
+++ b/ResolverController.h
@@ -31,6 +31,8 @@
     int setInterfaceAddress(const char* iface, struct in_addr* addr);
     int flushDefaultDnsCache();
     int flushInterfaceDnsCache(const char* iface);
+    int setDnsInterfaceForPid(const char* iface, int pid);
+    int clearDnsInterfaceForPid(int pid);
 };
 
 #endif /* _RESOLVER_CONTROLLER_H_ */